# Creating Native Applications v4

When it is necessary to integrate product quizzes and recommendations with native applications, developers generally apply one of two methods. The hybrid application route relies on the same robust web UX framework provided by Cartographer. Developers create a mobile-first theme for the quiz, which then gets displayed inside web view component provided by the native platform. A set of application API might need to be provided to the web view implementation to aspects such as displaying product details or checkout.

The web view method could be limiting in some cases, for example, when a true native implelemntation is required or when integrating with other dynamic experiences like chat bots. Cartographer provides Conversations API that allows developers to take advantange of all recommender platform features, including routing, page configuration, questions attribute mapping, while focusing soley on implementing the UI.

# Enabling Conversations API

There is nothing that needs to be done to start using the Conversations API as it is always enabled for all projects. Simply collect your project Draft or Production API key and direct calls to the runtime/conversations API endpoint.

TIP

In the examples below we will use JavaScript and fetch API to illustrate Conversations concepts. These can be easily translated to other programming languages and platforms.

# Reference Implementations

A React Native reference implementation can be found in the public Git repository (opens new window). This sample app is fully functional and illustrates all API featuers. It could even be used as a starting point if React Native is your chosen platform for native app development.

# Localizing the UI

All page, question, choice descriptions, or product fields automatically pick translated copy according to locale parameter.

# Starting a Conversation

Conversations is a stateful API. The current state is represented by a JSON object that is returned by the API. The state then can be modified by the implementation and posted back to the API to process the user input, retrieve the UI state or generate recommendations.

To start the conversation, two parameters are required:

  • A project API key, either draft or production mode;
  • Requested locale ISO identifier, either in short form (language only), or language and country combination.

State object should be posted as JSON body (Content-Type: application/json) tohttps://api.cartographer.drivecommerce.com/api/v4/runtime/conversations endpoint (or another latest version).

All initial parameters can be configured as following:

PARAMETER PURPOSE
project Project API key
locale Locale identifier
limit Maximum number of returned recommendations, 3 by default

TIP

Do not alter user or session identifiers.

# Slugs

Each API object has two identifers: an automatically generated system ID, and a slug. Slugs can be assigned manually in the Cartograher quiz configuration UI and could be helpful to customize the UX presentation.

# Interpreting Pages Structure

After the initial call, and with every following interaction, the state object returned from the API will contain a list of pages that describe the quiz structure. The can be multiple pages in the list — however only the last page is considered current. Preciding pages are included for the reference, for example if the native UI needs to display a history of previous questions or provide a nice animation when going from one page to another.

A sample API response, typically after the initial interaction

{
  "user": "...",
  "session": "...",
  "project": "HTNIg)njFTVmk)5T7jc_",
  "limit": 3,
  "locale": "en",
  "pages": [
    {
      "page": "7nOqatDc0GTtRO5Vqv8T",
      "complete": false,
      "resultsPage": false,
      "canAdvance": false,
      "canReturn": false,
      "slug": null,
      "description": "Let's brew some coffee",
      "questions": [
        {
          "question": "Kpv3qcJqRVtaKRSlDJTJ",
          "description": "Do you want to brew your own coffee today?",
          "type": "MultipleChoice",
          "showAs": "Buttons",
          "choices": [
            {
              "choice": "dJGdTlpxOBHgylRxamaX",
              "slug": null,
              "description": "Yes",
              "value": null,
              "selected": false
            },
            {
              "choice": "5qMhyjepuw9MwvxGDdTo",
              "slug": null,
              "description": "No",
              "value": null,
              "selected": false
            }
          ]
        }
      ]
    }
  ],
  "recommendations": null
}

Each page object contains a few properties that describe the current page state:

PROPERTY PURPOSE
page An automatically generated unique page identifier
slug A secondary page identifier as configured in the quiz
description Translated page title
body Translated page body
footer Translated page footer
image A body image
canReturn If set to true, a user may be able to return to the previous page
canAdvance If set to true, a user may advance to the next page (see Advancing the Quiz section)
complete If set to true, the page is considered complete and the user will be moved to the next page.
resultsPage If set to true, the page does not contain any questions and intented to display recommendations instead.
questions Questions list.

# Questions

A non-results page will also contain a list of questions. Each page may have one or more questions, and a user may be required to answer all or at least one question to advance to the next step.

A question object contains following properties:

PROPERTY PURPOSE
question An automatically generated unique question identifier
slug A secondary question identifier as configured in the quiz
description Translated question title
body Translated question body
footer Translated question footer
image A question image
type Question type (see below)
showAs Question preferred UX (see below)
choices Choices list

Each question will have one or more possible answers. The question's showAs parameter defines the preferred UX associated with the question as configured in the Cartographer backend, however this parameter can be overridden by the implementation.

SHOW AS TYPE DESCRIPTION
Buttons Show the question as a set of radion buttons, only as single answer can be selected at a time
MultiSelect Show the question as a set of checkbox buttons, with multiple choices that could be selected at the same time
Slider Show the question as a slider
Dropdown Show the question as a dropdown

# Choices

Each choice object represents a potential answer to the question that can be selected by a user:

PROPERTY PURPOSE
choice An automatically generated unique choice identifier
slug A secondary choice identifier as configured in the quiz
description Translated choice description
body Translated choice body
image An image associated with the choice
selected A flag indicating whether the choice is selected

# Responding To User Actions

When a user selects question choice, the implementation needs to make selected choice properties according to the selection. It is up to the implementation to enforce the single-select vs multi-select functionality according to the showAs question property. However, if a multiple choices are selected for a single select question type, only the first choice will be used by Cartographer backend and rest of choice selections will be ignored.

After the selection, the implementation should modify the state JSON object, post it back to the API endpoint, and recieve the new state object in return.

# Marking a Choice as Selected

// For example, in a single select scenario, unselect other choices first.
someQuestion.choices.forEach((choice) => {
    choice.selected = false;
});
// Select the new choice
someChoice.selected = true;

# Update the State

fetch('https://api.cartographer.drivecommerce.com/api/v4/runtime/conversations', {
    method: 'POST',
    cache: 'no-store',
    headers: {
        'Content-Type': 'application/json',
    },
    body: JSON.stringify(stateObject),
}).then((response) => response.json()).then((newState) => {
    // Save the new state.
    stateObject = newState;
});

# Advancing to the Next Step

Once the user input is processed, the state object will update the current page canAdvance and complete properties. Depending on the page type, the two things may happen.

# Auto-Advancing Pages

If the page is configured to autoadvance on completion, the current page complete property will be automatically set to true, Conversations API will determine the next page according to page routing rules, and will append the next page to the pages list. The implementation should detect that the current page had changed, generate the new page UX, and display any appropriate page transition animations.

# Manually Advancing Pages

If the page needs manual advancement, the API will set the canAdvance property to true, but will leave complete property as false. The implementation should display a Next button and process button clicks. Upon the click, the impementation should set the complete property to true, update the conversation state, and process page transations as in the autoadvancing case. See example code snippet below:

page.complete = true;

# Update the State

// As in the example above...

TIP

A single quiz may have a mix of autoadvancing and manually advancing pages. For example, pages with single choice selections may advance automatically, but pages with multi-select questions could require a Next button click.

# Returning to the Previous Page

If the page canReturn property is set to true, the implementation should provide a way to return to the previous question, for example, display a Back button. When a user choses to return to the previous page, the implementation should simply delete the last page from the pages stack.

# Returning Back

// Simply remove the current (last) page from the stack.
stateObject.pages.pop();

// For example, undo selections on the now last page.
stateObject.pages[stateObject.pages.length - 1].questions.forEach((question) => {
    question.choices.forEach((choice) => {
        choice.selected = false;
    });
});

// Also clear the page complete property to prevent the page autoadvancing again.
stateObject.pages[stateObject.pages.length - 1].complete = false;

# Update the State

// As in the example above...

TIP

The implementation should decide whether to leave previous choices selected or clear them.

# Displaying Recommendations

Ultimately, the goal of the quiz is provide product recommendations to the user. Depending on the quiz configuration, recommendations can be generated:

  • After each choice selection. For example, if the quiz UX prefers to display product recommendation as soon as possible and update the recommendations list as the user continues to navigate through quiz questions.
  • At the end of the quiz.

In either case, when it is time to display recommendations, Conversations API will populate recommendations property on the state object.

PROPERTY PURPOSE
recommendations Recommendations list.
recommendationsCount A number of generated recommendations.

Recommendations object will contain a number of propeties that describe each product: the product data, information about why a product was chosen for recommendations, and any included custom attibutes.

TIP

Generate some recommendations and explore the API state object to explore recommendations data structure.

# Recommendations JSON

{
  "recommendations": {
    "recommendations": [
      {
        "product": {
          "externalId": "FrenchPress",
          "name": "French Press",
          "description": "Offers direct infusion for full-bodied coffee",
          "defaultImageUrl": "//cdn.drrv.co/drive/frenchpress@2x.png",
          "orderable": true,
          "priceCurrency": "USD",
          "price": 1.00,
          "parentProduct": null
        },
        "match": 100.0,
        "plan": {
          "match": 100.0,
          "questions": [
            {
              "factorId": "6sdMYt2cP5u9XzWzeO6o",
              "questionId": "Kpv3qcJqRVtaKRSlDJTJ",
              "match": 100.0,
              "asked": 1.0,
              "rating": 1.0
            }
          ],
          "verboseData": null
        },
        "attributes": {},
        "segment": {
          "name": "Primary",
          "slug": null
        }
      },
      ...
    ],
    "recommendationsCount": 3,
    "recommendationsTruncated": true
  }
}