Set up push notifications

Updated

Our React Native SDK supports push notifications over APN or FCM—including rich push messages with links and images. Use this page to add support for your push provider and set your app up to receive push notifications.

This page is part of a setup flow for the SDK. Before you continue, make sure you've implemented previous features—i.e. you can't receive push notifications before you identify people!

graph LR getting-started(Install SDK) -->B(Initialize SDK) B --> identify(identify people) identify -.-> track-events(Send events) identify -.-> push(Receive push) identify -.-> in-app(Receive in-app) click getting-started href "/docs/sdk/react-native/getting-started/#install" click B href "/docs/sdk/react-native/getting-started/#initialize-the-sdk" click identify href "/docs/sdk/react-native/identify" click track-events href "/docs/sdk/react-native/track-events/" click register-token href "/docs/sdk/react-native/push" click push href "/docs/sdk/react-native/push" click rich-push href "/docs/sdk/react-native/rich-push" click in-app href "/docs/sdk/react-native/in-app" click test-support href "/docs/sdk/react-native/test-support" style push fill:#B5FFEF,stroke:#007069

How it works

Under the hood, our React Native SDK takes advantage of our native Android and iOS SDKs. This helps us keep the React Native SDK up to date. But, for now, it also means you’ll need to add a bit of code to support your iOS users. For Android, you’re ready to go if you followed our getting started instructions.

Before a device can receive a push notification, you must:

  1. (iOS) Add push notification capabilities in XCode.
  2. (iOS) Integrate push notifications: code samples on this page help you do that.
  3. Identify a person. This associates a token with the person; you can’t send push notifications to a device until you identify the recipient.
  4. Request, or check for, push notification permissions. If your app’s user doesn’t grant permission, notifications will not appear in the system tray.

While push providers support a number of features in their payloads, our React Native package only supports deep links and images right now. If you want to include action buttons or other rich push features, you need to add your own custom code. When writing your own custom code, we recommend that you use our SDK as it is much easier to extend than writing your own code from scratch.

 Did you already set up your push providers?

To send, test, and receive push notifications, you’ll need to set up your push notification service(s) in Customer.io. If you haven’t already, set up Apple Push Notification Service (APNs) and/or Firebase Cloud Messaging (FCM).

Set up push on Android

If you followed our Getting Started instructions, you’re already set up to send standard push notifications to Android devices.

Set up push on iOS

You’ll need to add some additional code to support push notifications for iOS. You’ll need to add push capabilities in XCode and integrate push capabilities in your app.

Add push capabilities in Xcode

Before you can work with push notifications, you need to add Push Notification capabilities to your project in XCode.

  1. In your React Native project, go to the ios subfolder and open <yourAppName>.xcworkspace.

  2. Select your project, and then under Targets, select your main app.

  3. Click the Signing & Capabilities tab

  4. Click Capability.

  5. Add Push Notifications to your app. When you’re done, you’ll see Push Notifications added to your app’s capabilities, but there are still a few more steps to finish setting things up.

    add push notification capabilities to your app
    add push notification capabilities to your app

  6. Go to File > New > Target.

    xcode-servicenotification1.png
    xcode-servicenotification1.png
  7. Select Notification Service Extension and click Next.

    xcode-servicenotification2.png
    xcode-servicenotification2.png
  8. Enter a product name, like NotificationServiceExtension (which we use in our examples on this page), and click Finish.

    xcode-servicenotification3.png
    xcode-servicenotification3.png
  9. When presented with the dialog below, click Cancel. This helps Xcode continue debugging your app and not just the extension you just added.

    xcode-servicenotification4.png
    xcode-servicenotification4.png

Now you have another target in your project navigator named NotificationServiceExtension. We’ll configure this extension when we Integrate Push Notifications in the following section.

Integrate push capabilities in your app

🎉Updated in version 3.4.0

 Using version 3.3 or earlier?

If you implemented push notifications using a previous version of the SDK, you can remove a significant amount of code when you update. Follow our upgrade guide to remove unnecessary code and increase compatibility with 3rd party SDKs in your app.

Pick your push provider (APN or FCM) and the language your native files are written in to get started (Objective C or Swift).

  1. Open your ios/Podfile and add a line to your main target and NotificationServiceExtension target.
    require Pod::Executable.execute_command('node', ['-p',
      'require.resolve(
        "react-native/scripts/react_native_pods.rb",
        {paths: [process.argv[1]]},
      )', __dir__]).strip
    
    platform :ios, '13.0'
    install! 'cocoapods', :deterministic_uuids => false
    
    require 'open-uri'
    IO.copy_stream(URI.open('https://raw.githubusercontent.com/customerio/customerio-ios/main/scripts/cocoapods_override_sdk.rb'), "/tmp/override_cio_sdk.rb")
    load "/tmp/override_cio_sdk.rb"
    
    target 'SampleApp' do
      config = use_native_modules!
    
      # Flags change depending on the env values.
      flags = get_default_flags()
    
      pod 'customerio-reactnative/apn', :path => '../node_modules/customerio-reactnative'
    
      use_react_native!(
        :path => config[:reactNativePath],
        :hermes_enabled => true,
        :fabric_enabled => flags[:fabric_enabled],
        # Note that if you have use_frameworks! enabled, Flipper will not work and
        # you should disable the next line.
        # An absolute path to your application root.
        :app_path => "#{Pod::Config.instance.installation_root}/.."
      )
      post_install do |installer|
          # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
          react_native_post_install(
            installer,
            config[:reactNativePath],
            :mac_catalyst_enabled => false
          )
      end
    
    end
    
    target 'NotificationServiceExtension' do
      # Notice the '-richpush' in the line below. This line of code is different from what you added for your main target. 
      pod 'customerio-reactnative-richpush/apn', :path => '../node_modules/customerio-reactnative'
    end
  2. Open your terminal, go to your project path and install the pods.
      pod install --project-directory=ios
  3. Open ios/<YourAppName>.xcworkspace in Xcode, and add a new Swift file to your project.

    Copy the code here into your file and replace the siteId and apiKey (on the highlighted line) with your credentials. We’re calling our file MyAppPushNotificationsHandler.swift and the associated class MyAppPushNotificationsHandler, but you might want to rename things to fit your app.

    import Foundation
    import CioMessagingPushAPN
    import CioTracking
    
    @objc
    public class MyAppPushNotificationsHandler : NSObject {
    
      public override init() {}
    
      @objc(setupCustomerIOClickHandling)
      public func setupCustomerIOClickHandling() {
        // This line of code is required in order for the Customer.io SDK to handle push notification click events.
        // We are working on removing this requirement in a future release.
        // Remember to modify the siteId, apiKey and region with your own values.
        CustomerIO.initialize(siteId: "siteId", apiKey: "apiKey", region: .US) { config in
        }
        MessagingPushAPN.initialize(configOptions: nil)
      }
    
      @objc(application:deviceToken:)
      public func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        MessagingPush.shared.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
      }
    
      @objc(application:error:)
      public func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        MessagingPush.shared.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
      }
    }
  4. Open your ios/AppDelegate.mm file and import your header file. The name of the header file will depend on your app’s main target name i.e. YourMainTargetName-Swift.h and is auto-created by Xcode.

    If you’re not a native iOS developer, the .h and .mm files represent interface and implementation respectively. It’s a convention of XCode to keep these files separate.

    #import "SampleApp-Swift.h"
  5. Inside AppDelegate’s @implementation, create an object of MyAppPushNotificationsHandler (remember to substitute the name of your handler).
    @implementation AppDelegate
    
    MyAppPushNotificationsHandler* pnHandlerObj = [[MyAppPushNotificationsHandler alloc] init];
  6. Update AppDelegate.mm to register a device to the current app user and handle push notifications.
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
      RCTAppSetupPrepareApp(application, true);
    
      NSMutableDictionary *modifiedLaunchOptions = [NSMutableDictionary dictionaryWithDictionary:launchOptions];
        if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]) {
            NSDictionary *pushContent = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
            if (pushContent[@"CIO"] && pushContent[@"CIO"][@"push"] && pushContent[@"CIO"][@"push"][@"link"]) {
              NSString *initialURL = pushContent[@"CIO"][@"push"][@"link"];
                if (!launchOptions[UIApplicationLaunchOptionsURLKey]) {
                    modifiedLaunchOptions[UIApplicationLaunchOptionsURLKey] = [NSURL URLWithString:initialURL];
                }
            }
        }
    
      RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:modifiedLaunchOptions];
    
    #if RCT_NEW_ARCH_ENABLED
      _contextContainer = std::make_shared<facebook::react::ContextContainer const>();
      _reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
      _contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
      _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
      bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
    #endif
    
      NSDictionary *initProps = [self prepareInitialProps];
      UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"SampleApp", initProps, true);
    
      [application registerForRemoteNotifications];
    
      self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
      UIViewController *rootViewController = [UIViewController new];
      rootViewController.view = rootView;
      self.window.rootViewController = rootViewController;
      [self.window makeKeyAndVisible];
      
      [pnHandlerObj setupCustomerIOClickHandling];
    
      [RNNotifications startMonitorNotifications];
    
      return YES;
    }
    
    ...
    
    // Required to register device token.
    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
      // Register device to receive push notifications with device token
      [pnHandlerObj application:application deviceToken:deviceToken];
    }
    
    // Required for the registrationError event.
    - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
    {
      [pnHandlerObj application:application error:error];
    }
  7. In XCode, select your NotificationServiceExtension. Go to File > New > File > Swift File and click Next. Enter a file name, like NotificationServicePushHandler, and click Create. This adds a new swift file in your extension target.

    Copy the code on the right and paste it into this new file (which we’ve caled NotificationServicePushHandler.swift) file—replacing everything in the file.

    import Foundation
    import UserNotifications
    import CioTracking
    import CioMessagingPushAPN
    
    @objc
    public class NotificationServicePushHandler : NSObject {
    
        public override init() {}
    
        @objc(didReceive:withContentHandler:)
        public func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
    
          // Remember to modify the siteId, apiKey and region with your own values.
          CustomerIO.initialize(siteId: "siteId", apiKey: "apiKey", region: .US) { config in
          }
          MessagingPush.shared.didReceive(request, withContentHandler: contentHandler)
        }
    
        @objc(serviceExtensionTimeWillExpire)
        public func serviceExtensionTimeWillExpire() {
            MessagingPush.shared.serviceExtensionTimeWillExpire()
        }
    }
  8. Open your NotificationServiceExtension.m file and copy the highlighted lines (beginning on line 2) into your file. The name of the header file on line 2 will depend on your extension’s name i.e. YourNotificationServiceExtensionName-Swift.h and is automatically created by Xcode.

    After this, you can run your app on a physical device and send yourself a push notification with images and deep links to test your implementation. You’ll have to use a physical device because simulators can’t receive push notifications.

    #import "NotificationService.h"
    #import "NotificationServiceExtension-Swift.h"
    
    @interface NotificationService ()
    
    @end
    
    @implementation NotificationService
    
    // Create object of class NotificationServicePushHandler
    NotificationServicePushHandler* nsHandlerObj = nil;
    
    // Initialize the object
    + (void)initialize{
      nsHandlerObj = [[NotificationServicePushHandler alloc] init];
    }
    
    - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
      [nsHandlerObj didReceive:request withContentHandler:contentHandler];
    }
    
    - (void)serviceExtensionTimeWillExpire {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
      [nsHandlerObj serviceExtensionTimeWillExpire];
    }
    
    @end

Sound in push notifications (iOS Only)

When you send a push notification to iOS devices that uses our SDK, you can opt to send the Default system sound or no sound at all. If your audience’s phone is set to vibrate, or they’ve disabled sound permissions for your app, the Default setting will cause the device to vibrate rather than playing a sound.

In most cases, you should use the Default sound setting to make sure your audience hears (or feels) your message. But, before you send sound, you should understand:

  1. Your app needs permission from your users to play sounds. This is done by your app, not our SDKs. Here’s an example from our iOS sample app showing how to request sound permissions.
  2. iOS users can go into System Settings and disable sound permissions for your app. Enabling the Default setting doesn’t guarantee that your audience hears a sound when your message is delivered!

 We don’t support custom sounds yet

If you want to send a custom sound, you’ll need to handle it on your own, outside the SDK and use a custom payload when you set up your push notifications.

Push icon (Android)

You’ll set the icon that appears on normal push notifications as a part of your app manifest—android/app/src/main/AndroidManifest.xml. If your icon appears in the wrong size, or if you want to change the standard icon that appears with your push notifications, you’ll need to update your app’s manifest.

<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_notification" />
<meta-data
    android:name="com.google.firebase.messaging.default_notification_color"
    android:resource="@color/colorNotificationIcon" />

Prompt users to opt-into push notifications

Your audience has to opt into push notifications. To display the native iOS and Android push notification permission prompt, you’ll use the CustomerIO.showPromptForPushNotifications method.

You can configure push notifications to request authorization for sounds and badges as well (only on iOS). If a user opts into push notifications, the CustomerIO.showPromptForPushNotifications method will return Granted, otherwise it returns Denied as a string. If the user has not yet been asked to opt into notifications, the method will return NotDetermined (only for iOS).

var options = {"ios" : {"sound" : true, "badge" : true}}
CustomerIO.showPromptForPushNotifications(options).then(status => {
  switch(status) {
    case "Granted":
     // Push permission is granted, your app can now receive push notifications
      break;
    case "Denied":
      // App is not authorized to receive push notifications
      // You might need to explain users why your app needs permission to receive push notifications
      break;
    case "NotDetermined":
      // Push permission status is not determined (Only for iOS)
      break;
  }
}).catch(error => {
  // Failed to show push permission prompt
  console.log(error)
})

Get a user’s permission status

To get a user’s current permission status, call the CustomerIO.getPushPermissionStatus() method. This returns a promise with the current status as a string.

CustomerIO.getPushPermissionStatus().then(status => {
  console.log("Push permission status is - " + status)
})

Optional: Remove POST_NOTIFICATIONS permission from Android apps

By default, the SDK includes the POST_NOTIFICATIONS permission which is required by Android 13 to show notifications on Android device. However, if you do not want to include the permission because don’t use notifications, or for any other reason, you can remove the permission by adding the following line to your android/app/src/main/AndroidManifest.xml file:

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" tools:node="remove"/>

Fetch currently stored device token

In customerio-reactnative versions 3.3.0 and later, you can fetch the currently stored device token using the CustomerIO.pushMessaging().getRegisteredDeviceToken() method. This method returns an APN/FCM token in a promise as a string.

   let token = await CustomerIO.pushMessaging().getRegisteredDeviceToken()
   if (token) {
    // Use the token as required in your app for example save in a state
        setDeviceToken(token);
   }

Test your implementation

After you set up rich push, you should test your implementation. Below, we show the payload structure we use for iOS and Android. In general, you can use our regular rich push editor; it’s set up to send messages using the JSON structure we outline below.

If you want to fashion your own payload, you can use our custom payload.

the rich push editor
the rich push editor
{
    "aps": {
        // basic iOS message and options go here
        "mutable-content": 1,
        "alert": {
            "title": "string", //(optional) The title of the notification.
            "body": "string" //(optional) The message you want to send.
        }
    },
    "CIO": {
        "push": {
            "link": "string", //generally a deep link, i.e. my-app:://...
            "image": "string" //HTTPS URL of your image, including file extension
        }
    }
}
      • image string
        The URL of an HTTPS image that you want to use for your message.
      • link string
        A deep link (to a page in your app), or a link to a web page.
    • alert
      string
      A simple alert message.
    • badge integer
      The number you want to display on your app’s icon. Set to 0 to remove the current badge, if any.
    • category string
      The notification’s type. This string must correspond to the identifier of one of the UNNotificationCategory objects you register at launch time.
    • content-available integer
      The background notification flag. Use 1 without an alert to perform a silent update. 0 indicates a normal push notification.
    • interruption-level string
      Indicates the importance and delivery timing of a notification.

      Accepted values:passive,active,time-sensitive,critical

    • mutable-content integer
      If you use the Customer.io SDK, you must set this value to 1 to support images and “delivered” metrics from your push notifications. When the value is 1, your notification is passed to your notification service app extension before delivery. Use your extension to modify the notification’s content.
    • relevance-score number
      A number between 0 and 1. The highest score is considered the “most relevant” and is featured in the notification summary.
    • sound
      string
      The name of a sound file in your app’s main bundle or in the Library/Sounds folder of your app’s container directory. Use “default” to play the system sound. For critical alerts, you’ll pass an object instead.
    • target-content-id string
      The identifier of the window brought forward.
    • thread-id string
      An identifier to group related notifications.
{
  "message": {
    "apns": {
      "payload": {
        "aps": {
          // basic iOS message and options go here
          "mutable-content": 1,
          "alert": {
            "title": "string", //(optional) The title of the notification.
            "body": "string" //(optional) The message you want to send.
           }
        },
        "CIO": {
          "push": {
            "link": "string", //generally a deep link, i.e. my-app://... or https://yourwebsite.com/...
            "image": "string" //HTTPS URL of your image, including file extension
          }
        }
      },
      "headers": {
        // (optional) headers to send to the Apple Push Notification Service.
        "apns-priority": 10
      }
    } 
  }
}
            • body string
              The body of your push notification.
            • image string
              The URL of an HTTPS image that you want to use for your message.
            • link string
              A deep link (to a page in your app), or a link to a web page.
            • title string
              The title of your push notification.
          • alert
            string
            A simple alert message.
          • badge integer
            The number you want to display on your app’s icon. Set to 0 to remove the current badge, if any.
          • category string
            The notification’s type. This string must correspond to the identifier of one of the UNNotificationCategory objects you register at launch time.
          • content-available integer
            The background notification flag. Use 1 without an alert to perform a silent update. 0 indicates a normal push notification.
          • interruption-level string
            Indicates the importance and delivery timing of a notification.

            Accepted values:passive,active,time-sensitive,critical

          • mutable-content integer
            If you use the Customer.io SDK, you must set this value to 1 to support images and “delivered” metrics from your push notifications. When the value is 1, your notification is passed to your notification service app extension before delivery. Use your extension to modify the notification’s content.
          • relevance-score number
            A number between 0 and 1. The highest score is considered the “most relevant” and is featured in the notification summary.
          • sound
            string
            The name of a sound file in your app’s main bundle or in the Library/Sounds folder of your app’s container directory. Use “default” to play the system sound. For critical alerts, you’ll pass an object instead.
          • target-content-id string
            The identifier of the window brought forward.
          • thread-id string
            An identifier to group related notifications.
        • Custom key-value pairs* any type
          Additional properties that you've set up your app to interpret outside of the Customer.io SDK.
{
  "message": {
    "data": {
      "title": "string", //(optional) The title of the notification.
      "body": "string", //The message you want to send.
      "image": "string", //https URL to an image you want to include in the notification
      "link": "string" //Deep link in the format remote-habits://deep?message=hello&message2=world
    }
  }
}
  • message
    Required The parent object for all push payloads.
        • body_loc_arg string
          Variable string values used in place of the format specifiers in body_loc_key to localize the body text to the user’s current localization. See Formatting and Styling for more information.
        • body_loc_key string
          The key to the body string in the app’s string resources that you want to use to localize the body text to the user’s current localization. See String Resources for more information.
        • click_action string
          The action that occurs when a user taps on the notification. Launches an activity with a matching intent filter when a person taps the notification.
        • color string
          The notification’s icon color in #rrggbb format.
        • icon string
          Sets the notification icon to myicon for drawable resource myicon. If you don’t send this key, FCM displays the launcher icon from your app manifest.
        • sound string
          The sound that plays when the device receives the notification. Supports "default" or the filename of a sound resource bundled in your app. Sound files must reside in /res/raw/.
        • tag string

          Identifier to replace existing notifications in the notification drawer. If empty, each request creates a new notification.

          If you specify a tag, and a notification with the same tag is already being shown, the new notification replaces the existing one in the notification drawer.

        • title_loc_arg string
          Variable string values used in place of the format specifiers in title_loc_key to localize the title text to the user’s current localization. See Formatting and Styling for more information.
        • title_loc_key string
          The key to the title string in the app’s string resources that you want to use to localize the title text to the user’s current localization. See String Resources for more information.
      • body string
        The body of your push notification.
      • image string
        The URL of an HTTPS image that you want to use for your message.
      • link string
        A deep link (to a page in your app), or a link to a web page.
      • title string
        The title of your push notification.

Copied to clipboard!
  Contents
Current release
 3.9.0
Is this page helpful?