Supporting Push Notifications
Track user interactions with push notifications to measure re-engagement campaigns and attribute conversions accurately by integrating Firebase Cloud Messaging (FCM) with the Singular SDK.
Follow the implementation guidelines below to ensure notification data is correctly passed to the Singular SDK for proper attribution.
Why Track Push Notifications: Push notifications drive re-engagement, but tracking requires correct integration. Singular ensures users who interact with notifications are properly attributed, optimizing marketing campaigns and engagement strategies.
Implementation Guide
Integrate the Singular SDK
Integrate the Singular SDK in your React Native project using the standard installation instructions as documented in the Singular React Native SDK guide.
Set Up Firebase Cloud Messaging
Install Firebase packages and configure platform-specific settings for push notification support.
Install Firebase Packages
Add the React Native Firebase dependencies for core functionality and messaging support.
npm install @react-native-firebase/app
npm install @react-native-firebase/messaging
iOS Configuration
Register your iOS app in Firebase and configure push notification capabilities in Xcode.
- Register iOS App: Create an iOS app in your Firebase Console project
-
Add Configuration File: Download
GoogleService-Info.plistand add it to your Xcode project - Enable Capabilities: In Xcode project settings, enable Push Notifications capability
- Enable Background Modes: Enable Background Modes and check Remote notifications
Android Configuration
Register your Android app in Firebase and add the configuration file to your project.
- Register Android App: Create an Android app in your Firebase Console project
-
Add Configuration File: Download
google-services.jsonand place it inandroid/app/ - Verify Dependencies: Ensure Firebase messaging dependencies are added and permissions are granted
Configure Push Link Paths
Define the JSON paths where Singular tracking links are located within your push notification payload structure.
Configure push link paths by passing arrays of strings that specify the key path to the Singular link in your notification data structure. Each path is an array representing the nested structure of keys.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/js/NativeSingular';
const config: SingularConfig = {
apikey: 'YOUR_SDK_KEY',
secret: 'YOUR_SDK_SECRET',
pushNotificationsLinkPaths: [
['sng_link'], // Top-level key
['path', 'to', 'url'], // Nested path
['rootObj', 'nestedObj', 'singularLink'] // Deep nested path
]
};
NativeSingular.init(config);
import { Singular, SingularConfig } from 'singular-react-native';
const config = new SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
)
.withPushNotificationsLinkPaths([
['sng_link'], // Top-level key
['path', 'to', 'url'], // Nested path
['rootObj', 'nestedObj', 'singularLink'] // Deep nested path
]);
Singular.init(config);
Path Configuration Examples:
-
Simple Keys: Use
['sng_link']for top-level keys in the payload -
Nested Keys: Use
['rootObj', 'nestedObj', 'key']to traverse nested JSON structures - Multiple Paths: Define multiple path arrays to check different possible locations for Singular links
For complete method documentation, see withPushNotificationsLinkPaths reference.
Platform-Specific Handling
iOS Push Notification Handling
App in Terminated State
Configure your iOS AppDelegate to pass launch options to the Singular SDK for automatic push tracking when the app opens from a terminated state.
In AppDelegate, add the following inside
didFinishLaunchingWithOptions:
// Import at the top of the file
import Singular
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
let delegate = ReactNativeDelegate()
let factory = RCTReactNativeFactory(delegate: delegate)
delegate.dependencyProvider = RCTAppDependencyProvider()
reactNativeDelegate = delegate
reactNativeFactory = factory
window = UIWindow(frame: UIScreen.main.bounds)
if let singularBridge = NSClassFromString("SingularBridge") {
let selector = NSSelectorFromString("startSessionWithLaunchOptions:")
if singularBridge.responds(to: selector) {
singularBridge.perform(selector, with: launchOptions, afterDelay: 1)
}
}
factory.startReactNative(
withModuleName: "YourAppName", // Update with your App Name
in: window,
launchOptions: launchOptions
)
return true
}
// Import at the top of the file
#import <Singular-React-Native/SingularBridge.h>
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Pass launch options to Singular for push tracking
[SingularBridge startSessionWithLaunchOptions:launchOptions];
// Your other initialization code
return YES;
}
Automatic Handling: When users tap push notifications while your app is not running, Singular automatically captures the notification payload during app launch through the launch options.
App in Background or Foreground
Use the handlePushNotification() method to pass push data
to the SDK when the app receives notifications in background or foreground
states.
// TurboModule direct API (React Native 0.76+ New Architecture)
import React, { useEffect } from 'react';
import NativeSingular from 'singular-react-native/js/NativeSingular';
import messaging from '@react-native-firebase/messaging';
export default function App() {
useEffect(() => {
// Handle foreground messages
const unsubscribeBackground = messaging().onMessage(handleForegroundMessage);
// Handle messages that opened the app from background
messaging().onNotificationOpenedApp(handleBackgroundMessage);
return () => {
unsubscribeBackground();
};
}, []);
function handleForegroundMessage(remoteMessage) {
console.log('Foreground message received:', remoteMessage);
// Pass notification data to Singular
if (remoteMessage.data) {
NativeSingular.handlePushNotification(remoteMessage.data);
}
// Your custom notification handling
displayLocalNotification(remoteMessage);
}
function handleBackgroundMessage(remoteMessage) {
console.log('Background message opened app:', remoteMessage);
// Pass notification data to Singular
if (remoteMessage.data) {
NativeSingular.handlePushNotification(remoteMessage.data);
}
// Navigate to appropriate screen
navigateFromNotification(remoteMessage);
}
function displayLocalNotification(remoteMessage) {
// Your notification display logic
console.log('Displaying notification:', remoteMessage.notification?.title);
}
function navigateFromNotification(remoteMessage) {
// Your navigation logic based on notification data
const route = remoteMessage.data?.route;
console.log('Navigating to:', route);
}
return (
// Your app components
null
);
}
import React, { useEffect } from 'react';
import { Singular } from 'singular-react-native';
import messaging from '@react-native-firebase/messaging';
export default function App() {
useEffect(() => {
// Handle background messages
const unsubscribeBackground = messaging().onMessage(handleForegroundMessage);
// Handle messages that opened the app from background
messaging().onNotificationOpenedApp(handleBackgroundMessage);
return () => {
unsubscribeBackground();
};
}, []);
function handleForegroundMessage(remoteMessage) {
console.log('Foreground message received:', remoteMessage);
// Pass notification data to Singular
if (remoteMessage.data) {
Singular.handlePushNotification(remoteMessage.data);
}
// Your custom notification handling
displayLocalNotification(remoteMessage);
}
function handleBackgroundMessage(remoteMessage) {
console.log('Background message opened app:', remoteMessage);
// Pass notification data to Singular
if (remoteMessage.data) {
Singular.handlePushNotification(remoteMessage.data);
}
// Navigate to appropriate screen
navigateFromNotification(remoteMessage);
}
function displayLocalNotification(remoteMessage) {
// Your notification display logic
console.log('Displaying notification:', remoteMessage.notification?.title);
}
function navigateFromNotification(remoteMessage) {
// Your navigation logic based on notification data
const route = remoteMessage.data?.route;
console.log('Navigating to:', route);
}
return (
// Your app components
);
}
For complete method documentation, see handlePushNotification reference.
Android Push Notification Handling
App in Terminated State
No action is required for Android apps in terminated state. The React Native bridge layer handles this scenario automatically when users tap notifications.
Automatic Handling: When users tap push notifications while your app is not running, Singular automatically captures the notification data through the native bridge integration.
App in Background or Foreground
Configure your Android MainActivity to pass notification intents to the Singular SDK when the app is in background or foreground states.
In your MainActivity (e.g., MainActivity.java or
MainActivity.kt), override onNewIntent:
// Add imports at the top
import android.content.Intent;
import net.singular.react_native.SingularBridgeModule;
// Add to MainActivity class
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// Pass intent to Singular for push tracking
SingularBridgeModule.onNewIntent(intent);
}
// Add imports at the top
import android.content.Intent
import net.singular.react_native.SingularBridgeModule
// Add to MainActivity class
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
// Pass intent to Singular for push tracking
SingularBridgeModule.onNewIntent(intent)
}
Additionally, implement Firebase message handling in your React Native code using the same approach shown for iOS background/foreground handling above.
Validation Guide
Verify Payload in Start Session
Confirm that push notification links are correctly passed to Singular by inspecting the start session API call.
The Singular SDK includes the push notification payload under the
singular_link parameter in the start session request when
users tap notifications.
Example Start Session Request:
https://sdk-api-v1.singular.net/api/v1/start?
a=<SDK-Key>
&singular_link=https://singularassist2.sng.link/C4nw9/r1m0?_dl=singular%3A%2F%2Ftest&_smtype=3
&i=net.singular.sampleapp
&s=1740905574084
&sdk=Singular/React-Native-v1.0.0
Alternative Verification: Use the Singular SDK Console to verify push notification tracking. Check the Deeplink URL field to confirm the tracking link is captured correctly.
Advanced Configuration
ESP Domain Configuration
Configure external domains if you wrap Singular links within Email Service Provider (ESP) or other third-party domains.
// TurboModule direct API (React Native 0.76+ New Architecture)
import NativeSingular from 'singular-react-native/js/NativeSingular';
// Configure ESP domains for wrapped Singular links
const config: SingularConfig = {
apikey: 'YOUR_SDK_KEY',
secret: 'YOUR_SDK_SECRET',
espDomains: ['sl.esp.link', 'custom.domain.com']
};
NativeSingular.init(config);
import { Singular, SingularConfig } from 'singular-react-native';
// Configure ESP domains for wrapped Singular links
const config = new SingularConfig(
'YOUR_SDK_KEY',
'YOUR_SDK_SECRET'
)
.withESPDomains(['sl.esp.link', 'custom.domain.com']);
Singular.init(config);
Security Note: By default, only sng.link
domains predefined in the Singular Manage Links page are permitted.
Configure ESP domains explicitly if using wrapped links.
For complete method documentation, see withESPDomains reference.
Dynamic Deep Link Routing
Implement multiple deep link destinations from a single notification by configuring one Singular tracking link with dynamic redirect overrides.
Use Case Example: A breaking news notification with multiple action options
-
Read Latest News:
newsapp://article?id=12345 -
Trending Topics:
newsapp://trending -
Sports:
newsapp://sports
Instead of creating multiple tracking links, use one Singular link and override redirects dynamically based on user selection. See Overriding Redirects in Singular Tracking Links for implementation details.
Important Considerations
Implementation Notes
-
No Callback Handler: Unlike
withSingularLink, the push notification feature does not provide payload callbacks. Implement your own deep linking logic to route users to specific content within your app - Attribution Flow: When users tap notifications, Singular retrieves the payload and includes it in the start session event triggered by SDK initialization. The backend processes this data to attribute the push notification touchpoint and register re-engagement tracking
-
Domain Restrictions: Only Singular link domains
(
sng.link) from the Manage Links page are permitted by default. Configure ESP domains explicitly for wrapped links usingwithESPDomains() - Platform Differences: iOS requires AppDelegate configuration for terminated state, while Android handles it automatically through the bridge module
Success: By following these steps, your app now tracks push notification interactions with Singular, improving campaign performance insights and ensuring accurate re-engagement attribution.