AlgorithmX Docs Logo
DocsReact Native Integration

React Native Integration

This guide shows how to add the AlgorithmX SDK to a React Native app that is already in production, with its own login, analytics, push notifications, and navigation. You do not rebuild any of these. You add a few calls in the places where your app already handles startup, sign-in, business events, notifications, and navigation. The SDK does the rest: it shows campaign notifications and in-app campaigns, tracks interactions, and applies frequency rules.

The package react-native-engage-sdk wraps the native Android and iOS SDKs. The work splits in two:

  • Native code (a few lines): start the SDK in MainApplication and AppDelegate, and pass push tokens and notifications to it. This has to be native because a push can start your app before JavaScript runs.
  • JavaScript: identify customers, send events, and handle campaign navigation.

Names such as appRouter in the examples stand for your own code; they are not SDK APIs.

How the SDK fits into your app

Where in your existing appWhat you addSection
package.json, Podfile, settings.gradleThe package and the native SDKs it wraps.2
MainApplication and AppDelegateOne line to start the SDK.3
JavaScript startupRegister the SDK listeners and bind JavaScript to the SDK.3
Login, restored session, logoutTell the SDK who the customer is.4
Business eventsForward the agreed events.5
Your push setup (Firebase on Android, APNs on iOS)Pass tokens and AlgorithmX notifications to the SDK.6
Your navigationOpen your screens from campaign links, custom actions, and buttons.7

In-app campaigns need no screen or view code from you: once these connections are in place, the SDK shows campaigns by itself (section 8).

1. Before you start

What AlgorithmX gives you

  • The react-native-engage-sdk package with its version, plus the native SDKs it wraps: the Android SDK module and the iOS EngageSDK pod.
  • The API base URL for each environment, such as staging and production. All URLs use HTTPS.

What your team gives AlgorithmX

AlgorithmX sends notifications through Firebase on Android and through Apple's push service (APNs) on iOS, so it needs the same details as a native app:

PlatformWhat to sendDetails
AndroidApplication ID, your Firebase project, and access to send messages through it.Android guide, section 1
iOSBundle ID, Team ID, APNs authentication key and Key ID, and which build type each workspace serves.iOS guide, section 1, including the Apple Developer account checklist

Also agree with the AlgorithmX team on the customer ID you will use (section 4), the events you will send (section 5), and the deep links and custom actions campaigns may use (section 7).

Expo: the package contains native code, so it does not run in Expo Go. Expo projects need a development build, and the native changes in sections 3 and 6 must survive prebuild, for example through a config plugin.

2. Add the package

Install the package from the location AlgorithmX gives you, for example a package file, then connect the native SDKs it wraps. The file name and paths below are examples.

npm install ./react-native-engage-sdk-1.0.0.tgz

Android: include the Android SDK module in android/settings.gradle, and add it to your app module so your native code can call it:

// android/settings.gradle
include ':android-sdk'
project(':android-sdk').projectDir = new File(rootDir, '../algorithmx/android-sdk')
// android/app/build.gradle, existing dependencies block
implementation project(':android-sdk')

iOS: add the native SDK pod to your app target in ios/Podfile, then run pod install. Autolinking adds the package's own pod.

target 'YourApp' do
  pod 'EngageSDK', :path => '../algorithmx/ios-sdk'
  # Keep your existing pods and React Native setup.
end

The SDK needs Android minSdk 24 and iOS 15.1 or later. It works with the New Architecture and was verified with React Native 0.87.

If you install the package from a folder instead of a package file, npm links the folder instead of copying it. In that case, add the folder to watchFolders in metro.config.js, set resolver.nodeModulesPaths to your app's node_modules, and set preserveSymlinks: true in tsconfig.json.

3. Initialize the SDK

In native code

Start the SDK in your existing native entry points, before React Native loads. A push can start your app in the background, and this is the code that runs then.

Android, in MainApplication.onCreate:

override fun onCreate() {
    super.onCreate()
    AlgorithmXReactNative.initialize(this, "https://api.example.com")
    AlgorithmX.setSmallIcon(R.drawable.ic_stat_notification) // Optional: your notification icon.
    // Keep your existing startup code, including loadReactNative(this).
}

iOS, in application(_:didFinishLaunchingWithOptions:) of your AppDelegate:

import EngageRnSdk
import EngageSDK
 
// Inside application(_:didFinishLaunchingWithOptions:), before React Native starts:
AlgorithmXReactNative.initialize(apiBaseUrl: "https://api.example.com", appGroup: "group.com.example.shop")
UNUserNotificationCenter.current().delegate = self // Keep your existing delegate assignment.
application.registerForRemoteNotifications() // Every launch, independent of permission.

The App Group is optional; it is only needed for iOS delivery reporting (section 6). Without it, call AlgorithmXReactNative.initialize(apiBaseUrl:). The examples use Swift, which React Native 0.77 and later generate; with an older Objective-C AppDelegate, make the same calls from Objective-C.

In JavaScript

At your app's entry, before the first screen renders, register your listeners (section 7) and then bind JavaScript to the SDK with the same URL:

import EngageSdk from 'react-native-engage-sdk';
 
// Register the listeners from section 7 here first.
 
async function start() {
  await EngageSdk.init('https://api.example.com');
}
start();
  • init connects JavaScript to the SDK that native code already started. It rejects with E_INIT if the URL differs from the native one.
  • Do not register native handlers yourself. The package already connects the SDK's native callbacks to JavaScript; setting deepLinkHandler or setNotificationClickListener natively would disconnect them.
  • To see each SDK request while testing, call AlgorithmX.setLoggingEnabled(true) (Android) or AlgorithmX.shared.setLoggingEnabled(true) (iOS) in the native startup code.

4. Identify your customers

Until you identify a customer, the SDK uses an anonymous device ID. Call identify with the same customer ID your backend uses, in two places: after a successful login, and when your app restores a saved session at startup. The second one matters for customers who are already signed in when they update to the first release with AlgorithmX.

// In the existing restored-session or successful-login code.
EngageSdk.identify('customer_123', { first_name: 'Amina', language: 'ar', loyalty_tier: 'gold' });

The SDK remembers the customer across app restarts and sends their previous anonymous ID, so AlgorithmX can link earlier activity to the profile. At logout, and when one account replaces another, clear waiting campaigns so the next person does not see them:

// Before completing logout:
await EngageSdk.clearWebViewQueue();
EngageSdk.resetIdentity();
 
// Before replacing one signed-in account with another:
await EngageSdk.clearWebViewQueue();
EngageSdk.identify('customer_456', { language: 'en' });

resetIdentity() returns to the anonymous device ID. AlgorithmX identity follows your login; it does not replace your authentication.

5. Track business events

Forward the agreed events from the places where your app already knows an action succeeded, for example your order-confirmation handler, next to your existing analytics calls.

EngageSdk.trackEvent('product_view', { product_id: 'SKU-123' });
EngageSdk.trackEvent('add_to_cart', { product_id: 'SKU-123', quantity: 2 });
EngageSdk.trackEvent('purchase', {
  order_id: 'ORDER-1001',
  total: 149.9,
  currency: 'SAR',
  items: [{ product_id: 'SKU-123', quantity: 2 }],
});
  • Use plain JSON values: strings, numbers, booleans, arrays, and objects. Send dates as timestamps or ISO 8601 strings.
  • Use simple event names such as add_to_cart; the name becomes part of the request URL.
  • Events are not retried. An event sent while the device is offline is lost, so keep your own backend as the source of truth for orders and payments.

6. Connect your existing push notifications

Your app keeps its current push setup. On each platform you pass the device token to AlgorithmX and pass AlgorithmX notifications to the SDK.

Android

If your app has its own FirebaseMessagingService, add the AlgorithmX branch and token registration exactly as in section 6 of the Android guide. The branch goes before your own notification code:

override fun onMessageReceived(message: RemoteMessage) {
    super.onMessageReceived(message)
    if (AlgorithmX.isAlgorithmXPush(message.data)) {
        AlgorithmX.handleFcmMessage(
            applicationContext, message.data,
            message.notification?.title, message.notification?.body
        )
        return
    }
    // Keep your current message handling here.
}
 
override fun onNewToken(token: String) {
    super.onNewToken(token)
    AlgorithmX.registerDeviceToken(token)
}

Also register the current token on every launch in MainApplication.onCreate:

FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
    AlgorithmX.registerDeviceToken(token)
}

If your app handles Firebase messages in JavaScript with @react-native-firebase/messaging, forward AlgorithmX messages from your existing handlers instead. Your background handler stays in index.js, outside any component, as Firebase requires:

import { Platform } from 'react-native';
import messaging, { FirebaseMessagingTypes } from '@react-native-firebase/messaging';
import EngageSdk from 'react-native-engage-sdk';
 
async function forwardToAlgorithmX(message: FirebaseMessagingTypes.RemoteMessage) {
  const data = (message.data ?? {}) as Record<string, string>;
  if (data.engage_action === 'algo_show_notification' || data.engage_action === 'algo_trigger_webview') {
    EngageSdk.handleFcmMessage(data, message.notification?.title, message.notification?.body);
  }
}
 
messaging().setBackgroundMessageHandler(async (message) => {
  await forwardToAlgorithmX(message);
  // Keep your existing background handling here.
});
messaging().onMessage(async (message) => {
  await forwardToAlgorithmX(message);
  // Keep your existing foreground handling here.
});
 
// Android only: on iOS, AlgorithmX needs the APNs token, registered natively below.
if (Platform.OS === 'android') {
  messaging().getToken().then((token) => EngageSdk.registerDeviceToken(token));
  messaging().onTokenRefresh((token) => EngageSdk.registerDeviceToken(token));
}

In both cases, keep your existing notification permission prompt; the SDK never asks for permission.

iOS

iOS notifications go through your AppDelegate, so this part is native and identical to section 6 of the iOS guide. Add the AlgorithmX lines to your existing token callback and notification delegate methods:

func application(
    _ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
    let apnsToken = deviceToken.map { String(format: "%02x", $0) }.joined()
    AlgorithmX.shared.registerDeviceToken(apnsToken)
}
 
func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    if AlgorithmX.shared.isAlgorithmXPush(userInfo: userInfo) {
        AlgorithmX.shared.handleNotification(userInfo: userInfo) { completionHandler(.newData) }
        return
    }
    // Keep your current handling here.
}
 
func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    willPresent notification: UNNotification,
    withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
    let userInfo = notification.request.content.userInfo
    if AlgorithmX.shared.isAlgorithmXPush(userInfo: userInfo) {
        AlgorithmX.shared.handleNotification(userInfo: userInfo) { completionHandler([.banner, .list, .sound]) }
        return
    }
    // Keep your current foreground presentation here.
}
 
func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void
) {
    let userInfo = response.notification.request.content.userInfo
    if AlgorithmX.shared.isAlgorithmXPush(userInfo: userInfo) {
        AlgorithmX.shared.handleNotificationResponse(
            actionIdentifier: response.actionIdentifier,
            userInfo: userInfo,
            completionHandler: completionHandler
        )
        return
    }
    // Keep your current tap handling here.
}
  • Send the APNs token, even if your app also uses Firebase on iOS.
  • If a library such as Firebase or Notifee owns your notification delegate, send a real AlgorithmX notification during testing to check that these lines still run.
  • Enable Background Modes, Remote notifications in Xcode; in-app campaigns need it.
  • Images and buttons (optional): add the call from section 10 of the iOS guide to your notification service extension. React Native apps use CocoaPods, so compile the SDK file EngageNotificationService.swift into the extension instead of adding the pod there.

7. Handle navigation in JavaScript

When a customer taps an AlgorithmX notification, the SDK sends an event to JavaScript. Your listeners open the screens:

EventWhenWhat your listener does
onDeepLinkThe campaign action is "open screen".Open the link with your router. The SDK does not navigate.
onCustomActionThe campaign action is a custom action.Run the named action with its parameters.
onActionButtonThe customer taps a notification button.Handle the button. Every button comes to JavaScript, and the notification's main action does not run afterwards.
onNotificationClickEvery tap on the notification itself, for information.Nothing required. The SDK already runs the campaign's action, so do not navigate here too.

"Open web page" and "open in-app campaign" actions are handled by the SDK and need no listener.

import EngageSdk from 'react-native-engage-sdk';
import { appRouter } from './navigation/appRouter'; // Your existing navigation code.
 
EngageSdk.addListener('onDeepLink', ({ url }) => appRouter.openUrl(url));
 
EngageSdk.addListener('onCustomAction', ({ action, data }) => {
  const params = (data.parsedActionData ?? {}) as Record<string, unknown>;
  if (action === 'view_product' && typeof params.product_id === 'string') {
    appRouter.openProduct(params.product_id);
  } else if (action === 'view_cart') {
    appRouter.openCart();
  }
});
 
EngageSdk.addListener('onActionButton', ({ actionText }) => {
  if (actionText === 'view_cart') appRouter.openCart();
});
  • Register all listeners together, at app startup, before the first render, for example in index.js or a module it imports. The SDK holds events that arrive before JavaScript is ready, such as the tap that launched the app, and delivers them as soon as the first listener is added. Listeners added later, inside a screen, can miss them.
  • Your router must wait for your navigation to be ready. On a tap that launches the app, events arrive before your navigation container is mounted. Keep the destination and open it once navigation is ready (with React Navigation, when navigationRef.isReady() is true or in onReady).
  • Reuse your existing links. Give the campaign team the list of links your app supports, with their required parameters, for example myapp://open/product?id=SKU-123. You do not need new URL schemes or screens.
  • Agree on action names and parameter types with the campaign team, and only offer campaign actions that your released app version implements. Match buttons on their action text, not their translated title.
  • A campaign parameter is only a navigation hint: keep your normal login and permission checks before sensitive actions.

8. In-app campaigns

In-app campaigns need no extra code. With sections 3 to 6 in place:

  1. AlgorithmX sends a background message to the device, and your native push code passes it to the SDK.
  2. The SDK saves the campaign.
  3. The next time the customer opens or returns to the app, the SDK shows the campaign over the current screen, at most one per visit. It closes with the campaign's close button, or with Back on Android.

Points to check in your app:

  • Logout and account switch: clear waiting campaigns as shown in section 4.
  • Your own pop-ups at launch: a campaign can appear at the moment the app opens, on top of whatever is on screen. Check the combination with any modal your app shows at startup.
  • Close button: iOS has no Back button, so every campaign template must include a close button. Remind your campaign team.
  • Notification permission is not needed for in-app campaigns.
  • Delivery is best effort when the customer force-stops the app (Android) or force-closes it from the app switcher (iOS): messages arrive only after the app is opened again.

To follow campaign activity while testing, listen to onCampaignInteraction; it reports each impression, click, and close.

9. Test and ship

Test on real Android and iOS devices with a test customer, using release builds as well as debug builds. On iOS, repeat the main checks with a TestFlight build, because store builds use a different push environment.

CheckExpected result
Update from your current store version while signed inThe customer is identified without logging in again, and the token is registered.
Login, restart, logout, second accountEvents use the right customer each time, and no campaign from the first account appears for the second.
Your existing notificationsOther notifications behave exactly as before.
AlgorithmX notification with the app open, in the background, and closedA tap opens the right screen once, including when the tap launches the app.
Each deep link, custom action, and button your app supportsThe right screen opens; unknown links and actions follow your app's normal fallback.
In-app campaignIt appears the next time the app becomes active, over the current screen, and closes with its close button.
Notification permission deniedIn-app campaigns still arrive; visible notifications do not.
Business eventsThe agreed events appear for the customer in AlgorithmX.

API reference

CallUse
AlgorithmXReactNative.initialize(this, apiBaseUrl) (Android)Start the SDK in MainApplication.onCreate.
AlgorithmXReactNative.initialize(apiBaseUrl:appGroup:) (iOS)Start the SDK in the AppDelegate. The App Group is optional.
EngageSdk.init(apiBaseUrl)Bind JavaScript to the SDK; use the same URL as native code.
EngageSdk.identify(userId, attributes)Identify the customer after login and when a session is restored.
EngageSdk.resetIdentity()Return to the anonymous device ID at logout.
EngageSdk.trackEvent(name, properties)Send a business event.
EngageSdk.registerDeviceToken(token)Pass a token from JavaScript, when your token handling lives there.
EngageSdk.handleFcmMessage(data, title, body)Android: forward an AlgorithmX message received in JavaScript.
EngageSdk.addListener(event, handler)Receive onDeepLink, onCustomAction, onActionButton, onNotificationClick, onCampaignInteraction. Returns a subscription with remove().
EngageSdk.clearWebViewQueue()Remove waiting campaigns at logout or account switch.
EngageSdk.getDeviceFingerprint(), EngageSdk.getWebViewQueueSize()Inspect the current customer ID and waiting campaigns while testing.

The native calls in section 6 (AlgorithmX.handleFcmMessage, AlgorithmX.shared.handleNotification, and so on) are the same as in the Android and iOS guides.

Troubleshooting

SymptomWhat to check
"Native module not found" at startupAndroid: :android-sdk is included in settings.gradle. iOS: the EngageSDK pod is in the Podfile and pod install ran. Rebuild the app after installing.
init rejects with E_INITJavaScript and native code use different URLs. Use the same value in both.
No events in AlgorithmXNative initialization runs at startup with the right HTTPS URL, and the device is online.
No AlgorithmX notifications arriveSee the troubleshooting section of the Android or iOS guide: token registration, sending access, and forwarding.
Tap on a notification does not reach JavaScriptListeners are registered at startup, all together, before the first render; native code does not set its own SDK handlers.
Tap that launches the app opens nothingYour router keeps the destination until navigation is ready.
Screen opens twice after a tapYou navigate in onNotificationClick as well as in onDeepLink or onCustomAction. Navigate only in the specific listener.
A button does nothingYour onActionButton listener has no case for that action text; the main action does not run as a fallback.
In-app campaign never appearsNative push forwarding is in place, iOS Background Modes is on, the app was not force-closed, and you reopened the app after the campaign was sent.
A previous account's campaign appearsCall clearWebViewQueue before logout and account switch.

For creating campaigns, continue with In-App Push Campaigns and In-App View Campaigns. Return to Integrations for the other platforms.