Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[🐛] setBackgroundMessageHandler does not trigger on IOS #7988

Closed
2 of 7 tasks
exoriri opened this issue Aug 25, 2024 · 20 comments
Closed
2 of 7 tasks

[🐛] setBackgroundMessageHandler does not trigger on IOS #7988

exoriri opened this issue Aug 25, 2024 · 20 comments
Labels
platform: ios plugin: messaging FCM only - ( messaging() ) - do not use for Notifications resolution: solution-provided type: bug New bug report

Comments

@exoriri
Copy link

exoriri commented Aug 25, 2024

Issue

I use firebase and Notifee. I try to send data to my server, but setBackgroundMessageHandler does not work on iOS at all. On android this method works fine.

I setup remote-notification, fetch options in Xcode. content-available is setted to true. Doesn't help.


Project Files

Javascript

Click To Expand

App.tsx:

import { Stack } from "expo-router";
import { useEffect } from "react";
import "react-native-reanimated";
import { usePushNotifications } from "./usePushNotifications";

import { getFCMToken } from "./messaging";
import "./messaging";

export default function RootLayout() {
  usePushNotifications();

  useEffect(() => {
    getFCMToken();
  }, []);

  return (
      <Stack>
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
        <Stack.Screen name="+not-found" />
      </Stack>
  );
}

messaging.ts:

import notifee, {
  AndroidImportance,
  Notification,
} from "@notifee/react-native";

import messaging, {
  FirebaseMessagingTypes,
} from "@react-native-firebase/messaging";
import { Platform } from "react-native";
import { sendToServer } from "./getFromServer";

export const remoteMessageHandler = async (
  remoteMessage: FirebaseMessagingTypes.RemoteMessage
) => {
  await notifee.displayNotification({
    title: remoteMessage.notification.title,
    body: remoteMessage.notification.body,
    data: remoteMessage.data,
  } as Notification);
};

messaging().setBackgroundMessageHandler(async ({ messageId }) => {
  try {
    if (messageId) {
      await sendToServer();
    }
  } catch (error) {
    console.error("setBackgroundMessageHandler", error);
  }
});

const requestMessagingPermissions = async () => {
  await notifee.requestPermission();
};

export const getFCMToken = async (options?: {
  shouldRequestPermission?: boolean;
}) => {
  try {
    if (options?.shouldRequestPermission) {
      await requestMessagingPermissions();
    }
    const hasFCMPermission = await hasMessagingPermission();

    if (!hasFCMPermission) {
      return;
    }

    await createAndroidMessagingChannel();
    const fcmToken = await messaging().getToken();
    return fcmToken;
  } catch (error) {
    console.error("getFCMToken", error);
  }
};

package.json:

    "@react-native-firebase/analytics": "^20.1.0",
    "@react-native-firebase/app": "^20.1.0",
    "@react-native-firebase/crashlytics": "^20.1.0",
    "@react-native-firebase/messaging": "^20.1.0",

firebase.json for react-native-firebase v6:

{
  "$schema": "./node_modules/@react-native-firebase/app/firebase-schema.json",
  "react-native": {
    "crashlytics_disable_auto_disabler": true,
    "crashlytics_auto_collection_enabled": false,
    "crashlytics_is_error_generation_on_js_crash_enabled": true,
    "crashlytics_javascript_exception_handler_chaining_enabled": false,
    "messaging_android_notification_channel_id": "high-priority"
  }
}

iOS

Click To Expand

ios/Podfile:

  • I'm not using Pods
  • I'm using Pods and my Podfile looks like:


Environment

Click To Expand

react-native info output:

 System:
  OS: macOS 14.3.1
  CPU: (8) arm64 Apple M1 Pro
  Memory: 90.77 MB / 16.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 21.7.1
    path: /opt/homebrew/bin/node
  Yarn:
    version: 1.22.21
    path: /usr/local/bin/yarn
  npm:
    version: 10.5.0
    path: /opt/homebrew/bin/npm
  Watchman:
    version: 2024.04.22.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.15.2
    path: /opt/homebrew/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.0
      - iOS 17.0
      - macOS 14.0
      - tvOS 17.0
      - watchOS 10.0
  Android SDK: Not Found
IDEs:
  Android Studio: 2022.2 AI-222.4459.24.2221.10121639
  Xcode:
    version: 15.0.1/15A507
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 17.0.11
    path: /usr/bin/javac
  Ruby:
    version: 2.6.10
    path: /usr/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.74.3
    wanted: 0.74.3
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: Not found
  newArchEnabled: Not found
iOS:
  hermesEnabled: true
  newArchEnabled: false
  • Platform that you're experiencing the issue on:
    • iOS
    • Android
    • iOS but have not tested behavior on Android
    • Android but have not tested behavior on iOS
    • Both
  • react-native-firebase version you're using that has this issue:
    • ^20.4.0
  • Firebase module(s) you're using that has the issue:
    • @react-native-firebase/messaging
  • Are you using TypeScript?
    • Yes & ~5.3.3

@exoriri exoriri changed the title [🐛] setBackgroundMessageHandler does not trigger callback on IOS [🐛] setBackgroundMessageHandler does not trigger on IOS Aug 26, 2024
@russellwheatley
Copy link
Member

Sorry, I was testing this last week and it worked. I'd advise stripping the app down to bare minimum for testing (i.e. strip out all but core + messaging dependencies), and firing messages via firebase admin. Example script:

var admin = require('firebase-admin');
// 1. Download a service account key (JSON file) from your Firebase console and add to the example/scripts directory
// var serviceAccount = require('../ff-e2e-service.json');
var serviceAccount = require('../rnfb-service-account.json');
admin.initializeApp({
  credential: admin.credential.cert(serviceAccount),
});

// 2. Copy the token for your device that is printed in the console on app start (`flutter run`) for the FirebaseMessaging example
const androidToken = '';
const iosToken = '';

// 3. From your terminal, root to example/scripts directory & run `npm install`.
// 4. Run `npm run send-message` in the example/scripts directory and your app will receive messages in any state; foreground, background, terminated.
// If you find your messages have stopped arriving, it is extremely likely they are being throttled by the platform. iOS in particular
// are aggressive with their throttling policy.
admin
  .messaging()
  .send(
    {
      token: iosToken,
      data: {
        foo: 'bars',
        float: '12034'
      },
      notification: {
        title: 'A great title',
        body: 'Great content',
      },
      android: {
        // Required for background/terminated app state messages on Android
        priority: 'high',
      },
      apns: {
        payload: {
          aps: {
            // Required for background/terminated app state messages on iOS
            contentAvailable: true,
          },
        },
      },
    },
  )
  .then((res) => {
    if (res.failureCount) {
      console.log('Failed', res.results[0].error);
    } else {
      console.log('Success');
    }
  })
  .catch((err) => {
    console.log('Error:', err);
  });

This should help you at least confirm it works, before slowly adding further dependencies + logic to see what the issue is.

@russellwheatley russellwheatley added plugin: messaging FCM only - ( messaging() ) - do not use for Notifications resolution: solution-provided platform: ios and removed Needs Attention labels Aug 27, 2024
@Ga1atex
Copy link

Ga1atex commented Sep 3, 2024

@russellwheatley Hello, I've tried both expo(sdk 51) & rn cli(0.75.2) empty projects,

only packages
"@react-native-firebase/app": "^20.4.0",
"@react-native-firebase/messaging": "^20.4.0",
were added

sent pushes by your example, I receive notification alert when app is backgrounded, but setBackgroundMessageHandler
doesnt trigger

reproduction (no GoogleService-Info.plist in the repo)
https://github.com/Ga1atex/firebase-push-notifications-repro

maybe there is something In the Firebase console/google cloud console/apple store connect I need to change to make it work?

android works fine

@Ga1atex
Copy link

Ga1atex commented Sep 4, 2024

Tested it on a real device ios 15.8.1 and it works now

I guess it doesn't work on new versions > 17ios like mentioned here
#7548

@Ga1atex
Copy link

Ga1atex commented Sep 4, 2024

@russellwheatley tested it on a real device 17.5.1

did a build via Xcode, at the first launch of the app, when I put it in background state, setBackgroundMessageHandler does work
but when I quit from the app completely it stops, and it doesnt work even after restarting the app

@ezequielobreque
Copy link

I have an app in production and my clients report the same problem, the app works fine in the foreground but when it goes to quit state it stops working, sometimes it works and sometimes it doesn't.

There is a way to easily reproduce this error by simply restarting the iPhone. With this, you will no longer receive notifications in the background until you reinstall the app.

@Ga1atex
Copy link

Ga1atex commented Sep 5, 2024

current workaround is to add native NotificationServiceExtension (need to have mutableContent field for backend to make it work)
payload: { aps: { contentAvailable: true, mutableContent: true }, },

@otech47
Copy link

otech47 commented Oct 3, 2024

does anyone know why when I send a test message on FCM, the notification DOES appear but my onBackgroundMessageHandler does NOT fire??

i'm just trying to test this log statement firing from a FCM Test message before figuring out why my server's notifications aren't firing

messaging().setBackgroundMessageHandler(async m => {
    console.info(`setBackgroundMessageHandler fired ${JSON.stringify(m)}`)
    return Promise.resolve()
})

was trying to follow the specific instructions here: #7548 (comment)

@dbs-tuan
Copy link

dbs-tuan commented Nov 5, 2024

@russellwheatley tested it on a real device 17.5.1

did a build via Xcode, at the first launch of the app, when I put it in background state, setBackgroundMessageHandler does work but when I quit from the app completely it stops, and it doesnt work even after restarting the app

current workaround is to add native NotificationServiceExtension (need to have mutableContent field for backend to make it work)
payload: { aps: { contentAvailable: true, mutableContent: true }, },

I have an app in production and my clients report the same problem, the app works fine in the foreground but when it goes to quit state it stops working, sometimes it works and sometimes it doesn't.

There is a way to easily reproduce this error by simply restarting the iPhone. With this, you will no longer receive notifications in the background until you reinstall the app.

I'm still facing the same issue

"@react-native-firebase/app": "^20.4.0",
"@react-native-firebase/messaging": "^20.4.0"

my payload in postman:

{
    "message": {
        "token": ...,
        "notification":{
            "title": "Title background notification",
            "body": "Body background Notification"
        },
        "data": {
            "message": "dsadsadsads"
        },
        "apns": {
            "payload": {
                "aps": {
                    "content-available": 1,
                    "mutable-content": 1
                }
            }
        }
    }
}

@mikehardy
Copy link
Collaborator

If you include a notification block in your FCM, the firebase-ios-sdk will post the notification directly to the user / notification center with no handlers called in your app ever
If the user interacts with the notification, then the app will be given focus and the message information will be delivered to the app with a handler called

@dbs-tuan
Copy link

dbs-tuan commented Nov 5, 2024

If you include a notification block in your FCM, the firebase-ios-sdk will post the notification directly to the user / notification center with no handlers called in your app ever

If I remove a notification block in FCM, setBackgroundMessageHandler is still not working in the background/quit state

tested it on a real device 17.4.1

did a build via Xcode, at the first launch of the app, when I put it in the background state, setBackgroundMessageHandler work
but when I quit the app completely it stops, and it doesn't work even after restarting the app

More specifically, when I reinstall the app, it works in the first launch. But when I quit the app, sometimes it has to wait for about ~5 minutes to get a remote message from setBackgroundMessageHandler, and sometimes it is still not working

How do you think I could do something to make BackgroundMessageHandler work?

@mikehardy
Copy link
Collaborator

If you remove the notification block then you have delivery problems, yes. iOS - in my experience - is very picky about donating some of the user device's energy to your app to run it and call a handler in response to a data-only message. That is an iOS policy and there is not much you can do to impact it.

For that reason, on iOS a notification block is encouraged so you have a chance at your FCM being delivered, but you'll need the user to interact with the notification for it to be useful.

So the final answer is: you really must send FCM to the device that the user finds useful, the need to want to see that notification pop up and interact with it, because it's useful. If you don't, nothing you try to do will be high-percentage - you're fighting the iOS philosophy at that point and won't see any sort of reliable delivery.

@dbs-tuan
Copy link

dbs-tuan commented Nov 7, 2024

I've expanded my test to 4 devices:

  1. device 1 (ios 16.7.1)
  2. device 2 (ios 17.7)
  3. device 3 (ios 17.5.1)
  4. device 4 (ios 18.1)

More specifically, when I reinstall the app, it works in the first launch. But when I quit the app, sometimes it has to wait for about ~5 minutes to get a remote message from setBackgroundMessageHandler, and sometimes it is still not working

on devices 2 & 3 & 4, it was still encountered in this case ↑

on device 1, it worked seamlessly although I rebooted the device or reinstalled my app lots of times. I think the issue occurred specifically on the ios 17 >=

@mikehardy
Copy link
Collaborator

Which reads to me exactly like:

"iOS, as it progresses from version to version, works even harder to preserve your battery so it lasts all day! Unlike Android where you are wondering if you'll make it through the day we work hard at Apple to make sure that your battery is used as efficiently as possible so worrying about charging is a thing of the past! Upgrade to the new iOS (or buy a new device that can run it) today!"

Of course as a pesky application developer trying to implement features this is irritating because our code doesn't run

But users love it

@dbs-tuan
Copy link

dbs-tuan commented Nov 8, 2024

It means that this library cannot support my case. So, from iOS 17, do I have to use another library to get notifications on background/quit state?

@mikehardy
Copy link
Collaborator

@dbs-tuan you probably want to send a notification block, and use a NotificationExtensionHandler so you get a small time slice of power from the user device in order to do some handling before posting the notification to the user. Notifee.app can do that (it is a companion to react-native-firebase). But it is still limited, you do not get a lot of power / compute time to do work, just a tiny amount.

Your use case may in fact simply not be technically possible depending on what exactly you want to do. The rules on iOS are that if the users cannot see it, you most likely simply cannot do it unless the user is actively interacting with the app.

@dbs-tuan
Copy link

dbs-tuan commented Nov 8, 2024

@dbs-tuan you probably want to send a notification block, and use a NotificationExtensionHandler so you get a small time slice of power from the user device in order to do some handling before posting the notification to the user. Notifee.app can do that (it is a companion to react-native-firebase). But it is still limited, you do not get a lot of power / compute time to do work, just a tiny amount.

Your use case may in fact simply not be technically possible depending on what exactly you want to do. The rules on iOS are that if the users cannot see it, you most likely simply cannot do it unless the user is actively interacting with the app.

I'm using notifee only in case of increasing badge count. The notification template is generated from the API. My task is just simply to increase the badge count in the background/quit state. Additionally, my current test is local, I'm thinking of building my app to TestFlight and testing it again

Your use case may in fact simply not be technically possible depending on what exactly you want to do.

So sad to hear that

Thanks for your quick response. Maybe I'll try another way

@mikehardy
Copy link
Collaborator

you should be able to - with notifee - send a notification block in the FCM to iOS and have your notification extension handler call the notifee API to increase the badge count prior to posting the notification to the user

@dbs-tuan
Copy link

dbs-tuan commented Nov 8, 2024

where could I get docs about "NotificationExtensionHandler"
Is it this link or another? Sorry for that cause I'm a beginner in notifications on mobile platforms
https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension

@dbs-tuan
Copy link

current workaround is to add native NotificationServiceExtension (need to have mutableContent field for backend to make it work) payload: { aps: { contentAvailable: true, mutableContent: true }, },

@Ga1atex Could you give me any tutorials or relevant documents related to "add native NotificationServiceExtension"?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
platform: ios plugin: messaging FCM only - ( messaging() ) - do not use for Notifications resolution: solution-provided type: bug New bug report
Projects
None yet
Development

No branches or pull requests

7 participants