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.71k stars 2.22k forks source link

[🐛]messaging().setBackgroundMessageHandler not working in release mode on iOS #6466

Closed charlieStrikeBright closed 1 year ago

charlieStrikeBright commented 2 years ago

(:fire:) I am using react native firebase version 15.3.0 (latest) with react version 66. I am testing on an iphone 13 with iOS 15.6. UPDATE: tested on an iphone xs with 15.5 OS and it works properly, so it seems to be an issue with iOS 15.6.

In index.js, I set the setBackgroundMessageHandler and it works perfectly if I am debugging on my iphone. However, in release mode (e.g., when my phone is not connected to the mac), the setBackgroundMessageHandler does not get assigned. The message just gets handled by iOS. I have scoured stackoverflow, github, etc. and have not found an answer that works. 2 threads of note were 1) a user mentioned that in debug mode a different version of javascript is loaded (chrome v8 for debug, vs. safari in release) and it may be that it works in Chrome version of javascript but not safari; and 2) another user said that they spoke with an apple developer and he said that apple does not allow apps to handle remote notifications when the app is in the background (but it is working fine in debug mode, so not sure if this is the case).

My code from index.js is below:

messaging().setBackgroundMessageHandler(async ( remoteMessage ) => { //this only works in debug mode/when connected to mac via cable let notification = { notificationKey: remoteMessage.data.notificationKey, priority: remoteMessage.data.priority, podID: remoteMessage.data.podID, notificationType: remoteMessage.data.notificationType, notificationTypeID: remoteMessage.data.notificationTypeID, title: remoteMessage.data.title, body: remoteMessage.data.body, createdAt: remoteMessage.data.Createtms, imageURL: remoteMessage.data.imageURL, viewed: remoteMessage.data.viewed, }
await notifee.displayNotification({title: notification.title, body: notification.body}); // Increment the count by 1 await notifee.incrementBadgeCount(); })

Help is appreciated.

dogankablan commented 2 years ago

I have the same problem.

charlieStrikeBright commented 2 years ago

NOTE: This issue only occurs with iOS 15.6 - I tested with 15.5 and background messaging is working in release mode.

mhpc0909 commented 2 years ago

I tested two model iPhone 8 with 15.6 and iPhone 13 mini with 15.6. iPhone 8 works well but 13 mini not working both debug and release.

charlieStrikeBright commented 2 years ago

Any update on from the RN firebase team? I have tried just about everything, e.g., updating delegate.m, moving the timing and code location of the background message handler, etc. and no matter what I try I still can't get background messaging to work on iphone 13 pro using iOS 15.6 unless I am in debug mode and connected to my mac. Is this an RN firebase issue or a google firebase issue? I've been monitoring google firebase page and don't see anything about this bug, and I am using latest version.

mikehardy commented 2 years ago

Sorry, been traveling. Still traveling a couple days more. Will be back in action next week. I have not personally seen this, probably because I am not testing on ios 15.6 yet. Frustrating that this changed though, between iOS versions. Has anyone checked upstream firebase-ios-sdk repo to see if it is logged there? Seems like it will be a lower-level thing if it varies by iOS version

w4ugit commented 2 years ago

is there any news or maybe someone found a solution?

charlieStrikeBright commented 2 years ago

Any updates?

mikehardy commented 2 years ago

Just catching up on things now, I do not see anything that looks on target in firebase-ios-sdk either, so there may be something wrong in react-native-firebase, I'm unsure.

It sounds like you have tested these setups with these results:

Can you provide a copy of the JSON (minus the device token...) that you use to test this? Can you make sure background refresh is enabled for your app? Can you make sure low power is enabled for your app?

For release mode + plugged in, you will have access to device logging, if you enable debugging (with -FFIRDebugEnabled I think...) you can get more information on delivery / execution but even without that flag enabled you should get information from the system explaining why it decided to deliver or not deliver the message to the app

Usually it is because it made a power consumption decision to not start the app for some reason it should detail

w4ugit commented 2 years ago

I tested on iphone 8 with ios 15.6, also on iphone 12 with ios 15.6 - not working

mikehardy commented 2 years ago

@w4ugit that is good to know, but the whole block of questions I posed and information I was looking for would be most helpful if you can provide it :pray:

w4ugit commented 2 years ago

I'll try, but I don't really understand what exactly needs to be done. I'm new to React Native and not very familiar with debugging. can you provide some instruction or link on how to do it properly?

mikehardy commented 2 years ago

Well, messaging, especially the delivery of background messages, is subtle (that is an understatement), so this is not the easiest thing to do but odds are you will have questions that may only be answered by debugging in the future so it is worth setting up.

In general the outline is:

w4ugit commented 2 years ago

i plugged in my phone, launched the app, rolled it up and sent a push. it appeared, but the program did not react in any way. no mistakes, nothing.

i updated to ios 15.6.1

w4ugit commented 2 years ago

as far as i know there is no need to specify anything extra to make the messages work in the background?

w4ugit commented 2 years ago

https://rnfirebase.io/#altering-cocoapods-to-use-frameworks

i had some compilation problems so my Pods file looks like this

require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

platform :ios, '13.0'
use_frameworks! :linkage => :static

target 'LuckyLife' do
  $static_framework = []
  # React Native Maps dependencies
  rn_maps_path = '../node_modules/react-native-maps'

  config = use_native_modules!

  use_react_native!(
    :path => config[:reactNativePath],
    # to enable hermes on iOS, change `false` to `true` and then install pods
    :hermes_enabled => true
  )

  $static_framework += [
      'react-native-maps',
      'react-native-google-maps',
      'Google-Maps-iOS-Utils',
      'GoogleMaps',
      'fmt',
    ]

    pod 'react-native-google-maps', :path => rn_maps_path
    pod 'GoogleMaps'
    pod 'Google-Maps-iOS-Utils'

  target 'LuckyLifeTests' do
    inherit! :complete
    # Pods for testing
  end

  pre_install do |installer|
      Pod::Installer::Xcode::TargetValidator.send(:define_method, :verify_no_static_framework_transitive_dependencies) {}
          installer.pod_targets.each do |pod|
              if $static_framework.include?(pod.name)
                  def pod.build_type;
                  Pod::BuildType.static_library # >= 1.9
              end
          end
      end
  end

  post_install do |installer|
    react_native_post_install(installer)
    __apply_Xcode_12_5_M1_post_install_workaround(installer)
  end
end

maybe I did something wrong in it

w4ugit commented 2 years ago

I read in another topic that there may be a problem with certificates. how to correctly check that the certificates are correct?

mikehardy commented 2 years ago

@w4ugit please use triple backticks (and optionally a language specifier) when you include blocks of code, I've edited your comment to do this, it presents a lot better, that is, it is actually readable :-)

You are doing something wrong there, you are not using use_frameworks, you are using all manner of workarounds to avoid it. I understand the desire to do so but I can't support that. Note that react-native-maps has a patch you can use that lets it work with use_frameworks, use that, and use use_frameworks, and get rid of all the workaround stuff, and you'll be supportable again.

i plugged in my phone, launched the app, rolled it up and sent a push. it appeared, but the program did not react in any way. no mistakes, nothing.

I don't know what "rolled it up" means, sorry. You opened the task switcher and flicked it away, that is you force-closed the app? I don't know what "it appeared" means. You were watching the logs and saw something? What? "the program did not react in any way", what reaction were you expecting, that you did not receive? Why were you expecting that?

I'm looking for concrete details here, as they are the foundation of a reproduction, and your description leaves me with room for doubt in each phrase unfortunately

as far as i know there is no need to specify anything extra to make the messages work in the background?

That expectation is incorrect unfortunately, it is exactly contrary to this information in this link: https://rnfirebase.io/messaging/usage#data-only-messages

One of the most important things to understand perhaps is that (pardon the big shout here) DATA-ONLY MESSAGES ARE UNRELIABLE BY DESIGN. Apple cares about battery life much more than it cares about what it calls "content refresh" messages. Phone is out of battery? Users are really mad! Content did not update in the background without the app even being open? Users don't care as much. The content should update when the app is opened (implying the user wants to spend energy on the app) and the app detects content is out of date.

That's what data-only messages are for on Apple, and apps must be designed accordingly.

Reliable delivery is afforded by notification messages.

mikehardy commented 2 years ago

I read in another topic that there may be a problem with certificates. how to correctly check that the certificates are correct?

The easiest way is not so much to compare, but to download fresh APNs profile information (p8 file, etc) from apple developer site, and upload that to firebase web console side in the messaging area. That is you do not compare you just copy known-correct ones in to place

w4ugit commented 2 years ago

sorry, I don't know English well, so I use a translator)

I will solve the problem. I send only-data pushes and they arrive

i read your post in another thread that mixed pushes might not work in the background. and it really is

mikehardy commented 2 years ago

No worries about language barriers, I live in a country where I have to use my second language full time, and I understand the struggle. The english was okay it just was not detailed enough. I want all the details ;-)

If you are sending a mixed-payload message then the notification block takes priority, and it behaves more like a notification, where you only get access to the data block when the user interacts and the notification handlers are called

dualism98 commented 2 years ago

Hey! I have same problem with setBackgroundMessageHandler on iOS. App gets and shows notifications, but I don't see any logs about handling them from handler. On Android it works fine: I see logs about processing message in background All other things works fine on both platforms: subscribing to topic, getting of fcm token, handling initial notification and notification opened app I would appreciate your help. Thanks

dualism98 commented 2 years ago

I have this problem while sending notifications from Firebase console and from our backend

mikehardy commented 2 years ago

There are multiple here on the same issue and in my experience with messaging, everyone is likely having different problems, with similar symptoms. No one is posting enough information to say conclusively the problems are the same.

Not a single person here has posted the JSON they can send to the FCM REST API that would demonstrate this problem, and the logs they see on device when the message is delivered. Without that there can be no progress, just a lot of filled comment boxes, which is not the best way to go about things

charlieStrikeBright commented 2 years ago

Hi Mike,

I have attempted to send notification packets with notification data and without notification data. Below is an actual JSON packet sent to FCM and succesfully received on a device. Here is a summary of what is occuring: 1) The messages are being delivered to the devices (both iOS and Android). For example, if the app that sends/consumes the messages is NOT running at all, the device shows the notification. 2) Furthermore, if the app is running in the background on an iOS device that is NOT running 15.6, RN firebase onbackgroundmessagehandler gets called and works properly. Additionally, if running on iOS 15.6 in debug mode or release mode but connected to the mac, the onbackgroundmessagehandler gets called. However, if running in release mode on 15.6 and not connected to the mac, the onbackgroundmessage handler does not get called. 3) Note that I added code to the app delegate file to nslog if the notification is being received by the device and app, and it is being received.

Actual JSON packet for notification: Posted to: https://fcm.googleapis.com/fcm/send

{"registration_ids": ["cAMnK6CVrESVjvotKYvIMz:APA91bFidbXTC1KhR7lzWAol1myZ9ZCH__xxxxxxxxxxxx...removedforsecurity"],
"mutable-content":true,
"content_available":true,
"notification":{"priority":"high",
"title":"Chat from ModPod",
"body":"How is it going today",
"image":"",
"click_action":"",
"sound":"",
"badge":1,
"dry_run":"false"} 
"data":{"notificationKey":"chat-101",
"podID":290,
"podMemberID":85,
"notificationType":"chat",
"notificationTypeID":"101",
"priority":"high",
"title":"Chat from ModPod","body":"How is it going today",
"imageURL":"", 
"viewed":"false" ,
"dry_run":"false"}}

Log received on the device - logged by the backgroundmessagehandler in debug mode:

2022-08-24 11:38:04.501065-0400 ModPod[6887:531371] [javascript] 'on background message received: ', { data: 
   { fcm_options: { image: '' },
     podID: '290',
     title: 'Chat from ModPod',
     imageURL: '',
     podMemberID: '85',
     viewed: 'false',
     notificationType: 'chat',
     dry_run: 'false',
     body: 'How is it going today',
     notificationKey: 'chat-101',
     priority: 'high',
     notificationTypeID: '101' },
  category: '',
  from: 'XXXXXXXXXXXXX',
  messageId: '1661355484074553',
  notification: 
   { ios: { badge: 1 },
     title: 'Chat from ModPod',
     sound: '',
     body: 'How is it going today' },
  contentAvailable: true,
  mutableContent: true }
charlieStrikeBright commented 2 years ago

Any update here? I posted the JSON and all the requested info. Still have this issue.

mikehardy commented 2 years ago

Sorry, I'm just not sure what to make of this. I have an iPhone SE (1st gen) running iOS 15.6.1

It's running an app in release mode (my main work app, where I use data-only FCM sometimes, and have some diagnostics commands that I can send when I need to).

I sent one of the diagnostics commands that causes the app to upload something to firestore, in the background, from a function installed via setBackgroundMessageHandler

I ran this cloud function, which sent a data-only FCM, while the iPhone SE was unplugged, the app was in the background, and the screen was off, and it worked. The upload to firestore nearly immediately happened, which indicates that things are working.

So, I dunno. It is possible, though would be really strange, if something in the release build process was wrecking the swizzling necessary for the react-native-firebase/messaging Delegates here https://github.com/invertase/react-native-firebase/tree/main/packages/messaging/ios/RNFBMessaging to swizzle in and get called

But that's all I can think of. They are sensitive to prior swizzling and will noop in case they detect something, I'm guessing something about that is breaking somehow

charlieStrikeBright commented 2 years ago

Thanks Mike. I think I know what it is - and it is just a configuration on my phone. I was able to get it to work by enabling background refresh and then restarting my phone. I had enabled background refresh before, but it did not resolve the issue, and so I turned it back off (saving battery). However, enabling it and restarting my phone then I get the background messages. So it looks like it was specific to my phone config. Sorry for the goose chase.

mikehardy commented 2 years ago

Ah, that makes sense. Yes background refresh is really important. I need to actually propose this as a PR to netinfo (I'm the maintainer...I think I could get it merged) but haven't had time

cat patches/@react-native-community+netinfo+9.3.0.patch

diff --git a/node_modules/@react-native-community/netinfo/android/src/main/java/com/reactnativecommunity/netinfo/ConnectivityReceiver.java b/node_modules/@react-native-community/netinfo/android/src/main/java/com/reactnativecommunity/netinfo/ConnectivityReceiver.java
index f0e6db1..be38e47 100644
--- a/node_modules/@react-native-community/netinfo/android/src/main/java/com/reactnativecommunity/netinfo/ConnectivityReceiver.java
+++ b/node_modules/@react-native-community/netinfo/android/src/main/java/com/reactnativecommunity/netinfo/ConnectivityReceiver.java
@@ -147,6 +147,23 @@ public abstract class ConnectivityReceiver {
                 "isInternetReachable",
                 mIsInternetReachable && (requestedInterface == null || requestedInterface.equals(mConnectionType.label)));

+        String refreshStatus = "unknown";
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N && getConnectivityManager() != null) {
+            int backgroundRefreshStatus = getConnectivityManager().getRestrictBackgroundStatus();
+            switch (backgroundRefreshStatus) {
+                case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED:
+                    refreshStatus = "available";
+                    break;
+                case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED:
+                    refreshStatus = "denied_if_metered";
+                    break;
+                case ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED:
+                    refreshStatus = "allowlist";
+                    break;
+            }
+        }
+        event.putString("backgroundRefresh", refreshStatus);
+
         // Add the details, if there are any
         String detailsInterface = requestedInterface != null ? requestedInterface : mConnectionType.label;
         WritableMap details = createDetailsMap(detailsInterface);
diff --git a/node_modules/@react-native-community/netinfo/ios/RNCNetInfo.m b/node_modules/@react-native-community/netinfo/ios/RNCNetInfo.m
index 732ea0a..e0bd57d 100644
--- a/node_modules/@react-native-community/netinfo/ios/RNCNetInfo.m
+++ b/node_modules/@react-native-community/netinfo/ios/RNCNetInfo.m
@@ -108,9 +108,20 @@ RCT_EXPORT_METHOD(getCurrentState:(nullable NSString *)requestedInterface resolv
     details[@"isConnectionExpensive"] = @(state.expensive);
   }

+  NSString *refreshStatus = @"unknown";
+  enum UIBackgroundRefreshStatus backgroundRefreshStatus = [[UIApplication sharedApplication] backgroundRefreshStatus];
+  if (backgroundRefreshStatus == UIBackgroundRefreshStatusAvailable) {
+    refreshStatus = @"available";
+  } else if(backgroundRefreshStatus == UIBackgroundRefreshStatusDenied) {
+    refreshStatus = @"denied";
+  } else if(backgroundRefreshStatus == UIBackgroundRefreshStatusRestricted) {
+    refreshStatus = @"restricted";
+  }
+
   return @{
     @"type": selectedInterface,
     @"isConnected": @(connected),
+    @"backgroundRefresh": refreshStatus,
     @"details": details ?: NSNull.null
   };
 }

I'm not aware of another place that checks both. Notifee has one for background restrictions but only on Android?

If you wanted to PR this to netinfo that would save me the trouble

mikehardy commented 2 years ago

(at least with the knowledge then you can prompt the user to enable it, if it's really important for a use case the user will actually value, and open app settings)

tigerjiang commented 2 years ago

Is there any update for this issue? I met the same problem too.

charlieStrikeBright commented 2 years ago

Hi Tiger, I found it was an issue with background app refresh - it MUST be on for notifications to work.

github-actions[bot] commented 1 year ago

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

DavidGOrtega commented 2 months ago

Im hitting this issue. I have restarted the iphone after enabling background refresh and still nothing. It only works for me when the device is attached to Xcode

DavidGOrtega commented 2 months ago

Suddenly works for me. My best guess is that the ML background scheduler in iOS is the culprit.