Skip to main content

React Native SDK - Current (23.3.1)

Introduction

The React Native SDK wraps the Atomic iOS and Android SDKs, allowing you to use them in your React Native apps.

The current stable release is 23.3.1.

Supported iOS and Android versions

The React Native SDK supports iOS 12.4 and above, and Android 5.0 (API 21) and above.

React Native 0.63+ is required.

Boilerplate app

You can use our React Native boilerplate app to help you get started with the Atomic SDK using React Native. You can download it from its GitHub repository. Alternatively, you can follow this guide.

Installation

To include the React Native SDK in your existing React Native app, run the following command:

npm install @atomic.io/react-native-atomic-sdk

Or if you are using yarn:

yarn add @atomic.io/react-native-atomic-sdk

Installation instructions can also be found in the public NPM module: https://www.npmjs.com/package/@atomic.io/react-native-atomic-sdk.

The React Native SDK supports auto-linking.

iOS

Navigate to the directory containing your Podfile (normally ios), and run pod install to install the latest Pods. You should see the following dependency in the output:

...
Installing react-native-atomic-sdk (x.y.z)
...

Android

Add the following block to the build.gradle file in your app's android directory, alongside the other React Native dependencies. This will allow the Atomic Android SDK dependency to be downloaded from our public Maven repository.

allprojects {
repositories {
...
maven {
url "https://downloads.atomic.io/android-sdk/maven"
}
}
}

Setup

Before you can display an Atomic stream container or single card view in your app, you must configure the SDK.

You can find your API base URL in the Atomic Workbench, under Configuration > SDK > API Host.

The SDK API base URL is different to the API base URL endpoint, which is also available under Configuration. The SDK API base URL ends with client-api.atomic.io.

You also need to provide the SDK a session delegate for resolving authentications.

SDK API base URL

Set your SDK API base URL by calling the setApiBaseUrl method:

import {Session as AtomicSession} from '@atomic.io/react-native-atomic-sdk'

AtomicSession.setApiBaseUrl('<url>')

Environment ID and API key

Within your host app, you will need to call the initialise method to configure the SDK. Your environment ID can be found in the Atomic Workbench, under Configuration, and your API key can be configured under Configuration > SDK > API Keys.

import {Session as AtomicSession} from '@atomic.io/react-native-atomic-sdk'

AtomicSession.initialise('<environmentId>', '<apiKey>')

Authenticating requests using a JWT

Atomic SDK uses a JSON Web Token (JWT) to perform authentications.

The SDK Authentication guide provides step-by-step instructions on how to generate a JWT and add a public key to the Workbench.

Within your host app, you will need to call setSessionDelegate to provide a callback to get the JWT. It is expected that the token returned by this method represents the same user until you call the logout method.

import {Session as AtomicSession} from '@atomic.io/react-native-atomic-sdk'

AtomicSession.setSessionDelegate(async () => {
const token = await getAuthTokenFromSomewhere()
return token
})

JWT Expiry interval

This feature is not available on React Native. Contact us if this feature is important to you.

JWT Retry interval

The Atomic SDK allows you to configure the timeout interval (in seconds) between retries to get a JSON Web Token from the session delegate if it returns a null token. The SDK will not request a new token for this amount of seconds from your supplied session delegate. The default value is 0, which means it will immediately retry your session delegate for a new token.

AtomicSession.setTokenRetryInterval(10);

WebSockets and HTTP API protocols

Atomic SDK uses WebSockets as a default communication protocol to fetch and send data to Atomic Platform. However, if the WebSockets connection is interrupted and cannot be re-established after three attempts, the SDK will fall back to HTTP.

You may override the default behavior, and communication between the Atomic SDK and Atomic Platform can be established by using HTTP instead. To switch protocols use the setApiProtocol method.

AtomicSession.setApiProtocol('http');

Displaying containers

Options for Displaying Cards

There are three distinct options for displaying cards:

  1. <StreamContainer />
  1. <HorizontalContainerView/>
  1. <SingleCardView />
    • Displays only a single card at a time - the most recently sent card in a stream.
    • Automatically adjusts its size to match the displayed card.

Before you can start streaming cards you must provide a stream container ID, authentication token and configuration object to each option.

Stream container ID

You’ll need to locate your stream container ID first.

Navigate to the Workbench, select Configuration > SDK > Stream containers and find the ID next to the stream container you are integrating.

Stream container props

The Atomic SDK allows configuration of the stream containers using the AACStreamContainerProps interface.

  • containerId (string): The ID of the stream container to display, found in the Atomic Workbench.
  • configuration (AACStreamContainerConfiguration): A configuration object that allows you to customize functionality within the stream container.
  • filter (object): A filter that can be applied to the stream container, influencing the cards displayed.
  • runtimeVariablesRequested (callback, optional): Triggered when the card list contains one or more cards with runtime variables that need to be resolved. You must return a Promise that resolves to the provided list of cards, with runtime variables resolved.
  • linkButtonTapped (callback, optional): Triggered by the stream container or single card view when the user taps on a link button with a custom payload (as defined on the card in the Atomic Workbench). The only argument passed to this function is an object containing the stream container ID and card instance ID that triggered the call, and the custom action payload itself (defined in the Atomic Workbench).
  • submitButtonTapped (callback, optional): Triggered by the stream container or single card view when the user taps on a submit button with a custom payload (as defined on the card in the Atomic Workbench). The only argument passed to this function is an object containing the stream container ID and card instance ID that triggered the call, and the custom action payload itself (defined in the Atomic Workbench).
  • cardEventTriggered (callback, optional): Triggered when a card event occurs, such as when a card is submitted, dismissed or snoozed.
  • actionButtonTapped (callback): Triggered by the stream container when the user taps on the action button in the top left (iOS) or top right (Android) of the container. Only applies if configuration.presentationStyle is set to actionButton.

Single and horizontal card views also support the following property:

Stream container configuration

The following functionality can be customized using the configuration object:

If you do not wish to customize any of the configuration options, you must pass an empty object for the configuration property.

Style and presentation

  • presentationStyle (string): The presentation style for the stream container. Does not apply in single card view. Set to actionButton to display an action button in the top left (iOS) or top right (Android) of the stream container, or noButton to hide the button. If set to actionButton, the actionButtonTapped handler is called when the user taps on the action button.
  • launchColors (object): Colors used for the initial load screen, shown the very first time that a user views a stream container or single card view. The initial theme for the container is downloaded on this screen. Colors must be wrapped with processColor() before being specified here. Properties are:
    • background (color): The color to use for the background of the initial load screen.
    • loadingIndicator (color): The color to use for the loading indicator on the initial load screen.
    • button (color): The color to use for the buttons on the initial load screen.
    • text (color): The color to use for the text on the initial load screen.
  • statusBarBackgroundColor (color, Android only): The background color to use for the status bar on secondary screens, such as the snooze selection screen.
  • interfaceStyle (string, iOS only): Sets the interface style of the stream container or single card view. This influences whether the light or dark theme is used. Possible values are:
    • automatic: Use the system setting for iOS 13+, otherwise use the container's light theme (default).
    • light: Always use the light theme.
    • dark: Always use the dark theme.
  • enabledUiElements: An object representing UI elements that should be enabled in the stream container. Defaults to showing toast messages and the card list header in a stream container. The card list header setting doesn't have any effect in the single card view. Supply an empty object to turn off all possible UI elements. Each property takes a boolean value:
    • cardListToast: Toast messages should appear at the bottom of the card list, or bottom of the screen when using a single card view. Toast messages appear when cards are submitted, dismissed or snoozed, or when an error occurs in any of these actions.
    • cardListFooterMessage: A footer message should be displayed below the last card in the card list, if at least one is present. The message is customized using the cardListFooterMessage custom string.
    • cardListHeader: The header should display at the top of the card list, allowing the user to pull down from the top of the screen to refresh the card list.
  • cardVotingOptions (string): Sets the card voting options available from a card's overflow menu.
    • both: The user can flag a card as either useful or not useful.
    • notUseful: The user can flag a card as 'not useful' only.
    • useful: The user can flag a card as 'useful' only.
    • none: The user cannot vote on a card (default).

Functionality

  • pollingInterval (number, in seconds): How often the stream container should check for new cards. Defaults to 15 seconds. pollingInterval only applies to HTTP polling and has no effect when WebSockets is on.
  • runtimeVariablesTimeout (number, in seconds): The maximum amount of time allocated when resolving runtime variables. If the tasks inside of the resolver take longer than this timeout, default values will be used for all runtime variables. Defaults to 5 seconds, and cannot be negative.
  • features (object, optional): An object representing features that can be turned on or off in the stream container or single card view. Supported features include:
    • runtimeVariableAnalytics (boolean): Whether the runtime-vars-updated analytics event, which includes resolved values of each runtime variable, should be sent when runtime variables are resolved. Defaults to false. When setting this flag to true, ensure that the resolved values of your runtime variables do not include any sensitive information that should not appear in analytics.
  • automaticallyLoadNextCard: (Single card view, iOS only): When enabled, will automatically display the next card in the single card view if there is one, using a locally cached card list. Defaults to false.

Custom strings

  • customStrings (object): Strings that can be customized throughout the SDK. Properties are:
    • cardListTitle (string): The title to display at the top of the card list. Defaults to "Cards".
    • cardSnoozeTitle (string): The title to display for the card snooze functionality in the card overflow menu. Defaults to "Remind me".
    • awaitingFirstCard (string): The message displayed over the card list, when the user has never received a card before. Defaults to "Cards will appear here when there’s something to action.".
    • allCardsCompleted (string): The message displayed when the user has received at least one card before, and there are currently no cards to show. Defaults to "All caught up".
    • votingUseful (string): The title to display for the action a user taps when they flag a card as useful. Defaults to "This is useful".
    • votingNotUseful (string): The title to display for the action a user taps when they flag a card as not useful. Defaults to "This isn't useful".
    • votingFeedbackTitle (string): The title to display at the top of the screen allowing a user to provide feedback on why they didn't find a card useful. Defaults to "Send feedback".
    • cardListFooterMessage: The message to display below the last card in the card list, provided there is at least one present. Does not apply in single card view, and requires enabledUiElements to contain cardListFooterMessage. Defaults to an empty string.
    • noInternetConnectionMessage: The error message shown when the user does not have an internet connection. Defaults to "No internet connection".
    • dataLoadFailedMessage: The error message shown when the theme or card list cannot be loaded due to an API error. Defaults to "Couldn't load data".
    • tryAgainTitle: The title of the button allowing the user to retry the failed request for the card list or theme. Defaults to "Try again".
    • toastCardDismissMessage: The toast message for when the user dismisses a card. Defaults to "Card dismissed".
    • toastCardCompletedMessage: The toast message for when the user completes a card. Defaults to "Card completed".
    • toastCardSnoozeMessage: The toast message for when the user snoozes a card. Defaults to "Snoozed until $TIME", e.g. "Snoozed until 10:30am".
    • toastCardFeedbackMessage: The toast message for when the user sends feedback (votes) a card. Defaults to "Feedback received".

Displaying a vertical container

Start by importing the React Native component StreamContainer:

import {StreamContainer} from '@atomic.io/react-native-atomic-sdk'

Then you can create a stream container using the following code snippet:

<StreamContainer
style={{width: '100%', height: '100%'}}
containerId={streamContainerId}
configuration={{
...
}}
/>

Displaying a custom header

Only for vertical stream containers

Custom header is only available for the vertical <StreamContainer> component. It has no effect in the single card view and horizontal stream container.

You can provide a custom component to the SDK as a header that scrolls up along with the cards. This custom component is placed below the built-in header if the built-in one is enabled.

There are no restrictions for views in the header, except that it should not change size dynamically.

To include a custom header, surround your custom view in the CustomHeader container, and place it inside your StreamContainer.

import { StreamContainer, CustomHeader } from '@atomic.io/react-native-atomic-sdk'


<StreamContainer
style={{width: '100%', height: '100%'}}
containerId={streamContainerId}
configuration={{
...
}}>

<CustomHeader>
<View style={{backgroundColor: "#1c72b2"}}>
<Text>ReactNative custom header</Text>
<Text>Name: John Doe</Text>
</View>
</CustomHeader>

</StreamContainer>

Displaying a horizontal container

(Requires React Native SDK Version 1.0.7+. This feature is available only on iOS.)

The Atomic React Native SDK also supports rendering a horizontally laid stream container in your host app. The horizontal view renders cards from left to right. You must provide a value for the width of the cards. The height of the container will be determined by the cards in the feed. You can monitor size changes using the sizeDidChange callback.

import {HorizontalContainerView} from '@atomic.io/react-native-atomic-sdk'
<HorizontalContainerView
style={{width: '100%'}}
containerId={streamContainerId}
configuration={{
...
cardWidth: 350,
...
}}
sizeDidChange={(width, height) => {
console.log(
`Horizontal container view size changed: width=${width}, height=${height}`
)
}}
/>

Displaying a single card

Start by importing the React Native component SingleCardView:

import {SingleCardView} from '@atomic.io/react-native-atomic-sdk'

Then create a single card view:

<SingleCardView
style={{width: '100%'}}
containerId={streamContainerId}
configuration={{
...
}}
/>

The single card view automatically sizes itself to fit the card it is displaying - therefore you do not need to specify a height.

You can also be notified when the single card view changes size, by assigning a callback to the sizeDidChange property on the single card view. You will be supplied with the width and height of the single card view as arguments.

<SingleCardView
...
sizeDidChange={(width, height) => {
console.log(
`view size changed: width=${width}, height=${height}`
)
}}
/>

Customizing the first time loading behavior

When a stream container with a given ID is launched for the first time on a user's device, the SDK loads the theme and caches it for future use. On subsequent launches of the same stream container, the cached theme is used and the theme is updated in the background, for the next launch. Note that this first-time loading screen is not presented in single card view and horizontal container view - if those views fail to load, they collapse to a height of 0.

The SDK supports some basic properties to style the first-time load screen, which displays a loading spinner in the center of the container. If the theme or card list fails to load for the first time, an error message is displayed with a 'Try again' button. One of two error messages is possible - 'Couldn't load data' or 'No internet connection'.

First-time loading screen colors are customized using the launchColors property on AACStreamContainerConfiguration:

  • background: The background color to use for the launch screen, seen on the first load. Defaults to white.
  • loadingIndicator: The color to use for the loading spinner on the first-time loading screen. Defaults to black.
  • button: The color of the buttons that allow the user to retry the first load if the request fails. Defaults to black.
  • text: The text color to use for the view displayed when the SDK is first presented. Defaults to black at 50% opacity.

You can also customize the text for the first load screen error messages and the 'Try again' button, using the customStrings object

Note: These customized error messages also apply to the card list screen.

  • noInternetConnectionMessage: The error message shown when the user does not have an internet connection. Defaults to "No internet connection".
  • dataLoadFailedMessage: The error message shown when the theme or card list cannot be loaded due to an API error. Defaults to "Couldn't load data".
  • tryAgainTitle: The title of the button allowing the user to retry the failed request for the card list or theme. Defaults to "Try again".

Dark mode

Stream containers in the Atomic SDK support dark mode. You configure an (optional) dark theme for your stream container in the Atomic Workbench.

The AACInterfaceStyle determines which theme is rendered:

  • automatic: If the user's device is currently set to light mode, the stream container will use the light (default) theme. If the user's device is currently set to dark mode, the stream container will use the dark theme (or fallback to the light theme if this has not been configured). On iOS versions less than 13, this setting is equivalent to light.
  • light: The stream container will always render in light mode, regardless of the device setting.
  • dark: The stream container will always render in dark mode, regardless of the device setting.

Filtering cards

Stream containers and single card views can have an optional filter applied, which affects the cards displayed.

One filter is currently supported, which requests that the stream container or single card view show only a card matching the specified card instance ID, if it exists.

The card instance ID can be found in the push notification payload, allowing you to apply the filter in response to a push notification being tapped.

const cardIdFilter = { byCardInstanceId: "<cardId>" }
const containerRef = useRef(null)

...

<StreamContainer
style={{width: '100%', height: '100%'}}
containerId={streamContainerId}
filter={cardIdFilter}
configuration={{
...
}}
ref={containerRef}
/>

In the Atomic Workbench, you can create a submit or link button with a custom action payload.

  • When such a link button is tapped, the linkButtonTapped callback that can be provided with the AACStreamContainerProps is invoked.
  • When such a submit button is tapped, and after the card is successfully submitted, the linkButtonTapped callback is invoked.

These callbacks take a AACCardAction action object, containing the payload that was defined in the Workbench for that button. You can use this payload to determine the action to take, within your app, when the submit or link button is tapped.

The action object also contains the card instance ID and stream container ID where the custom action was triggered.

Customizing toast messages for card events

You can customize any of the toast messages used when dismissing, completing, snoozing and placing feedback on a card. This is configurable for each stream container. You simply supply a string for each custom message. If you do not supply a string, the defaults will be used.

  • toastCardDismissMessage: The toast message for when the user dismisses a card. Defaults to "Card dismissed".
  • toastCardCompletedMessage: The toast message for when the user completes a card. Defaults to "Card completed".
  • toastCardSnoozeMessage: The toast message for when the user snoozes a card. Defaults to "Snoozed until $TIME", e.g. "Snoozed until 10:30am".
  • toastCardFeedbackMessage: The toast message for when the user sends feedback (votes) a card. Defaults to "Feedback received".

Card snoozing

The Atomic SDKs provide the ability to snooze a card from a stream container or single card view. Snooze functionality is exposed through the card’s action buttons, overflow menu and the quick actions menu (exposed by swiping a card to the left, on iOS and Android).

Tapping on the snooze option from either location brings up the snooze date and time selection screen. The user selects a date and time in the future until which the card will be snoozed. Snoozing a card will result in the card disappearing from the user’s card list or single card view, and reappearing again at the selected date and time. A user can snooze a card more than once.

When a card comes out of a snoozed state, if the card has an associated push notification, and the user has push notifications enabled, the user will see another notification, where the title is prefixed with Snoozed:.

You can customize the title of the snooze functionality, as displayed in a card’s overflow menu and in the title of the card snooze screen using the customStrings object. The default title, if none is specified, is Remind me.

Card voting

The Atomic SDKs support card voting, which allows you to gauge user sentiment towards the cards you send. When integrating the SDKs, you can choose to enable options for customers to indicate whether a card was useful to the user or not, accessible when they tap on the overflow button in the top right of a card.

If the user indicates that the card was useful, a corresponding analytics event is sent for that card (card-voted-up).

If they indicate that the card was not useful, they are presented with a secondary screen where they can choose to provide further feedback. The available reasons for why a card wasn’t useful are:

  • It’s not relevant;
  • I see this too often;
  • Something else.

If they select "Something else", a free-form input is presented, where the user can provide additional feedback. The free form input is limited to 280 characters. After tapping "Submit", an analytics event containing this feedback is sent (card-voted-down).

You can customize the titles that are displayed for these actions, as well as the title displayed on the secondary feedback screen. By default, these are:

  • Thumbs up - "This is useful";
  • Thumbs down - "This isn’t useful";
  • Secondary screen title - "Send feedback".

Card voting can be enabled on a per-card basis, or as a default for the environment. You can override this in the SDK to always show positive card voting ("This is useful"), negative card voting ("This isn’t useful"), or both. Set the cardVotingOptions field on the AACStreamContainerConfiguration to both, notUseful, useful or none. Where none will inherit the card setting.

You can also customize the titles for the card voting options, and the title displayed at the top of the feedback screen, presented when a user indicates the card wasn’t useful. Use the customStrings configuration to change these.

...
customStrings: {
votingFeedbackTitle: "Provide feedback",
votingUseful: "Thumbs up",
votingNotUseful: "Thumbs down"
},
...

Refreshing a stream container manually

Feature not fully supported in React Native

This feature is only supported on iOS currently.

You can choose to manually refresh a stream container or single card view, such as when a push notification arrives while your app is open. Refreshing will result in the stream container or single card view checking for new cards immediately, and showing any that are available.

const containerRef = useRef(null)

...

<StreamContainer
style={{width: '100%', height: '100%'}}
containerId={streamContainerId}
configuration={{
...
}}
ref={containerRef}
/>

...

if(containerRef.current) {
// Check for new cards immediately.
containerRef.current.refresh()
}

Responding to card events

The SDK allows you to perform custom actions in response to events occurring on a card, such as when a user:

  • submits a card (or fails to submit a card);
  • dismisses a card (or fails to dismiss a card);
  • snoozes a card (or fails to snooze a card);
  • indicates a card is useful (when card voting is enabled);
  • indicates a card is not useful (when card voting is enabled).

To be notified when these happen, assign a cardEventTriggered callback on your stream container or single card view:

<StreamContainer
style={{width: '100%', height: '100%'}}
containerId={streamContainerId}
configuration={{
...
}}
ref={containerRef}
cardEventTriggered={event => {
// Event is stored in `event.cardEvent`
// Possible values are: cardSubmitted, cardDismissed, cardSnoozed, cardSubmitFailed, cardDismissFailed, cardSnoozeFailed, cardVotedUseful, cardVotedNotUseful
}}
/>

Sending custom events

You can send custom events directly to the Atomic Platform for the logged-in user, via the sendCustomEvent method on AACSession.

A custom event can be used in the Workbench to create segments for card targeting. For more details of custom events, see Custom Events.

The event will be created for the user defined by the authentication token returned by the session delegate. As such, you cannot specify target user IDs using this method.

import {Session as AtomicSession} from '@atomic.io/react-native-atomic-sdk'

await AtomicSession.sendCustomEvent({
eventName: 'myEvent',
properties: {
firstName: 'John',
age: 42
}
})

Push notifications

For the quickest way to see push notifications working. See the React Native boilerplate app push notification branch on GitHub.

Enabling push notifications also requires organization level configuration within the Apple developer portal on iOS, or using the Firebase Cloud Messaging service on Android. For a complete step-by-step guide for this process refer to the Configure Push Notification support for React Native guide.

To use push notifications in the React Native SDK, you'll need to set up your notification preferences and add your iOS push certificate and Android server key in the Workbench (as explained in the Notifications section of the Configuration guide), request push notification permission in your app, and register the device token and stream container using the Atomic SDK.

Push notification support requires a React Native library such as react-native-push-notification. You may also need a library to request permissions from the user such as react-native-permissions.

Once this is integrated, you can configure push notifications via the React Native SDK. The steps below can occur in either order in your app. The methods are authenticated, so you should wait until you have received your JWT token before calling them.

1. Register the user against specific stream containers for push notifications

You need to signal to the Atomic Platform which stream containers are eligible to receive push notifications in your app for the current device.

import {Session as AtomicSession} from '@atomic.io/react-native-atomic-sdk'

AtomicSession.registerStreamContainersForNotifications([containerId])

You will need to do this each time the logged in user changes.

This method takes an optional parameter - notificationsEnabled. This parameter updates the user's notificationsEnabled preference in the Atomic Platform. You can also inspect and update this preference using the Atomic API - consult the Atomic API documentation for user preferences for more information.

If you pass false for this parameter, the user's notificationsEnabled preference will be set to false, which means that they will not receive notifications on any eligible devices, even if their device is registered in this step, and the device push token is passed to Atomic in the next step. If you pass true, the user's notificationEnabled preference will be set to true, which is the default, and allows the user to receive notifications on any eligible device. This allows you to explicitly enable or disable notifications for the current user, via UI in your own app - such as a notification settings screen.

If you call registerStreamContainersForNotifications, without supplying the notificationsEnabled parameter, the user's notificationsEnabled preference in the Atomic Platform is not affected.

AtomicSession.registerStreamContainersForNotifications([containerId], true)

To deregister the device for Atomic notifications for your app, such as when a user completely logs out of your app, call deregisterDeviceForNotifications on AtomicSession. If the deregistration fails, the promise will be rejected, returning a description of the failure that occurred.

try {
await AtomicSession.deregisterDeviceForNotifications()
} catch (e) {
// `e` contains the reason for the failure to deregister the device for notifications.
}

2. Send the push token to the Atomic Platform

Send the device's push token to the Atomic Platform when it changes. Call this method in the appropriate push notification callback in your app:

import {Session as AtomicSession} from '@atomic.io/react-native-atomic-sdk'

AtomicSession.registerDeviceForNotifications(deviceToken)

You can call the registerDeviceForNotifications method any time you want to update the push token stored for the user in the Atomic Platform; pass the method the device token as a string.

You will also need to update this token every time the logged-in user changes in your app, so the Atomic Platform knows who to send notifications to.

3. (Optional) Track when push notifications are received

To track when push notifications are delivered to your user's device on iOS, you can use a native Notification Service Extension, supported in iOS 10 and above. This requires you to write native code - further information on tracking notification receipt is available in the iOS documentation.

If you do not wish to build a native notification service extension, you can choose to track notification delivery when a user taps on the notification instead:

import {Session as AtomicSession} from '@atomic.io/react-native-atomic-sdk'

// Call this method when the user taps on the notification.
AtomicSession.trackPushNotificationReceived(result.payload)
.then(() => {
// Push notification receipt was successfully tracked.
// Convert the push payload to a structured object.
const notification = AtomicSession.notificationFromPushPayload(
result.payload
)

// If the notification originated from Atomic, `notification` will be non-null
})
.catch(e) => {
// Failed to track - either the push notification payload was not for an Atomic push notification,
// or the user is offline.
})

Retrieving card count

Use user metrics

It is recommended that you use user metrics to retrieve the card count instead. See the next section for more information.

The SDK supports observing the card count for a particular stream container, or receiving a single card count, even when that stream container does not exist in memory.

The behavior for this functionality differs between iOS and Android:

  • iOS: Card count is provided to your callback independently of whether a stream container or single card view has been created, and is updated at the provided interval.
  • Android: A stream container or single card view must be created on screen for card count to be returned. The interval for updates is set on the stream container, rather than the value you specify when requesting card count.
import {Session as AtomicSession} from '@atomic.io/react-native-atomic-sdk'

// Retain this token so that you can stop observing later.
const token = AtomicSession.observeCardCountForStreamContainer(
containerId,
pollingInterval,
cardCount => {
console.log(`Card count is now ${cardCount}`)
}
)

When you want to stop observing the card count, you can remove the observer using the token returned from the observation call above:

import {Session as AtomicSession} from '@atomic.io/react-native-atomic-sdk'

AtomicSession.stopObservingCardCountForStreamContainer(token)

Alternatively, you can request a one-off card count without having to create a stream container first:

try {
const cardCount = await AtomicSession.requestCardCount(`<streamContainerId>`)
console.log(`There are ${cardCount} cards in the container.`)
} catch (e) {
// `e` provides details of the failure.
}

Retrieving the count of active and unseen cards

What is an active card? What is an unseen card?

All cards are unseen the moment they are sent. A card becomes "seen" when it has been shown on the customer's screen (even if only briefly or partly). A quick scroll-through might not make the card "seen", this depends on the scrolling speed. The user metrics only count "active" cards, which means that snoozed and embargoed cards will not be included in the count.

The Atomic React Native SDK allows you to fetch the following metrics for a user:

  • The number of cards available to the user across all stream containers;
  • The number of cards that haven't been seen across all stream containers;
  • The number of cards available to the user in a specific stream container (equivalent to the card count functionality in the previous section);
  • The number of cards not yet seen by the user in a specific stream container.

These metrics enable you to display badges in your UI that indicate how many cards are available to the user but not yet viewed, or the total number of cards available to the user.

This method rejects the returned promise if metrics cannot be retrieved.

To fetch metrics for a specific stream container:

try {
const metrics = await AtomicSession.userMetrics('<containerId>')
console.log(`Total cards in container: ${metrics.totalCards}`)
console.log(`Unseen cards in container: ${metrics.unseenCards}`)
} catch (e) {
// `e` provides a description of what went wrong.
}

To fetch metrics across all stream containers:

try {
const metrics = await AtomicSession.userMetrics(null)
console.log(`Total cards in all containers: ${metrics.totalCards}`)
console.log(`Unseen cards in all containers: ${metrics.unseenCards}`)
} catch (e) {
// `e` provides a description of what went wrong.
}

Runtime variables

Runtime variables are resolved in the SDK at runtime, rather than from an event payload when the card is assembled. Runtime variables are defined in the Atomic Workbench.

The SDK will ask the host app to resolve runtime variables when a list of cards is loaded (and at least one card has a runtime variable), or when new cards become available due to polling (and at least one card has a runtime variable).

Runtime variables are resolved by your app via the runtimeVariablesRequested callback on the StreamContainer or SingleCardView components. If you do not provide this callback, runtime variables will fall back to their default values, as defined in the Atomic Workbench.

The callback, when triggered by the SDK, provides you with an array of objects representing the cards in the list. Each card object contains:

  • The event name (eventName) that triggered the card’s creation;
  • The lifecycle identifier (lifecycleId) associated with the card;
  • A map of runtime variables to be resolved, along with their default values (runtimeVariables).

The callback must return a promise. The promise is resolved with the original array of cards, where each card in the original array has its runtimeVariables map updated to reflect the variables you have resolved (see example below).

If a variable is not resolved, that variable will use its default value, as defined in the Atomic Workbench.

If your returned promise is not resolved before the runtimeVariableResolutionTimeout elapses (defined on the configuration object), the default values for all runtime variables will be used.

<StreamContainer
containerId={streamContainerId}
configuration={{
...
}}
runtimeVariablesRequested={async cards => {
// Resolve your runtime variables here.
cards.forEach(card => {
card.runtimeVariables.set('dateShort', new Date().toDateString())
card.runtimeVariables.set('dateLong', new Date().toString())
})

return Promise.resolve(cards)
}}
/>

Manually updating runtime variables

To manually update runtime variables on a stream container or single card view, call the updateVariables() method:

const containerRef = useRef(null)

...

<StreamContainer
style={{width: '100%', height: '100%'}}
containerId={streamContainerId}
configuration={{
...
}}
ref={containerRef}
/>

...

if(containerRef.current) {
containerRef.current.updateVariables()
}

Accessibility and fonts

Dynamic Type

The Atomic SDK supports dynamic font scaling. On iOS this feature is called Dynamic Type, and can be enabled in the Settings app, under Display & Brightness, Text Size. On Android, this feature is enabled in the Settings app, under "Font size".

To opt in to Dynamic Type for a typography style:

  1. Open the Atomic Workbench;
  2. Navigate to Configuration > SDK > Container themes. Select the theme you want to edit.
  3. When editing a typography style in the theme (e.g. Headline > Typography), turn Dynamic scaling on.
  4. Optionally specify a minimum and maximum font size to use when scaling is applied.

The SDK automatically scales the typography style from the base font size that you specify, adding or subtracting a pixel value from this base font size, to create a size tailored to each Dynamic Type level. The minimum font size is 1px - this is enforced by the SDK.

The pixel value that is added or subtracted for each Dynamic Type level is determined inside the SDK and is not customizable.

info

Typography styles that do not have 'Dynamic scaling' enabled stay at a fixed size regardless of the user's text size (this is the default behavior for all typography styles).

Font sizing behavior

When the user changes their system-wide text size, the SDK will automatically re-render any components containing typography styles that opt-in to dynamic scaling (typography styles that have the 'Dynamic scaling' option enabled in the stream container theme).

  • On iOS, the font size in the typography style has a delta value added to, or subtracted from, it using the following values. User-selected sizes correspond to the values defined in UIContentSizeCategory:
User-selected sizeDelta applied
XS-3
S-2
M-1
L (default)0
XL2
XXL4
XXXL6
Accessibility M10
Accessibility L15
Accessibility XL20
Accessibility XXL25
Accessibility XXXL30

Once these delta values (iOS) are applied, the minimum and maximum scaling sizes are applied to the resultant font size. See the example below for a demonstration of how this works.

Example scaling scenario

A typography style is configured in the Workbench with the following properties:

  • Font size: 17px
  • Dynamic scaling: On
  • Dynamic scaling minimum size: 15px
  • Dynamic scaling maximum size: 30px

On iOS, the following font sizes would be used for each content size category:

User-selected sizeResultant size
XS15px (14px is below min)
S15px (at or above min)
M16px
L (default)17px
XL19px
XXL21px
XXXL23px
Accessibility M27px
Accessibility L30px (32px is above max)
Accessibility XL30px (37px is above max)
Accessibility XXL30px (42px is above max)
Accessibility XXXL30px (47px is above max)

On Android, the following font sizes would be used for each scale factor (examples only; actual scale factors returned by the operating system may differ):

Scale factorResultant size
0.7515px (12.75px is below min)
117px
1.1519.55px
1.2521.25px
1.525.5px
230px (34px is above max)

Alternate text

Alternate text is supported on all image and video components. This alternate text is supplied by you when authoring a card, and is used to describe the image or video itself. Alternate text forms part of a description that is passed to the system screen reader (when enabled).

The following description is used for each image or video format:

FormatText supplied to screen reader
BannerAlternate text
InlineAlternate text
TextLabel is used (also defined in the Workbench), as this component does not render an image thumbnail
ThumbnailAlternate text, label and description

Using embedded fonts in themes

See also: embedded fonts in iOS and embedded fonts in Android.

To map a font in a stream container theme to one embedded in your app, use the registerEmbeddedFonts method on AtomicSession, passing an array of objects, each containing the following:

  • A familyName that matches the font family name declared in the Atomic Workbench;
  • A weight - either bold or normal, or a numeric value matching the weight in the Workbench, for example "100".
  • A style - either italic or regular;
  • A postscriptName, which matches the Postscript name of a font available to your app. This can be a font bundled with your application or one provided by the operating system (iOS).
  • A typefacePath, the path (relative to your apps "assets" folder) to your typeface file (Android).

If the familyName, weight and style of a font in the stream container theme matches an object that you've registered with the SDK, the SDK will use the postscriptName to create an instance of your embedded font on iOS and the typefacePath on Android, and will not download the font from a remote URL.

In the example below, any use of a custom font named RubikVinyl-Regular in your theme, that has a font weight of 100 and is italicized, would use the embedded font in your named RubikVinyl-Regualr on iOS, and the typeface file located at assets/fonts/RubikVinyl-Regular.ttf on Android instead:

Invalid Postscript names

If the postscriptName (iOS) or typefacePath (Android) provided is invalid, or the family name, weight and style do not match a custom font in the stream container theme exactly, the SDK will download the font at the remote URL specified in the theme instead.

AtomicSession.registerEmbeddedFonts([{
familyName: "RubikVinyl-Regular",
postscriptName: "RubikVinyl-Regular", // iOS
typefacePath: "fonts/RubikVinyl-Regular.ttf", // Android
weight: "100",
style: "italic",
}]
);

SDK Analytics

Default behavior

In React Native SDK, the default behavior is to not sending analytics for resolved runtime variables. Therefore, you must explicitly enable this feature to use it.

If you use runtime variables on a card, you can optionally choose to send the resolved values of any runtime variables back to the Atomic Platform as an analytics event. This per-card analytics event - runtime-vars-updated - contains the values of runtime variables rendered in the card and seen by the end user. Therefore, you should not enable this feature if your runtime variables contain sensitive data that you do not wish to store on the Atomic Platform.

To enable this feature, set the runtimeVariableAnalytics flag on your configuration's features object:

<StreamContainer
style={{width: '100%', height: '100%'}}
containerId={streamContainerId}
configuration={{
...
features: {
runtimeVariableAnalytics: true
}
}}
ref={containerRef}
/>

Updating user data

The SDK allows you to update profile & preference data for the logged in user via the updateUser method. This is the user as identified by the auth token provided by the authentication callback.

Setting up profile fields

To update, call the updateUser method of the AtomicSession, passing an object conforming to the AACUserSettings interface. The following optional profile fields can be supplied to update the data for the user. Any fields which have not been supplied will remain unmodified after the user update.

  • externalID: An optional string that represents an external identifier for the user.
  • name: An optional string that represents the name of the user.
  • email: An optional string that represents the email address of the user.
  • phone: An optional string that represents the phone number of the user.
  • city: An optional string that represents the city of the user.
  • country: An optional string that represents the country of the user.
  • region: An optional string that represents the region of the user.

The following code snippet shows how to set up some profile fields.

await AtomicSession.updateUser({
externalID: '123-externalId',
name: 'Jack Doe'
})

Setting up custom profile fields

You can also setup your custom fields of the user profile. Custom fields must first be created in Atomic Workbench before updating them. For more details of custom fields, see Custom Fields.

There are two types of custom fields: date and text. Text should be a string, and date should be an IOS8601 formatted date string.

Note: Use the name property in the Workbench to identify a custom field, not the label property.

The following code snippet shows how to set up a date and a text field.

await AtomicSession.updateUser({
customFields: {
'someCustomField': 'Some string example',
'someCustomDate': new Date().toISOString()
}
})

Setting up notification preferences

You can use the following optional properties to update the notification preferences for the user. Again, any fields which have not been supplied will remain unmodified after the user update.

  • notificationsEnabled: An optional boolean to determine whether notifications are enabled. The default notification setting of a user is true.
  • allowedNotificationTimeframes: A record mapping a AACUserNotificationTimeframeWeekdays (weekday) to an array of AACUserNotificationTimeframe (whitelisted timeframe where push notifications are allowed).

If an empty array is provided notifications will be disabled for that day. Hours are in the 24h format that must be between 0 & 23 inclusive, while minutes are values between 0 & 59 inclusive.

The following code snippet shows how to set up notifications as allowed between 8am - 5:30pm & 7pm - 10pm on Monday, disabled on Saturday and Sunday, and allowed from 9am - 5:30pm every other day.

await AtomicSession.updateUser({
notificationsEnabled: true,
allowedNotificationTimeframes: {
monday: [
{
startHour: 8,
startMinute: 0,
endHour: 17,
endMinute: 30
},
{
startHour: 19,
startMinute: 0,
endHour: 22,
endMinute: 0
}
],
saturday: [],
sunday: [],
default: [
{
startHour: 9,
startMinute: 0,
endHour: 17,
endMinute: 30
}
]
}
})

UpdateUser method: full example

The following code snippet shows an example of using the updateUser method to update profile fields, custom profile fields and notification preferences.

try {
await AtomicSession.updateUser(
{
externalID: '123-externalId',
name: 'Jack Doe (RN)',
email: 'rn@atomic.io',
phone: '555-555-5555',
city: 'New Plymouth',
country: 'New Zealand',
region: 'Taranaki',
notificationsEnabled: true,
allowedNotificationTimeframes: {
monday: [
{
startHour: 9,
startMinute: 0,
endHour: 12,
endMinute: 30
},
{
startHour: 13,
startMinute: 0,
endHour: 17,
endMinute: 30
}
],
saturday: [],
sunday: [],
default: [
{
startHour: 9,
startMinute: 0,
endHour: 17,
endMinute: 30
}
]
},
customFields: {
'customField': 'something',
'customDate': new Date().toISOString()
}
}
)
console.log('Succesfully updated the user')
} catch (e) {
console.log(`Failed to update the user: ${e}`)
}
Optional values

Though all fields of AACUserSettings are optional, you must supply at least one field when calling updateUser.

Observing SDK events

(introduced in 23.3.0)

The Atomic React Native SDK provides functionality to observe SDK events that symbolize identifiable SDK activities such as card feed changes or user interactions with cards. The following code snippet shows how to observe SDK events.

AtomicSession.observeSDKEvents((event) => {
console.log('sdk event observed: ', event)
})

The SDK provides all observed events in the base class AACSDKEvent. To access more specific information about a particular event, you can cast it to its corresponding event class. The table below lists all currently supported events, including some that are part of Atomic analytics. For detailed information about these events, please refer to Analytics reference.

Event nameEvent classAnalyticsDescription
card-dismissedAACSDKEventCardDismissedYESThe user dismisses a card.
card-snoozedAACSDKEventCardSnoozedYESThe user snoozes a card.
card-completedAACSDKEventCardCompletedYESThe user submits a card.
card-feed-updatedAACSDKEventCardFeedUpdatedNOA card feed has been updated
card-displayedAACSDKEventCardDisplayedYESA card is displayed in a container.
card-voted-upAACSDKEventCardVotedUpYESThe user taps on the “This is useful” option.
card-voted-downAACSDKEventCardVotedDownYESThe user taps the “Submit” button on the card feedback screen.
runtime-vars-updatedAACSDKEventRuntimeVarsUpdatedYESOne or more runtime variables are resolved.
stream-displayedAACSDKEventStreamDisplayedYESA stream container is first loaded or returned to.
user-redirectedAACSDKEventUserRedirectedYESThe user is redirected by a URL or a custom payload.
snooze-options-displayedAACSDKEventSnoozeOptionsDisplayedYESThe snooze date/time selection UI is displayed.
snooze-options-canceledAACSDKEventSnoozeOptionsCanceledYESThe user taps the “Cancel” button in the snooze UI.
card-subview-displayedAACSDKEventCardSubviewDisplayedYESA subview of card is opened.
card-subview-exitedAACSDKEventCardSubviewExitedYESThe user leaves the subview.
video-playedAACSDKEventVideoPlayedYESThe user hits the play button of a video.
video-completedAACSDKEventVideoCompletedYESA video finishes playing.
sdk-initializedAACSDKEventSDKInitializedYESAn instance of the SDK is initialized, or the JWT is refreshed.
request-failedAACSDKEventRequestFailedYESAny API request to the Atomic client API fails within the SDK, or a failure in WebSocket causes a fallback to HTTP polling.
Note: Network failure and request timeout does not trigger this event.
notification-receivedAACSDKEventNotificationReceivedYESA push notification is received by the SDK.

Accessing a specific SDK event

To access the unique properties of each event, you need to cast the base class AACSDKEvent passed in the callback to the specific event classes. You can determine the type of the event using either the eventType or eventName property.

The following code snippet shows how to retrieve the attributes of a card's subview when it is exited.

AtomicSession.observeSDKEvents((event) => {
if (event.eventType === 'cardSubviewExited') {
let title = (event as AACSDKEventCardSubviewExited).subviewTitle
console.log(title)
}
})

Stopping the observation

To stop the observation, call AtomicSession.stopObservingSDKEvents().

info

Calling stopObservingSDKEvents() will stop the observation for all registered callbacks.

More examples

Fetching the unseen card count when required

The unseen card number can be retrieved from user metrics. But it's a one-off call, so we had to repeatedly call the method to keep the unseen card number updated. By observing SDK events, you can retrieve the unseen card number whenever the seen status of a card changes, or the stream is updated. The following code snippet shows how to get the unseen card number of a container in these scenarios.

AtomicSession.observeSDKEvents((event) => {
if (event.eventType == 'cardDisplayed' || event.eventType == 'cardFeedUpdated' || event.eventType == 'streamDisplayed' || event.eventType == 'sdkInitialized') {
fetchAndUpdateUserMetrics()
}
})

Because the SDK event observer is global for all stream containers and card count listeners in the SDK, the function that calls the user metrics API should be throttled to prevent too many requests. For example, if you have several containers you may see many cardFeedUpdated events in rapid succession.

Utility methods

Debug logging

Debug logging allows you to view more verbose logs regarding events that happen in the SDK. It is turned off by default, and should not be enabled in release builds. To enable debug logging:

import {Session as AtomicSession} from '@atomic.io/react-native-atomic-sdk'

AtomicSession.enableDebugMode(level)

Where level can be any value from 0 to 3. A higher value indicates a higher verbosity level of the logs exposed.

The levels are:

  • 0 Default, exposes no logs.
  • 1 Exposes basic information. Including critical errors or hints, SDK transactions between the SDK and Platform.

Example

AtomicSDK iOS [Msg Centre] Update complete feed, card number 0.
  • 2 Includes all logs from level 1. Also exposes any package contents if available.

Example

AtomicSDK iOS [Msg Centre] Card feed update Received by WebSockets. {"messageType":"update-cards","data":{"updates":[{"type":"replace-feed","data":{"cardInstances":{"instances":[],"totalCards":0},"subscriptionId":"abcd"}}]}}
  • 3 Exposes all logs.

Logout

The React Native SDK provides a method for purging the local cache of a user's card data. The intent of this method is to clear user data when a previous user logs out, so that the cache is clear when a new user logs in to your app. This method also sends any pending analytics events back to the Atomic Platform.

From version 23.3.0, this method also closes any open WebSocket connections, and clears the cached JWT token and clears the API host to prevent any network traffic after logout.

You must ensure that all stream container views are removed after logging out, as they will no longer continue to display cards.

To logout:

import {Session as AtomicSession} from '@atomic.io/react-native-atomic-sdk'

AtomicSession.logout(async error => {
if (error) {
console.log(`Error when logging out:${JSON.stringify(error)}.`)
}
// Perform action after clearing caches
})

Setting the version of the client app

The SDK provides a method AtomicSession.setClientAppVersion for setting the current version of your app. This app version is attached to generated analytics events to make it easy to track issues between app versions and to get insight into version usage.

Version strings have a maximum length of 128 characters, if you supply a longer string it will be trimmed to that length.

The following example sets the client app version to Version 2.3.1.

AtomicSession.setClientAppVersion('Version 2.3.1')

Dependencies

The React Native SDK has dependencies on:

The iOS and Android SDKs both include their own dependencies which are listed here for iOS and here for Android.