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

[πŸ›] setBackgroundMessageHandler is not called reliable #5576

Closed FuriKuri closed 3 years ago

FuriKuri commented 3 years ago

Hi, I'm facing some strange issues with the messaging().setBackgroundMessageHandler() function.

Issue

I have noticed that just when the iPhone has been restarted or even completely resetted, setBackgroundMessageHandler() is not called. This is also the case, if the app is installed on a new device, where it has not been installed before. Only after some time (up to a few hours) setBackgroundMessageHandler() is called reliably again. From then on, I no longer notice any problems.

On Android I do not facing any issues.

The notifications also go out with content-available: true and apns-priority: 10.

const App: React.FC = () => {
    const [msg, setMsg] = React.useState({ ... });

    messaging().setBackgroundMessageHandler(async (remoteMessage) => {
        setMsg(mapMessage(remoteMessage));
        Notifications.ios.setBadgeCount(1);
    });

    ...
};

export default App;

Project Files

Javascript

Click To Expand

#### `package.json`: ```json "dependencies": { "@react-native-community/checkbox": "^0.5.7", "@react-native-cookies/cookies": "^6.0.4", "@react-native-firebase/app": "^12.4.0", "@react-native-firebase/messaging": "^12.4.0", "@react-navigation/native": "^5.9.3", "@react-navigation/stack": "^5.14.3", "react": "17.0.2", "react-native": "0.64.2", "react-native-button": "^3.0.1", "react-native-device-info": "^8.1.7", "react-native-gesture-handler": "^1.10.3", "react-native-htmlview": "^0.16.0", "react-native-notifications": "^4.1.2", "react-native-safe-area-context": "^3.3.0", "react-native-screens": "^2.18.1", "react-native-vector-icons": "^8.1.0", "react-native-webview": "^11.6.6" }, ``` #### `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 require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' platform :ios, '10.0' target 'News' do config = use_native_modules! use_react_native!(:path => config["reactNativePath"]) pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons' target 'NewsTests' do inherit! :complete # Pods for testing end # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and # you should disable these next few lines. #use_flipper!({'Flipper'=>'0.78.0'}) #post_install do |installer| # flipper_post_install(installer) #end end # add pods for desired Firebase products # https://firebase.google.com/docs/ios/setup#available-pods ``` #### `AppDelegate.m`: ```objc #import "AppDelegate.h" #import #import #import #import #ifdef FB_SONARKIT_ENABLED #import #import #import #import #import #import static void InitializeFlipper(UIApplication *application) { FlipperClient *client = [FlipperClient sharedClient]; SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; [client addPlugin:[FlipperKitReactPlugin new]]; [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; [client start]; } #endif @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { #ifdef FB_SONARKIT_ENABLED InitializeFlipper(application); #endif if ([FIRApp defaultApp] == nil) { [FIRApp configure]; } RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"News" initialProperties:nil]; rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; return YES; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } @end ```


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:** ``` System: OS: macOS 11.4 CPU: (8) x64 Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz Memory: 653.89 MB / 32.00 GB Shell: 5.8 - /bin/zsh Binaries: Node: 15.14.0 - ~/.nvm/versions/node/v15.14.0/bin/node Yarn: 1.22.10 - /usr/local/bin/yarn npm: 7.7.6 - ~/.nvm/versions/node/v15.14.0/bin/npm Watchman: 2021.06.07.00 - /usr/local/bin/watchman Managers: CocoaPods: 1.10.2 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: iOS 14.5, DriverKit 20.4, macOS 11.3, tvOS 14.5, watchOS 7.4 Android SDK: Not Found IDEs: Android Studio: 4.2 AI-202.7660.26.42.7486908 Xcode: 12.5.1/12E507 - /usr/bin/xcodebuild Languages: Java: 11.0.11 - /Users/theo/.jenv/shims/javac npmPackages: @react-native-community/cli: Not Found react: 17.0.2 => 17.0.2 react-native: 0.64.2 => 0.64.2 react-native-macos: Not Found npmGlobalPackages: *react-native*: Not Found ``` - **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:** - `12.4.0` - **`Firebase` module(s) you're using that has the issue:** - `Cloud Messaging` - **Are you using `TypeScript`?** - `Y` & `4.3.5`


mikehardy commented 3 years ago

What you are noticing is standard for iOS. iOS does not guarantee deliver of background messages, period - you must not rely on it. If you set content-available to 1, then you are telling iOS "hey, the app has new content available, and it would be nice to cache it in advance if you will give the app an allocation of power to run"

That's it. It's for caching content. To use it for imperative business logic purposes is a design flaw, you must use visible notifications if you want to increase your delivery chances

This is outside of the ability of this module to affect - it's part of the platform unfortunately

FuriKuri commented 3 years ago

Thank you for the quick response. What is meant by "visible notifications"?

FuriKuri commented 3 years ago

The only idea, what comes up to my mind is, just to notify the user, that they are new data and the app itself has to pull the new data from the server.

mikehardy commented 3 years ago

Thank you for the quick response. What is meant by "visible notifications"?

an FCM JSON containing a notification block, it will result in a visible notification, sorry I was not more precise

The only idea, what comes up to my mind is, just to notify the user, that they are new data and the app itself has to pull the new data from the server.

The app should always, by design, compare current state with server state when it starts, and pull new data as needed (that is, it should expect to handle the situation where the cache is out of date, because for instance the background message was not delivered so pre-caching did not happen)

FuriKuri commented 3 years ago

Ok we are already using notification, and the notification itself works without problems so far. Only the background processing makes problems. But that's ok if the messages have to be actively fetched from the server. We thought it would be sufficient to have the data pushed.

Thank you again for the answer πŸ₯‡

Jwlee134 commented 3 years ago

@mikehardy Hi I want to ask you a question. if I understand right, visible notification just increases delivery chances, it also doesn't guarantee 100% delivery?

mikehardy commented 3 years ago

Correct. Nothing is 100% guaranteed. Visible notifications are as close as you can get though, assuming your app doesn't "abuse" them (that is, when you send visible notifications, your users typically interact with them and/or interact with your app a lot - so the power-miser algorithms in phones "learn" that delivery is wanted)

Jwlee134 commented 3 years ago

@mikehardy Thank you for the quick answer. Can I ask one more question? I tried to send "data-only" message 10 times in a row when my app is in background state, not in "quit" state and everything was delivered successfully. Like you said nothing is 100% guaranteed, It's because my device was in best condition, such as not in low power mode and etc etc?

mikehardy commented 3 years ago

@Jwlee134 data-only messages will tempt you in like this. A typical developer scenario is to have a device plugged in to their laptop (so the battery is at full power and it is plugged in! the algorithm likes that), with all permissions granted (the algorithm likes that! background refresh is on etc) and of course you interact with your app a lot in testing (the algorithm loves that!).

So it looks really reliable.

Then in practice your users will be at 30% battery and pre-emptively enable low power mode and maybe turn off background refresh. And they may only use your app a little bit. So in reality on a user's device the algorithm doesn't let your app wake up for a data-only messages and it's disappointing.