invertase / react-native-firebase

šŸ”„ A well-tested feature-rich modular Firebase implementation for React Native. Supports both iOS & Android platforms for all Firebase services.
https://rnfirebase.io
Other
11.53k stars 2.19k forks source link

SetBackgroundHandler not working on ios. #3530

Closed tranty9597 closed 3 years ago

tranty9597 commented 4 years ago

SetBackgroundHandler not working on ios.

I've setup rnfirebase/message v6 for my project, and up to now it's work perfectlty on android. But on IOS SetBackgroundHandler dot not trigger any event.

I've try to debug on xcode at RNFBMessaging+AppDelegate.m and RNFBMessaging+UNUserNotificationCenter.m, and when app in background, didReceiveRemoteNotification delegate dont be fired while my notification still be presented.

Do I have to add content-available to my message payload to make it works?


Project Files

Javascript

Click To Expand

#### `package.json`: ```json # N/A ``` #### `firebase.json` for react-native-firebase v6: ```json # N/A ```

iOS

Click To Expand

#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like: ```ruby # N/A ``` #### `AppDelegate.m`: ```objc // N/A ```


Android

Click To Expand

#### Have you converted to AndroidX? - [ ] my application is an AndroidX application? - [ ] I am using `android/gradle.settings` `jetifier=true` for Android compatibility? - [ ] I am using the NPM package `jetifier` for react-native compatibility? #### `android/build.gradle`: ```groovy // N/A ``` #### `android/app/build.gradle`: ```groovy // N/A ``` #### `android/settings.gradle`: ```groovy // N/A ``` #### `MainApplication.java`: ```java // N/A ``` #### `AndroidManifest.xml`: ```xml ```


Environment

Click To Expand

**`react-native info` output:** ``` OUTPUT GOES HERE ``` - **Platform that you're experiencing the issue on**: - [x] 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:** - 6.7.1 - **`Firebase` module(s) you're using that has the issue:** - FirebaseMessaging - **Are you using `TypeScript`?** - Y


alejandrovasta commented 3 years ago

Hey folks! any news on this? I am not receiving notifications on iOS when the app is quit or background - following all the guide points, but nothing.

ivarchand commented 3 years ago

Hi @forbesgillikin,

same issue here as well,

packages' info:

dependencies: "@react-native-firebase/analytics": "7.1.5", "@react-native-firebase/app": "7.3.0", "@react-native-firebase/crashlytics": "7.1.6", "@react-native-firebase/messaging": "7.1.7", "react": "16.9.0", "react-native": "0.61.1",

devDependencies: "@types/react-native": "0.60.23",

Others: Xcode-11.2.1 node- v12.18.2 npm- 6.14.5

when using admin.messaging().sendToDevice and setBackgroundMessageHandler: foreground & background works fine, but the quit state is not able to handle using setBackgroundMessageHandler messaging().setBackgroundMessageHandler(async remoteMessage => { try { console.log(index.ios.js setBackgroundMessageHandler remoteMessage : ${JSON.stringify(remoteMessage)}); } catch (error) { console.log(index.ios.js setBackgroundMessageHandler error : ${JSON.stringify(error)}); } }); In order to handle the quit state, api & passed message got updated like below, here, admin.messaging().send is used instead of admin.messaging().sendToDevice

admin.messaging().send({ data: { channelNumber: 'channel13' }, apns: { payload: { aps: { contentAvailable: true } }, headers: { 'apns-push-type': 'background', 'apns-priority': '5', 'apns-topic': "com.xyz.abc"//your app bundle identfier } }, fcmToken }).then((response) => { // Response is a message ID string. console.log('Successfully sent message:', response); }) .catch((error) => { console.log('Error sending message:', error); });

but facing the below error, due to this, no data message is passed node_modules/firebase-admin/lib/messaging/messaging-types.js:46 throw new error_1.FirebaseMessagingError(error_1.MessagingClientErrorCode.INVALID_PAYLOAD, 'Exactly one of topic, token or condition is required'); ^ FirebaseMessagingError: Exactly one of topic, token or condition is required at FirebaseMessagingError.FirebaseError [as constructor] (/Users/user/fcm-data-messages/node_modules/firebase-admin/lib/utils/error.js:42:28) at FirebaseMessagingError.PrefixedFirebaseError [as constructor] (/Users/user/fcm-data-messages/node_modules/firebase-admin/lib/utils/error.js:88:28) at new FirebaseMessagingError (/Users/user/fcm-data-messages/node_modules/firebase-admin/lib/utils/error.js:254:16) at Object.validateMessage (/Users/user/fcm-data-messages/node_modules/firebase-admin/lib/messaging/messaging-types.js:46:15) at Messaging.send (/Users/user/fcm-data-messages/node_modules/firebase-admin/lib/messaging/messaging.js:208:27) at buildDataMessageAndSend (/Users/user/fcm-data-messages/index.js:156:21) at Object. (/Users/user/fcm-data-messages/index.js:267:21) at Module._compile (internal/modules/cjs/loader.js:1138:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1158:10) at Module.load (internal/modules/cjs/loader.js:986:32) { errorInfo: { code: 'messaging/invalid-payload', message: 'Exactly one of topic, token or condition is required' }, codePrefix: 'messaging' }

Please help me Thanks In Advance

GustafLevinsson commented 3 years ago

I have a similar issue. It would not work on iOS but then I added content_avaible = true to my FCM-message and it seemed to work. Now after release it only works on some devices. The notification is received but the SetBackgroundHandler is not firing. The older TestFlight-versions that I tested does not seem to work any longer either. Im on v6.7.1. Anyone else faced similar issues? Thanks in advance.

mikehardy commented 3 years ago

@GustafLevinsson iOS may decide your app, on a specific device, is not worthy of the power budget to wake up and receive the message, so it simply won't. You can't see this unless you have the device logs (like the device is connected via cable with app launched via xcode or similar). User interaction, speed of device, cost of app init, all can factor in. It's maddening.

RPapin commented 3 years ago

@mikehardy Is there any other function which could be firing when we click on a notification when the app is on background ?

israelouteiro commented 3 years ago

to ios background listen:

        firebase.messaging().onNotificationOpenedApp(remoteMessage => { 
            console.log('FIREBASE IOS Background', remoteMessage)

        });
danielzzz commented 3 years ago

thanks @israelouteiro - that worked for me when the app was in background, but not when it was closed.

Edit: to get the notification when the app was closed you have to use firebase.messaging.getInitialNotification() method. between these three methods (onMessage, onNotificationOpenedApp and getInitialNotification) it seems that it covers all use cases.

mikehardy commented 3 years ago

As part of the work on #4104 several PRs have been merged, including one just merged and released now as @react-native-firebase/7.8.4

We believe FCM messages will be handled now:

We are no longer aware of cases that don't have known solutions.

If you can reproduce a case after updating to the latest code and running npx react-native-clean-project to make sure you have a clean build, please open a new issue post an App.js that demonstrates the problem.

kunalword commented 3 years ago

In iOS How to catch a push notification in background without user interaction? android working fine getting response at setBackgroundMessageHandler.

mikehardy commented 3 years ago

@kunalword using exactly the information in the comment I posted above yours, in combination with the documents for the background message handler in the messaging section of rnfirebase.io

hasansultan92 commented 3 years ago

So essentially we need to set the 'apns' property as part of the payload to trigger the functionality when the app is closed?

I have tried that from my backend and the result is the same ( app doesn't trigger the setBackgroundMessageHandler() functionality in the background)

mikehardy commented 3 years ago

@hasansultan92 Launch on a real device and watch console. Yes, send content-available in apns or it will not work with data only messages. If the user has swiped your app away your app is dead until a new user interaction, that's operating system policy. If you have been spending the power of the device too heavily iOS will throttle you. If your code and config isn't correct nothing will work. It is not easy but it does work when done correctly.

hasansultan92 commented 3 years ago

Hey so yes I have been using a real device and I used Xcode to build the app on the device. I can see the logs when the app is in background state(minimized) but obviously once I close the app on the device, the logs and connection to Xcode gets terminated so I can't really see what's happening when a message is being sent while the app is closed. The library/modules are working just fine on android.

I am sending notification plus data using my backend with the Admin SDK and whichever device I send it to, the notification pops up right away (including iOS even when in quit state) but just the background processes aren't being triggered for iOS. I did check my app power usage and it was high on a iPhone 6 however(that's probably because of the enormous number of console logs I was working with earlier). Please note I tried to check if the functionality was working in production env by uploading the app to TestFlight but yea that didn't work either. Im doubting my appdelegate.m config since I don't understand objective C so maybe if you have the simplest form of appdelegate.m that works could you share that? I also noticed iOS enabled some keys in info.plist and Id like to confirm if that's normal cause I have not seen any information about them so far.

mikehardy commented 3 years ago

Use Console.app If you send notification in the payload, not data only, your messages receive different handling AppDelegate shouldn't need anything i RNFBv6, just follow the rnfirebase.io docs

hasansultan92 commented 3 years ago

@mikehardy not sure what you mean by Console.app, is it the remote debugger using chrome?

As far as the docs show on firebase.io the handlers handling the messages with notification+ data are onMessage() - this works for sure - and setBackgroundMessageHandler()- doesn't work when app is quit in iOS.

Also Im not using RNFBv6, my packages are "@react-native-firebase/app": "^8.4.2", "@react-native-firebase/messaging": "^7.8.6", in package.json and I believe the corresponding numbers in the pod file.

If your sure this issue isn't a problem anymore on v6 I don't mind setting up the older version to be honest.

mikehardy commented 3 years ago

Definitely not the remote debugger with chrome. I believe that might even confound some of the functionality, I do not develop the debugger turned on, myself, as it executes the bundle off device and can have incorrectness side effects that are not expected.

Console.app is literally Console.app on your mac machine, while the real ios device is plugged in you may follow the devices logs.

Sorry the use of the phrase "v6" is imprecise and I should not have used it. There was a big break between V5 and V6 and we used to use "V6" as shorthand for "most current / modern". All of the versions are higher now of course, current stable is what I really intend.

setBackgroundMessageHandler does work when app is quit but not force closed / swiped away if that makes sense - the wording needs to be accurate/precise here. quit is the state when the app has been stopped by the OS in the background to preserve resources but the user did not actually kill the thing. In those cases, data only will work.

hasansultan92 commented 3 years ago

@mikehardy yea so I checked console.app from Xcode etc and yea indeed the device isn't registering the notification that came when the app has been killed/swiped off from the app drawer. I can see a log for the speaker to make a sound when the message comes in but nothing else no errors either. I however, can see the logs for a notification when the app is in background(minimized) and in foreground. To eliminate the CPU throttling fact that has been mentioned in several docs, I do not see any logs for cpu usage being high or anything when the app is in background or foreground but I do see an instruction when the device is sitting idle doing nothing to reduce cpu power.

As for the following - 'setBackgroundMessageHandler does work when app is quit but not force closed / swiped away if that makes sense - the wording needs to be accurate/precise here.' - I am guessing when the message comes in and the app was swiped away it takes the app to a background only mode? This however is not working for me on iOS. I have been debugging this app for the past 1 week just so I can save the message being sent from another user but I guess Ill have to now try another push notification service as this headache Is beyond my knowledge. PS, I did try data only and no that did not work either.

If there is anyone who actually got this library to work please please dm me

mikehardy commented 3 years ago

@hasansultan92 - "app swiped" == your app is dead. You will never get logical control back until a user taps on a visible notification or launches your app.

hasansultan92 commented 3 years ago

@mikehardy so essentially are u saying this is an iOS problem or something the library is not able to achieve? clearly when the app is swiped in android I can still perform the tasks I want using set backgroundHandler so Im gonna goes iOS on this. Ill tell you what I am trying to achieve and maybe you can tell me back if its impossible with this library or even push notifications in general. I am basically making a chat app and every time the receiving user is offline I want the push notifications to carry the data to the device and perform the actions to update local storage. Essentially if the user doesn't have my app running in the background or foreground on iOS he will never be able to see the new messages in the chat area. I obviously don't want to load all the messages from the db- it'll keep my server load busy and consumption of bandwidth will be high- so what other way can I solve this task of saving texts that were sent to the user when they were offline?

Yes, I am aware your a contributor to the library but you know, you might know the answer I am looking for

mikehardy commented 3 years ago

If the app is swiped the app is dead via ios policy. Iron law with the only exception being a notification may be posted to the notification center. You will need to do your own research and testing, my answers are brief but complete

mikehardy commented 3 years ago

...at least to the extent that I am not going to design your app architecture. That's on you of course, no offense

hasansultan92 commented 3 years ago

@mikehardy none taken. Its just the docs stated that the app is allowed to run headless/in the background and the handler is going to be the same as the background state for the quit state. If I knew that this functionality is limited by either the library itself or how react-native works I wouldn't have spent nearly a week into this you know. its alright thank you for your feedback. back to the drawing board it is for me

mikehardy commented 3 years ago

headless is a complicated topic In general it is android-specific / android only, it is a separate execution mode that bears examination on it's own, there are documents on react-native dev site about it

However, there is the case where your app on ios is fully launched (there is no true headless mode in ios), and there is a way to inject a property into the launch sequence simply so you can see if that is how you were launched in ios. This can happen if your app is allowed to start in response to a data-only cloud message, but is different than the "pure headless" mode that android has

You are implementing a pretty complicated system, in the exact areas that the operating systems protect the most (app starts, remote programming that requires permissions, energy usage etc). There's no avoiding the difficulty unfortunately. Make tiny example prototypes and test them is my advice. When I was developing this area of my app I just had a separate dev-only screen with buttons for each thing I wanted, and lots of logging and tested away until I found a mix of things that worked.

hasansultan92 commented 3 years ago

I believe the how is UIApplicationLaunchOptionsRemoteNotificationKey and I don't know Objective-C/Swift so yea right now Im looking for some objective c developers who can help me. Since this is more of a react-native headache I am going to assume that apps like Airbnb, FB, etc use the native part to handle the notification data and save it etc because so far I don't see any other way that user messages can come to devices and be saved. Thank you for your help though @mikehardy .

mikehardy commented 3 years ago

I believe they fetch state from server on app startup when user interacts with notification. Have you ever gotten a new message notification from facebook but then interacted with it while in airplane mode? That will show more how other apps do this

forbesgillikin commented 3 years ago

Ok. So. I have seen a lot of these threads over the last 2 months since I implemented Notifications in my app. The issue here is the RN Firebase Docs and the terminology used for different app states, coupled with a fundamental misunderstanding of how notifications work on iOS, and developers not doing their own docs research on the Apple Developer resources.

RN FIREBASE WORKS AS EXPECTED. YOU MUST UNDERSTAND THAT IF YOUR APP HAS BEEN CLOSED OR KILLED, BACKGROUND NOTIFICATIONS WILL NOT BE DELIVERED. THERE ARE NO PROBLEMS WITH THE RN FIREBASE CODE.

READ THE DOCUMENTATION FROM APPLE

https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/pushing_background_updates_to_your_app

Pushing Background Updates to Your App

Deliver notifications that wake your app and update it in the background.

IMPORTANT: Notifications that WAKE your app. If your app is closed, nothing will happen.

Background notifications are not useful in most common app frameworks. Stick to alert style notifications.

justcodejs commented 3 years ago

I had made a Firebase Push notification tutorial video hope it will help those who faced problem.

https://www.youtube.com/watch?v=wC3N-hxkWbk

ch3tan03 commented 3 years ago

Guys use below payload while sending data only notifications(remove title and body from notification) or data plus notifications

{ "registration_ids": [""], "message" : "You Got A Call", "priority": "high", "notification": { "title": "msg for topic", "body": "bodytext", "content_available": "true" }, "data": { "priority": "high", "callType": "audio", } }

Index.js should be messaging().setBackgroundMessageHandler(async remoteMessage => { // your logic or put alert }); function HeadlessCheck({ isHeadless }) { if (isHeadless) { // App has been launched in the background by iOS, ignore return null; } return ; }

// Current main application AppRegistry.registerComponent(appName, () => HeadlessCheck);

setBackgroundMessageHandler works on both iOS and Android when app is minimized:)

kitfai commented 3 years ago

Guys use below payload while sending data only notifications(remove title and body from notification) or data plus notifications

{ "registration_ids": [""], "message" : "You Got A Call", "priority": "high", "notification": { "title": "msg for topic", "body": "bodytext", "content_available": "true" }, "data": { "priority": "high", "callType": "audio", } }

Index.js should be messaging().setBackgroundMessageHandler(async remoteMessage => { // your logic or put alert }); function HeadlessCheck({ isHeadless }) { if (isHeadless) { // App has been launched in the background by iOS, ignore return null; } return ; }

// Current main application AppRegistry.registerComponent(appName, () => HeadlessCheck);

setBackgroundMessageHandler works on both iOS and Android when app is minimized:)

Hi @ch3tan03 .

I saw you mentioned tha "sending data only notifications(remove title and body from notification) or data plus notifications",but in your notification tag , there are title and body? Can you elaborate more?

Thanks

yasir-netlinks commented 3 years ago

I am facing a similar problem using the latest version 10.5.1. The setBackgroundMessageHandler doesn't trigger when the app is in quit state. However, the app wakes up on OS versions <= 14.3. But on the latest versions of OS it doesn't wake the device. Any ideas guys, this is really frustrating.

yasir-netlinks commented 3 years ago

It seems that I have got it to work. I had content_available and priority appropriately set and yet the setBackMessageHandler didn't trigger when the app is in quit state, and I'm sending data-only -- silent notification. it seems that setting this did the job: https://rnfirebase.io/messaging/usage#background-application-state. Don't forget to inject isHeadless in your AppDelegate.m too.

I am using legacy API and this is the payload, hope this might help

{
  "to": "/topics/myTopic",
  "priority": "high",
  "content_available": true,
  "data": {}
 }
harutyundr commented 3 years ago

Hi everyone,

I had the same issue and I managed to resolve it by including it "content-available" => 1, in the message. I see several people mention content_available, contentAvailable and other keys, and it is also not clear where exactly the key should be specified. So the structure that worked for me was (using PHP, but I guess it represent the structure required by the REST API itself):

[
    // use data in the application to build custom local notifications
    'data' => [
        'custom_title' => 'LOCAL - $GOOG up 1.43% on the day',
        'custom_body' => '$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
    ],
    'apns' => [
        'headers' => [
            'apns-priority' => '10',
        ],
        'payload' => [
            'aps' => [
                "content-available" => 1,
                'badge' => random_int(1, 100),
            ],
        ],
    ]
]

I provide a random badge count with each request as it seems iOS always updates this count, even if the content never reaches the app and my callback is not invoked, so this was an easy way to understand if my message is properly formatted.

barghi commented 2 years ago

Hi everyone,

This works for me in both Android and iOS and of course, you can use these methods in Hooks.

https://rnfirebase.io/messaging/notifications#main

componentDidMount() {
    this._handleNotification();
}

_handleNotification() {
      // 'Notification caused app to open from background state:'
      messaging()
            .onNotificationOpenedApp(remoteMessage => {
                  console.log(remoteMessage);
            });

      // 'Notification caused app to open from quit state'
      messaging()
            .getInitialNotification()
                  .then(remoteMessage => {
                        console.log(remoteMessage);
                  });
}
rnike commented 2 years ago

Hi everyone,

I had the same issue and I managed to resolve it by including it "content-available" => 1, in the message. I see several people mention content_available, contentAvailable and other keys, and it is also not clear where exactly the key should be specified. So the structure that worked for me was (using PHP, but I guess it represent the structure required by the REST API itself):

[
    // use data in the application to build custom local notifications
    'data' => [
        'custom_title' => 'LOCAL - $GOOG up 1.43% on the day',
        'custom_body' => '$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
    ],
    'apns' => [
        'headers' => [
            'apns-priority' => '10',
        ],
        'payload' => [
            'aps' => [
                "content-available" => 1,
                'badge' => random_int(1, 100),
            ],
        ],
    ]
]

I provide a random badge count with each request as it seems iOS always updates this count, even if the content never reaches the app and my callback is not invoked, so this was an easy way to understand if my message is properly formatted.

I believed the key depends on which API we're using, for Lagacy API we are using it with content_available, in your case you are using v1 API and the key will be content-available

mikehardy commented 2 years ago

Depends on the library you are using to generate the JSON as well, or if it's raw. If you use firebase-admin-sdk, that's a library that will then generate JSON, and it uses contentAvailable. The raw key that Apple's APNs servers actually consume in json is content-available though. Perhaps if you use Python it's content_available

The documentation for your library will tell you. Or if you are doing raw JSON then the Apple docs tell you. You must use the appropriate key for your context.