React Native

If you are on version 1.2 or below, you would need to upgrade to 1.3 or above to follow the setup. As 1.3 release had breaking changes with respect to setting up of push notifications. We recommend to update to the current latest version.

Set up push notifications for non-ringing events (missed calls, livestream started, etc.) in your React Native app.

Install Dependencies

yarn add @react-native-firebase/app \
  @react-native-firebase/messaging \
  @notifee/react-native \
  @react-native-community/push-notification-ios

npx pod-install

Package purposes:

  • @react-native-firebase/app, @react-native-firebase/messaging - Handle Firebase Cloud Messaging on Android
  • @notifee/react-native - Customize and display push notifications
  • @react-native-community/push-notification-ios - Handle APNs notifications on iOS

Android-specific setup

  1. Create a Firebase project at Firebase console
  2. Add your Android app in Project settings > Your apps. Use the same package name as android.package in app.json
  3. Download google-services.json to /android/app/google-services.json
  4. Enable the google-services plugin:
buildscript {
  dependencies {
    // ... other dependencies
    classpath 'com.google.gms:google-services:4.3.15'
  }
}
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'

The google-services.json file contains unique and non-secret identifiers of your Firebase project. For more information, see Understand Firebase Projects.

iOS-specific setup

Disable Firebase installation

Firebase Cloud Messaging is not used for iOS. Disable auto-linking unless Firebase is needed for other purposes. Create react-native.config.js:

module.exports = {
  dependencies: {
    "@react-native-firebase/app": {
      platforms: {
        ios: null,
      },
    },
    "@react-native-firebase/messaging": {
      platforms: {
        ios: null,
      },
    },
  },
};

Then run pod install to remove the installed pods.

Add background modes

In Xcode, open Info.plist and add UIBackgroundModes:

<key>UIBackgroundModes</key>
<array>
  <string>remote-notification</string>
</array>

Enable push notifications capability

Enable Push Notifications in Xcode: Project > Signing & Capabilities.

Update AppDelegate.h

Add imports:

#import <UserNotifications/UNUserNotificationCenter.h>

Add UNUserNotificationCenterDelegate. For React-Native v0.71+:

@interface AppDelegate : RCTAppDelegate <UNUserNotificationCenterDelegate>

For React-Native v0.70 and earlier:

@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCen

Update AppDelegate.m or AppDelegate.mm

Add headers

#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>

Add methods

Add notification handling methods:

// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
 [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
 [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
  [RNCPushNotificationIOS didReceiveNotificationResponse:response];
}
//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}

Add to didFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;
  // ...rest
}

Enable push notifications

Enable Push Notifications in Xcode: Project > Signing & Capabilities.

Add Push message handlers

Add SDK utility functions to notification listeners.

Add callbacks to process notifications

Process Firebase messages and display via @notifee/react-native:

import messaging from "@react-native-firebase/messaging";
import {
  isFirebaseStreamVideoMessage,
  firebaseDataHandler,
} from "@stream-io/video-react-native-sdk";

export const setFirebaseListeners = () => {
  // Set up the background message handler for Android
  messaging().setBackgroundMessageHandler(async (msg) => {
    if (isFirebaseStreamVideoMessage(msg)) {
      await firebaseDataHandler(msg.data);
    } else {
      // your other messages (if any)
    }
  });
  // Set up the foreground message handler for Android
  messaging().onMessage((msg) => {
    if (isFirebaseStreamVideoMessage(msg)) {
      firebaseDataHandler(msg.data);
    } else {
      // your other messages (if any)
    }
  });
};

Firebase message handlers:

  • isFirebaseStreamVideoMessage - Checks if push message is video-related
  • firebaseDataHandler - Processes message and displays notification via @notifee/react-native

If you had disabled the installation of Firebase on iOS, add the above method only for Android using the Platform-specific extensions for React Native.

For example, say you add the following files in your project:

setFirebaseListeners.android.ts
setFirebaseListeners.ts

The method above must only be added to the file that .android extension. The other file must add the method but do nothing like below:

export const setFirebaseListeners = () => {
  // do nothing
};

This is to ensure that @react-native-firebase/messaging is only imported on the Android platform.

Add notification onPress listeners

Android notification press handlers using @notifee/react-native:

import {
  isNotifeeStreamVideoEvent,
  onAndroidNotifeeEvent,
} from "@stream-io/video-react-native-sdk";
import { Platform } from "react-native";
import notifee from "@notifee/react-native";

export const setNotifeeListeners = () => {
  // on press handlers of background notifications for Android
  notifee.onBackgroundEvent(async (event) => {
    if (isNotifeeStreamVideoEvent(event)) {
      await onAndroidNotifeeEvent({ event, isBackground: true });
    } else {
      // your other notifications (if any)
    }
  });
  // on press handlers of foreground notifications for Android
  notifee.onForegroundEvent((event) => {
    if (isNotifeeStreamVideoEvent(event)) {
      onAndroidNotifeeEvent({ event, isBackground: false });
    } else {
      // your other notifications (if any)
    }
  });
};

Notifee event handlers:

  • isNotifeeStreamVideoEvent - Checks if event is video-related
  • onAndroidNotifeeEvent - Processes accept/decline actions

iOS handler

Add notification listener in your root component using @react-native-community/push-notification-ios:

import PushNotificationIOS from "@react-native-community/push-notification-ios";
import {
  isPushNotificationiOSStreamVideoEvent,
  onPushNotificationiOSStreamVideoEvent,
} from "@stream-io/video-react-native-sdk";

useEffect(() => {
  PushNotificationIOS.addEventListener("notification", (notification) => {
    if (isPushNotificationiOSStreamVideoEvent(notification)) {
      onPushNotificationiOSStreamVideoEvent(notification);
    } else {
      // any other APN notifications
    }
  });
  return () => {
    PushNotificationIOS.removeEventListener("notification");
  };
}, []);

Setup the push config for the SDK

Configure push via StreamVideoRN.setPushConfig.

Add static navigation

Enable navigation when tapped from push notification before JS is ready. See navigating without navigation prop:

import { createNavigationContainerRef } from "@react-navigation/native";

import { RootStackParamList } from "../navigation/types";

export const navigationRef = createNavigationContainerRef<RootStackParamList>();

/**
 * This is used to run the navigation logic from root level even before the navigation is ready
 */
export const staticNavigate = (
  ...navigationArgs: Parameters<typeof navigationRef.navigate>
) => {
  // note the use of setInterval, it is responsible for constantly checking if requirements are met and then navigating
  const intervalId = setInterval(async () => {
    // run only when the navigation is ready and add any other requirements (like authentication)
    if (navigationRef.isReady() && GlobalState.hasAuthentication) {
      clearInterval(intervalId);
      navigationRef.navigate(...navigationArgs);
    }
  }, 300);
};

Set navigationRef in your navigation container:

import { navigationRef } from './src/utils/staticNavigationUtils';

<NavigationContainer ref={navigationRef}>
  <MyAppNavigator />
</NavigationContainer>;

Setup the push config

Configure push settings. Override notification texts, set push provider names, and customize navigation:

import {
  StreamVideoClient,
  StreamVideoRN,
} from "@stream-io/video-react-native-sdk";
import { AndroidImportance } from "@notifee/react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { STREAM_API_KEY } from "../../constants";
import { staticNavigate } from "./staticNavigationUtils";

export function setPushConfig() {
  StreamVideoRN.setPushConfig({
    ios: {
      // add your push_provider_name for iOS that you have setup in Stream dashboard
      pushProviderName: __DEV__ ? "apn-video-staging" : "apn-video-production",
    },
    android: {
      // the name of android notification icon (Optional, defaults to 'ic_launcher')
      smallIcon: "ic_notification",
      // add your push_provider_name for Android that you have setup in Stream dashboard
      pushProviderName: __DEV__
        ? "firebase-video-staging"
        : "firebase-video-production",
      // configure the notification channel to be used for non ringing calls for Android.
      callChannel: {
        id: "stream_call_notifications",
        name: "Call notifications",
        // This importance will ensure that the notification will appear on-top-of applications.
        importance: AndroidImportance.HIGH,
        sound: "default",
      },
      // configure the functions to create the texts shown in the notification
      // for non ringing calls in Android.
      callNotificationTextGetters: {
        getTitle(type, createdUserName) {
          if (type === "call.live_started") {
            return `Call went live, it was started by ${createdUserName}`;
          } else {
            return `${createdUserName} is notifying you about a call`;
          }
        },
        getBody(_type, createdUserName) {
          return "Tap to open the call";
        },
      },
    },
    // optional: add the callback to be executed when a non ringing call notification is tapped
    onTapNonRingingCallNotification: () => {
      const [callType, callId] = call_cid.split(":");
      if (callType === "livestream") {
        staticNavigate({ name: "LiveStreamCallScreen", params: undefined });
      } else {
        staticNavigate({ name: "ActiveCallScreen", params: undefined });
      }
    },
    // add the async callback to create a video client
    // for incoming calls in the background on a push notification
    createStreamVideoClient: async () => {
      // note that since the method is async,
      // you can call your server to get the user data or token or retrieve from offline storage.
      const userId = await AsyncStorage.getItem("@userId");
      const userName = await AsyncStorage.getItem("@userName");
      if (!userId) return undefined;
      // an example promise to fetch token from your server
      const tokenProvider = () =>
        yourServer.getTokenForUser(userId).then((auth) => auth.token);
      const user = { id: userId, name: userName };
      return StreamVideoClient.getOrCreateInstance({
        apiKey: STREAM_API_KEY, // pass your stream api key
        user,
        tokenProvider,
      });
    },
  });
}

Always use StreamVideoClient.getOrCreateInstance(..) instead of new StreamVideoClient(..). Reusing the client instance preserves call accept/decline states changed while the app was in the background. The getOrCreateInstance method ensures the same user reuses the existing instance.

Set android.smallIcon for best results. Use Android Asset Studio to generate icons with correct settings.

Call the created methods outside of the application lifecycle

Call configuration methods alongside AppRegistry.registerComponent(). This ensures configuration is available when the app opens from a push notification:

import { AppRegistry } from "react-native";
import { setPushConfig } from "src/utils/setPushConfig";
import { setNotifeeListeners } from "src/utils/setNotifeeListeners";
import { setFirebaseListeners } from "src/utils/setFirebaseListeners";
import App from "./App";

setPushConfig();
setNotifeeListeners();
setFirebaseListeners();
AppRegistry.registerComponent("app", () => App);

Request for notification permissions

Request permissions using react-native-permissions:

import { requestNotifications } from "react-native-permissions";

// This will request POST_NOTIFICATION runtime permission for Anroid 13+
await requestNotifications(["alert", "sound"]);

Disabling push - usually on logout

Disable push on user logout or user switch:

import { StreamVideoRN } from "@stream-io/video-react-native-sdk";

await StreamVideoRN.onPushLogout();

Troubleshooting

See the Troubleshooting guide for common issues.