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.63k stars 2.2k forks source link

[🐛] 🔥 onNotificationOpenedApp() not running and getInitialNotification() not providing data when restoring app state from storage on Android #7976

Open haakonrj-dyplink opened 1 month ago

haakonrj-dyplink commented 1 month ago

Issue

Describe your issue here

Hi, we are experiencing a problem where opening the app from a push notification do not provide notification data, or trigger the onNotificationOpenenedApp event when app with app-state is restored from storage on Android. I do not mean from memory.

It happens when the app-state has been stopped and lies in memory then moved to storage after phone runs out of ram by opening other apps etc. When opening a push notification when the app is in this state. The app-state is restored from storage, and the onNotificationOpenenedApp does not trigger and getInitialNotification() gives null for the remoteMessage.

In all other scenarios we have no problems with push notifications, it only happens in this rear scenario.


Project Files

build.gradle

buildscript { ext { buildToolsVersion = "34.0.0" minSdkVersion = 23 compileSdkVersion = 34 targetSdkVersion = 34 ndkVersion = "26.1.10909125" kotlinVersion = "1.9.22" } repositories { google() mavenCentral() } dependencies { classpath("com.android.tools.build:gradle") classpath("com.google.gms:google-services:4.4.2") classpath("com.facebook.react:react-native-gradle-plugin") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") } }

apply plugin: "com.facebook.react.rootproject"

Javascript

// User has permissions or provisional permissions messaging().onNotificationOpenedApp(async (remoteMessage) => { console.log('Notification opened app:', remoteMessage); if (remoteMessage?.data?.url) { notificationQueue.enqueue({ url: remoteMessage.data.url as string }); } });

// Handle foreground messages messaging().onMessage(async (remoteMessage) => { console.log('Foreground message:', remoteMessage); });

messaging().setBackgroundMessageHandler(async (remoteMessage) => { console.log('Doing some handling :)'); });

// Check for notifications that caused the app to open from a terminated state messaging() .getInitialNotification() .then((remoteMessage) => { console.log('Notification caused app to open from terminated state:', remoteMessage); if (remoteMessage?.data?.url) { notificationQueue.enqueue({ url: remoteMessage.data.url as string }); } });

Click To Expand

#### `package.json`: ```json "@react-native-firebase/analytics": "^20.3.0", "@react-native-firebase/app": "^20.3.0", "@react-native-firebase/messaging": "^20.3.0", "react-native": "^0.74.5", ``` #### `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? - [x] 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 // This is MainApplication.kotlin package com.newsroomapp import android.app.Application import com.facebook.react.PackageList import com.facebook.react.ReactApplication import com.facebook.react.ReactHost import com.facebook.react.ReactNativeHost import com.facebook.react.ReactPackage import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost import com.facebook.react.defaults.DefaultReactNativeHost import com.facebook.soloader.SoLoader class MainApplication : Application(), ReactApplication { override val reactNativeHost: ReactNativeHost = object : DefaultReactNativeHost(this) { override fun getPackages(): List = PackageList(this).packages.apply { // Packages that cannot be autolinked yet can be added manually here, for example: // add(MyReactNativePackage()) } override fun getJSMainModuleName(): String = "index" override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED } override val reactHost: ReactHost get() = getDefaultReactHost(applicationContext, reactNativeHost) override fun onCreate() { super.onCreate() SoLoader.init(this, false) if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { // If you opted-in for the New Architecture, we load the native entry point for this app. load() } } } ``` #### `AndroidManifest.xml`: ```xml ```


Environment

Click To Expand

**`react-native info` output:** ``` System: OS: macOS 14.6.1 CPU: (8) arm64 Apple M1 Pro Memory: 86.53 MB / 16.00 GB Shell: version: 3.2.57 path: /bin/bash Binaries: Node: version: 20.12.2 path: ~/.nvm/versions/node/v20.12.2/bin/node Yarn: version: 1.22.10 path: ~/.npm-packages/bin/yarn npm: version: 10.8.2 path: ~/repos/newsroom/NewsroomApp/node_modules/.bin/npm Watchman: version: 2024.08.05.00 path: /opt/homebrew/bin/watchman Managers: CocoaPods: version: 1.15.2 path: /Users/haakonreiss-jacobsen/.rvm/gems/ruby-3.0.0/bin/pod SDKs: iOS SDK: Platforms: - DriverKit 23.5 - iOS 17.5 - macOS 14.5 - tvOS 17.5 - visionOS 1.2 - watchOS 10.5 Android SDK: Not Found IDEs: Android Studio: 2022.2 AI-222.4459.24.2221.9862592 Xcode: version: 15.4/15F31d path: /usr/bin/xcodebuild Languages: Java: version: 17.0.10 path: /usr/bin/javac Ruby: version: 3.0.0 path: /Users/haakonreiss-jacobsen/.rvm/rubies/ruby-3.0.0/bin/ruby npmPackages: "@react-native-community/cli": Not Found react: Not Found react-native: Not Found react-native-macos: Not Found npmGlobalPackages: "*react-native*": Not Found Android: hermesEnabled: true newArchEnabled: false iOS: hermesEnabled: true newArchEnabled: false ``` - **Platform that you're experiencing the issue on**: - [ ] iOS - [x] 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.3.0` - **`Firebase` module(s) you're using that has the issue:** - `e.g. Instance ID` - **Are you using `TypeScript`?** - `Y` & `4.8.4`


russellwheatley commented 3 weeks ago

hey @haakonrj-dyplink - happy to look into this. Do you have a simple way to verify this behaviour?

haakonrj-dyplink commented 2 weeks ago

Hi, sorry for the late response, but we wanted to due some more testing, and ask a friend that has more experience than me with this package.

Here are two videos. One where a notification opens the app normally from killed state. The other I open the app through a notification after opening a ton of other apps, moving the app state from ram to storage. We have removed any splash screens for these tests.

We have tried reproducing the result by limiting the phones resources trough developer options, but have been unsuccessful. Only way we have reproduced the error is by opening apps until the appstate is moved to storage. When doing this the onRestoreInstanceState in MainActivity gets called.

NB! while the first video shows navigation to the correct page while the second dont, it is not what we are testing for. To exclude potential error with navigation we are purely looking if messaging().getInitialNotification() gives notification data when opening the app

// First video opening fresh. Here the messaging().getInitialNotification() always gives the notification data, and navigates the user to the correct page https://github.com/user-attachments/assets/afdcf6ee-8ed9-4851-a288-e680d1ac8bd1

// Restoring state from storage. This video is cut to reduce length. Just a bunch of app openings. https://github.com/user-attachments/assets/d1a0e205-7d11-461b-8d13-43c7537f3ee3

Here the app is opened then put in the background by opening other apps through a script. After opening enough apps the appstate is moved to storage. Then app is opened through the notification. messaging().getInitialNotification() runs but the remoteMessage is null, and navigation does not happen.

We belive after much logging and testing by moving the listeners around the app and trying different initiation timeout that this is some kind of race condition. While we never got remoteMessage to contain the data in this situation. We did replicate the same behaviour by changing where we initiated our app by placing the listeners outside of the react native app, by just setting them up directly in index.js. Then when opening the app from a notification while not in background (fresh state) messaging().getInitialNotification() gave null 50% of the time