Push Notifications on React Native

Push notifications and how to set them up are simply not clear to me at all. I’ve been wrestling with your documentation, which links to react-native-firebase’s documentation, which links to Firebase, which links to Apple and I’ve just been spinning in circles for several hours now.

Here’s my goal: My app, built in React Native, currently on iOS only should receive a push notification whenever a message is received via Sendbird.

What I have done:

No matter what I do, firebase.messaging().ios is undefined and I have no idea why. I’m running on a device and from what I understand so far, this should work. Perhaps this has nothing to do with Sendbird, but I’m not really sure why I can’t actually run your sample code to get an APNS token from Firebase. It looks like that is the only way to grab an APNS token and register that into Sendbird. The Push Notification FAQ post does not answer this either.

How do I get around this? What am I doing wrong?

@Sameer_Madan,

Can you provide us some code around where you’re calling firebase.messaging().ios? I don’t see anything in the documentation where a method like this would be called.

Thanks,
Tyler

Here is your documentation from where I’m copying this code: https://sendbird.com/docs/chat/v3/javascript/guides/push-notifications

The same code is also used inside your sample app: SendBird-JavaScript/user.js at c8f7b2372524f6a0b7a244c5b8666f2e465d44b9 · sendbird/SendBird-JavaScript · GitHub

I noticed the sample app has some extra code around notifications inside AppDelegate.m, but that’s not really working for me either.

Ah, looks like your documentation says messaging().getAPNSToken(), but the sample code says messaging().ios.getAPNSToken(). The first one at least gets me a token. The second one does not seem right to me. I’m also pretty sure your documentation said messaging().ios.getAPNSToken() until yesterday.

Still not actually receiving any push notifications. Here’s the relevant code so far:

const sendbirdRegisterPushToken = () => {
  return new Promise((resolve, reject) => {
    let sb = SendBird.getInstance();
    if (!sb) {
      sb = new SendBird({appId: APP_ID});
    }
    if (Platform.OS === 'ios') {
      messaging()
        .getAPNSToken()
        .then((token) => {
          console.log(token);
          if (token) {
            sb.registerAPNSPushTokenForCurrentUser(token, (result, error) => {
              if (!error) {
                resolve();
              } else reject(error);
            });
          } else {
            resolve();
          }
        })
        .catch((error) => {
          reject(error);
        });
    } else { .. }
  });
};

  // Register push token to receive push notifications
  useEffect(() => {
    sendbirdRegisterPushToken()
      .then((result) => {
        console.log('Registered');
      })
      .catch((error) => {
        console.log(error);
      });
  }, []);

  // Handle foreground push notifications -- never gets called
  useEffect(() => {
    const unsubscribe = messaging().onMessage(async (message) => {
      console.log('Here');
      const text = message.data.message;
      const payload = JSON.parse(message.data.sendbird);
      console.log(payload);
    });

    return unsubscribe;
  }, []);

The onMessage() handler does not get called. This should be called when the app is in the foreground.

It looks like the documentation was updated a couple of days ago to match some changes made to Firebase.

Can you DM me your APP_ID please?

The rest of this discussion took place over DMs, but I’ll attempt to summarize here for anyone whom may have this issue in the future.

There were a few notable configuration items to validate during this discussion. Its important that you check the following:

  • Both APNs and FCM server tokens are registered in the Sendbird Dashboard.
  • Ensure user devices are registering device tokens. Can be confirmed by going to the specific user in the Sendbird Dashboard.

An additional piece to this puzzle was that the user was attempting to send push notifications when an agent responded to a desk ticket. While you do utilize the Core SDK to view the desk ticket and its messages, you will still need to authenticate via the Desk SDK in your application to allow push notifications to be received.

Lastly, as React-Native does not automatically set background or foreground state, you’ll need to ensure that you’re calling the sb.setBackgroundState() and sb.setForegroundState() methods manually. By default, push notifications will be sent only when all devices are offline. This can be changed in the Dashboard to send regardless of online status.

1 Like

Can you tell us what these two functions actually do? I haven’t been able to find out why it’s necessary to call them. Just that I should.

Hi @anirudh,

There are a number of aspects that these two functions impact. The biggest are websocket connections and push notifications. If you do not call these, websocket connections could be left open and would potentially cause you to incur more Peak Connections than are actually being utilized by your users.

Additionally, if the background state is never set, the user would not be considered offline and thus never receive push notifications.

In the native SDKs this is handled automatically but is not within React Native.

I’m not sure when the SendbirdUIKitContainer was released, but it looks like its SendbirdChatProvider now handles the setBackgroundState() and setForegroundState() calls automatically (see below).

When I observe the events as described in the documentation, I get the error TypeError: Cannot read property '_iid' of undefined, js engine: hermes, which I assume is due to calling setBackgroundState()/disconnect() twice. So am I correct that if we use SendbirdUIKitContainer we no longer need to manually observe these app state change events?

  useAppState('change', (status) => {
    // 'active' | 'background' | 'inactive' | 'unknown' | 'extension';
    if (status === 'active') sdkInstance.connectionState === 'CLOSED' && sdkInstance.setForegroundState();
    else if (status === 'background') sdkInstance.connectionState === 'OPEN' && sdkInstance.setBackgroundState();
  });