sirrus-react-native-sdk
v0.1.80
Published
Lightweight React Native SDK for Sirrus event ingestion and push notification handling.
Maintainers
Readme
Sirrus React Native SDK
Sirrus React Native SDK adds Sirrus event ingestion and Sirrus push notification handling to a React Native app.
It is designed to live alongside Firebase, MoEngage, Notifee, or an app's own notification code. The SDK handles only notifications that are explicitly marked for Martech handling.
What The SDK Handles
- Initializes with a Sirrus SDK API key.
- Calls Sirrus SSO init internally and uses the returned org/project IDs, ingestion URL, notification URL, JWT, and PII public key.
- Creates and persists visitor, installation, and session IDs using storage provided by the host app.
- Tracks custom events and screen views through the Sirrus ingestion API.
- Registers the app's Firebase token as a
userTokenevent after login. - Receives and displays Sirrus push notifications.
- Supports rich push images and dynamic action buttons.
- Sends Sirrus push receive/open/action analytics to the
notificationUrlreturned by SSO init.
What The Host App Owns
The SDK does not replace the app's Firebase/APNs setup.
The host app must:
- Configure Firebase and APNs.
- Add
google-services.json/GoogleService-Info.plist. - Request notification permission.
- Get the Firebase token.
- Pass the token to
registerTokenafter the user is logged in. - Keep existing handling for notifications that are not owned by this SDK.
- Add the required native extension/service setup for rich push where needed.
Installation
yarn add sirrus-react-native-sdkor:
npm install sirrus-react-native-sdkFor local tarball testing:
yarn add file:./sirrus-react-native-sdk-<version>.tgzAfter installing, rebuild the native app. For iOS, run pods:
cd ios
pod installQuick Start
Initialize the SDK once when the app starts. Pass persistent storage from your app, such as MMKV or AsyncStorage.
import {
initSirrusManagedSdk,
sirrusReactNativeSDK,
} from "sirrus-react-native-sdk";
const sirrusStorage = {
getItem: async (key: string) => mmkv.getString(key) ?? null,
setItem: async (key: string, value: string) => {
mmkv.set(key, value);
},
removeItem: async (key: string) => {
mmkv.remove(key);
},
};
await initSirrusManagedSdk(
sirrusReactNativeSDK,
{
apiKey: "SIRRUS_SDK_API_KEY",
storage: sirrusStorage,
debug: __DEV__,
appInfo: {
name: "Your App",
version: "1.0.0",
buildNumber: "100",
bundleId: "com.example.app",
environment: "production",
},
},
{
firebaseMessaging: {
onMessage: (listener) => messaging().onMessage(listener),
onNotificationOpenedApp: (listener) =>
messaging().onNotificationOpenedApp(listener),
getInitialNotification: () => messaging().getInitialNotification(),
},
},
);The default notification setup is usually enough:
- The SDK handles only Martech-marked notifications.
- Android uses a high-importance channel named
Sirrus Marketingwith IDsirrus-marketing. - iOS foreground Sirrus notifications use the native iOS notification path.
- Android data-only Martech messages are rendered by the SDK native service when the SDK service is the active FCM receiver.
Use notificationUI only when your app needs custom Android channels, custom iOS categories, a custom presenter, or custom source matching.
For Android, product apps can pass channel IDs during init. The SDK creates those channels on the device, and incoming Martech notifications can target them with data.android_channel_id.
await initSirrusManagedSdk(sirrusReactNativeSDK, {
apiKey: "SIRRUS_SDK_API_KEY",
storage: sirrusStorage,
notificationUI: {
androidChannelIds: ["Promotion", "Updates"],
},
});Register The User Token
Call registerToken after login, when your app has both the Firebase token and the logged-in user details.
token, userId, and phone are required. userId and phone are sent unencrypted because the backend needs them as plain root fields.
await sirrusReactNativeSDK.registerToken({
token: fcmToken,
userId: loggedInUserId,
phone: loggedInPhoneNumber,
});This sends an immediate ingestion request using the normal Sirrus batch envelope with a single userToken event.
The SDK also stores userId and phone in the provided storage and includes them in later event payloads.
You can update or clear user details explicitly:
await sirrusReactNativeSDK.setUserInfo({
userId: loggedInUserId,
phone: loggedInPhoneNumber,
});
await sirrusReactNativeSDK.setUserInfo(null);Track Events
await sirrusReactNativeSDK.track("booking_started", {
entryPoint: "home",
});
await sirrusReactNativeSDK.screen("Home", {
role: "customer",
});Events are queued, persisted, batched, retried, and flushed through the ingestion URL returned by SSO init.
Each event includes device context and context.device.sdkVersion so the backend can identify which SDK version produced the payload.
Optional PII Data
Use setPii only for extra PII values that should be encrypted before being sent to Sirrus.
sirrusReactNativeSDK.setPii({
email: "[email protected]",
name: "Example User",
});
sirrusReactNativeSDK.setPii(null);The SDK encrypts this object with the PII public key returned by SSO init and sends it as the root pii field on ingestion batches.
If the React Native runtime does not provide WebCrypto, pass your own encryptor:
await sirrusReactNativeSDK.init({
apiKey: "SIRRUS_SDK_API_KEY",
storage: sirrusStorage,
piiEncryptor: async (publicKey, payload) => {
return encryptWithYourNativeCrypto(publicKey, payload);
},
});Notification Ownership
The backend must mark SDK-owned notifications with data.source set to martech.
Notifications without the Martech marker are ignored by the SDK, so Firebase, MoEngage, Notifee, or your app's existing notification code can continue to handle them.
Notification Analytics
SSO init returns two important URLs:
ingestionUrlfor normal analytics events and token registration.notificationUrlfor Sirrus push lifecycle events.
The SDK sends notification analytics for:
receivedwhen a Sirrus notification is received.clickwhen the notification body is opened.replywhen an action button is clicked.
For action buttons, the SDK includes the clicked action value and label when available. Campaign metadata is forwarded only when it is present in the notification data. The SDK does not invent campaign metadata.
All SDK API requests include these headers:
client_id: TCG-WEB-APP
application_platform: TCG-DXP-APPAndroid Setup
If Your App Does Not Have A FirebaseMessagingService
The SDK includes SirrusFirebaseMessagingService in its Android library manifest. If your app does not declare another FirebaseMessagingService, Android can merge and use the SDK service automatically.
Your app still needs:
- Firebase configured in the app.
google-services.jsonin the Android app.- Notification permission requested by the app on Android 13+.
- A valid notification channel ID in the notification payload.
If Your App Already Has A FirebaseMessagingService
Use one deterministic FCM receiver. In that receiver, let the SDK check the message first. If the SDK handles it, return immediately. Otherwise continue with your existing notification logic.
import ai.sirrus.reactnativesdk.SirrusPushMessagingHandler
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
class AppFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
if (SirrusPushMessagingHandler.handleRemoteMessage(applicationContext, remoteMessage)) {
return
}
// Existing non-Sirrus handling, such as MoEngage, Notifee, or app logic.
}
override fun onNewToken(token: String) {
SirrusPushMessagingHandler.handleNewToken(applicationContext, token, "fcm")
// Existing product token handling.
}
}Declare your app service and remove the SDK auto service from the final merged manifest:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application>
<service
android:name=".AppFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service
android:name="ai.sirrus.reactnativesdk.SirrusFirebaseMessagingService"
tools:node="remove" />
</application>
</manifest>Android Payload Rules
For Android rich media and action buttons, the backend should send SDK-owned pushes as data-only FCM messages.
Do not include the top-level FCM notification object for Android SDK-owned pushes. Do not include android.notification. If Firebase renders the notification itself, the SDK cannot attach native action buttons.
Coordinate with the backend team to include data.source = "martech", a unique message ID, title/body, optional raw image URL, optional action list, and a valid Android channel ID.
Android Notification Channels
Android will drop or hide notifications that target a channel that does not exist on the device.
By default, the SDK creates:
channel id: sirrus-marketing
channel name: Sirrus Marketing
importance: highIf your backend sends a different android_channel_id, create that channel during SDK init.
For simple channels, pass only the IDs:
await sirrusReactNativeSDK.init({
apiKey: "SIRRUS_SDK_API_KEY",
storage: sirrusStorage,
notificationUI: {
androidChannelIds: ["Promotion", "Updates"],
},
});The android_channel_id value in the notification data must match the channel ID exactly:
android_channel_id: PromotionIf a user disables that channel in Android notification settings, Android will block notifications for that channel. The SDK does not override user notification settings.
For custom names or importance, pass full channel objects:
await sirrusReactNativeSDK.init({
apiKey: "SIRRUS_SDK_API_KEY",
storage: sirrusStorage,
notificationUI: {
defaultAndroidChannelId: "marketing",
androidChannels: [
{
id: "marketing",
name: "Marketing",
importance: "high",
},
],
},
});Use stable channel IDs. Android does not allow an app to change a channel's importance after the channel has been created on the device.
iOS Setup
The main iOS app must have:
- Firebase/APNs configured.
GoogleService-Info.plistadded to the app target.- Push Notifications capability enabled.
- Notification permission requested by the app.
- Sirrus initialized from React Native.
- Firebase token passed to
registerTokenafter login.
iOS Rich Push Extension
iOS requires a Notification Service Extension for rich media and dynamic action buttons in background or killed state.
Create one extension target per app bundle ID. For example:
Main app bundle ID: com.example.app
Extension bundle ID: com.example.app.SirrusNotificationService
Extension target: SirrusNotificationServiceIf your app has separate Dev, Staging, and Production bundle IDs, each app target needs its own embedded extension target:
com.example.app.dev.SirrusNotificationService
com.example.app.staging.SirrusNotificationService
com.example.app.SirrusNotificationServiceRecommended Extension Source Setup
Add the SDK notification-service source file to the extension target's Compile Sources:
node_modules/sirrus-react-native-sdk/ios/NotificationService/SirrusNotificationServiceExtension.swiftThen keep the extension's NotificationService.swift small:
import UserNotifications
final class NotificationService: SirrusNotificationServiceExtension {}This method keeps the extension independent from React Native and avoids linking the full SDK framework into the extension.
When using this source-file method, do not also add a separate Podfile target for sirrus-react-native-sdk/NotificationService; the extension compiles the notification-service source directly.
The extension Info.plist must point to the service class:
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>Make sure the extension target is embedded in the main app target under Embed App Extensions and is signed with the correct team/profile.
iOS Notification Tap Forwarding
If your app owns UNUserNotificationCenterDelegate, forward notification taps to Sirrus before your other push handling. This lets Sirrus send click/action analytics even when a notification opens the app from killed state.
import SirrusReactNativeSdk
import UserNotifications
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
UNUserNotificationCenter.current().delegate = self
_ = SirrusNotificationResponseBridge.handleLaunchOptions(launchOptions)
return true
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void
) {
if SirrusNotificationResponseBridge.handleNotificationResponse(response) {
completionHandler()
return
}
// Existing non-Sirrus notification handling.
completionHandler()
}iOS Payload Rules
iOS rich notifications need:
- A visible APNs alert.
mutableContent: true.- Image URL in
apns.fcmOptions.imageUrl. - The same image URL in
data.imageUrl. data.actionsas a JSON string when action buttons are needed.
Use raw image URLs. Do not send Markdown link strings like [https://...](https://...).
Coordinate with the backend team to include data.source = "martech", a unique message ID, a visible APNs alert, mutableContent: true, optional raw image URL, and optional action list.
Notification Interaction Listener
Use this when the product app wants to navigate or react after a Sirrus notification body/action click.
const unsubscribe = sirrusReactNativeSDK.onNotificationInteraction((event) => {
const data = {
...(event.payload.rawPayload ?? {}),
...(event.payload.data ?? {}),
};
if (event.type === "opened") {
// User tapped the notification body.
}
if (event.type === "action_pressed") {
// User tapped an action button.
console.log(event.actionId, event.action);
}
});If a notification opens the app before React Native subscribes, the SDK stores the interaction briefly and delivers it to the first listener.
React Root Wrapper
SirrusSdkRoot is available as a compatibility wrapper. It does not render overlays.
import { SirrusSdkRoot } from "sirrus-react-native-sdk";
export const App = () => {
return (
<SirrusSdkRoot>
<Navigation />
</SirrusSdkRoot>
);
};Using the wrapper is optional unless your app already relies on it.
Setup Helper
The package includes a helper that can inspect common native setup issues.
npx sirrus-react-native-sdk setupUse --apply only after reviewing what it will change:
npx sirrus-react-native-sdk setup --applyNative projects differ a lot, especially when Firebase, MoEngage, Notifee, or multiple app targets are present. Always review generated native changes before committing.
Useful APIs
sirrusReactNativeSDK.init(config);
sirrusReactNativeSDK.track(eventName, properties);
sirrusReactNativeSDK.screen(screenName, properties);
sirrusReactNativeSDK.registerToken({
token,
userId,
phone,
});
sirrusReactNativeSDK.setUserInfo({ userId, phone });
sirrusReactNativeSDK.setUserInfo(null);
sirrusReactNativeSDK.setPii({ email, name });
sirrusReactNativeSDK.setPii(null);
sirrusReactNativeSDK.onNotificationInteraction((event) => {
// Handle Sirrus notification body/action clicks.
});
sirrusReactNativeSDK.flush();
sirrusReactNativeSDK.shutdown();Troubleshooting
Foreground notification received but not displayed:
- Confirm the payload has
source: "martech". - Confirm the app has notification permission.
- On Android, confirm the message is data-only.
- On iOS, confirm the notification delegate forwards Sirrus notifications or the SDK delegate is active.
Android image or action buttons missing:
- Do not send a top-level
notificationobject for Android SDK-owned pushes. - Do not send
android.notification. - Confirm
android_channel_idexists on the device. - Confirm
data.actionsis a JSON string.
iOS image or action buttons missing:
- Confirm the Notification Service Extension target is embedded in the app target you are running.
- Confirm the extension bundle ID is prefixed by the main app bundle ID.
- Confirm
mutableContent: trueis present. - Confirm
apns.fcmOptions.imageUrlanddata.imageUrlare raw URLs. - Use a direct
.jpgor.pngURL for the simplest rich-image test.
Other notifications affected:
- Confirm the SDK only receives messages with
source: "martech". - If your app has its own
FirebaseMessagingService, callSirrusPushMessagingHandler.handleRemoteMessage(...)first and continue existing logic only when it returnsfalse.
Release Checklist
Before publishing a new SDK version:
- Run
pnpm lint. - Run
pnpm build. - Run
pnpm test. - Run
git diff --check. - Test Android foreground, background, and killed-state Sirrus notifications.
- Test iOS foreground, background, and killed-state Sirrus notifications.
- Verify image rendering and action buttons.
- Verify notification receive/open/action analytics.
- Verify non-Sirrus notifications still work.
