Search code examples
firebasereact-nativefirebase-cloud-messagingreact-native-iosreact-native-firebase

setBackgroundMessageHandler: How to invoke when app is in quit state on iOS


I am using the react-native-firebase library.

On iOS, I am able to receive notifications in the foreground, background and also when the app is in quit state. Handlers work in every scenario too, except for one.

My only problem is: setBackgroundMessageHandler is not being invoked when the app is in quit state, even though I am receiving the notification. I also tried to send data-only messages but the result was the same.

I am even able to invoke the app in a headless state using content-available: 1 in the message's apns section.

But still the setBackgroundMessageHandler is not getting invoked. This is my index.js page:

messaging().setBackgroundMessageHandler(async remoteMessage => {
  console.log('HEADLESS BACKGROUND: ' + ' | ' + JSON.stringify(remoteMessage));
});

function HeadlessCheck({ isHeadless }) {
  if (isHeadless) {
    console.log("Headless");
    // App has been launched in the background by iOS, ignore
    return null;
  }

  return <App />;
}

AppRegistry.registerComponent(appName, () => HeadlessCheck);

Solution

  • Instead of returning null from in case of a headless invocation, return a simple component that contains nothing and renders nothing.

    Index.js

    messaging().setBackgroundMessageHandler(async (remoteMessage) => {
      console.log('HEADLESS BACKGROUND: ' + JSON.stringify(remoteMessage));
    });
    
    function HeadlessCheck({ isHeadless }) {
      if (isHeadless) {
        console.log('Headless');
        return <AppFake />;
        /* Notice this component, it is not the App Component but a different one*/
      }
    
      return <App />;
    }
    
    AppRegistry.registerComponent(appName, () => HeadlessCheck);
    

    The AppFake component simply contains this:

    import { useEffect } from 'react';
    import SplashScreen from 'react-native-splash-screen';
    
    const AppFake = () => {
      useEffect(() => {
        SplashScreen.hide();
      }, []);
    
      return null;
    };
    
    export default AppFake;
    

    I did have to hide the SplashScreen. This was essential in my case.

    Here is the payload that I send from the server:

    var admin = require('firebase-admin');
    
    const message = {
      notification: {
        title: title,
        body: body,
      },
      data: { campaignId: campaignId, title: title, body: body },
      token: registrationId,
      android: {
        priority: 'high',
        notification: {
          title: title,
          body: body,
          sound: 'default',
        },
      },
      apns: {
        payload: {
          aps: {
            sound: 'default',
            'content-available': 1,
          },
        },
        headers: {
          'apns-priority': '5',
        },
      },
    };
    
    let response = admin
      .messaging()
      .send(message)
      .then((res) => {
        console.log(res);
      })
      .catch((err) => {
        console.log(err);
      });
    

    Now the setBackgroundMessageHandler is going to be invoked on every new message even when the app is in the quit state on iOS.