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.59k stars 2.19k forks source link

setBackgroundMessageHandler not working on test flight #7504

Closed alperenkarip closed 8 months ago

alperenkarip commented 8 months ago

Hello, everything works correctly in the development environment, but I cannot receive kill state notifications in the test flight. setBackgroundMessageHandler is not triggered. When I open the application, notifications start coming. I tried many solutions but none of them worked. I even wrote a module in setBackgroundMessageHandler that will leave a log in firestore every time it runs, but no recording occurs, when I try it in the development environment, a notification comes and it records in firestore. I decided to ask you as a last resort. What can I do about this issue?

Firebase Admin C#

```javascript var message = new MulticastMessage() { Tokens = registrationTokens, Android = new AndroidConfig() { Priority = Priority.High }, Apns = new ApnsConfig() { Headers = new Dictionary { ["apns-priority"] = "10", ["apns-push-type"] = "background", ["apns-topic"] = "com.sabisv1" }, Aps = new Aps() { ContentAvailable = true, MutableContent = true, Badge = 1, Sound = "default" }, }, Data = new Dictionary() { ["title"] = request.Title, ["body"] = request.Body, ["ID"] = request.ID, ["notificationType"] = request.NotificationType.ToString(), }, }; ```

package.json

```javascript { "name": "sabisv1", "version": "0.0.1", "private": true, "scripts": { "android": "react-native run-android", "ios": "react-native run-ios", "lint": "eslint .", "start": "react-native start", "test": "jest", "check-dependencies": "rnx-align-deps", "fix-dependencies": "rnx-align-deps --write", "postinstall": "npx patch-package" }, "dependencies": { "react": "18.2.0", "react-native": "0.73.0", "@freakycoder/react-native-bounceable": "^1.0.3", "@notifee/react-native": "^7.8.0", "@react-native-async-storage/async-storage": "^1.17.11", "@react-native-community/datetimepicker": "^6.4.2", "@react-native-firebase/app": "^18.7.1", "@react-native-firebase/auth": "^18.7.1", "@react-native-firebase/firestore": "^18.7.1", "@react-native-firebase/messaging": "^18.7.1", "@react-native-firebase/remote-config": "^18.7.1", "@react-native-masked-view/masked-view": "^0.2.7", "@react-navigation/bottom-tabs": "^6.0.9", "@react-navigation/material-top-tabs": "^6.0.6", "@react-navigation/native": "^6.0.8", "@react-navigation/native-stack": "^6.0.2", "@react-navigation/stack": "^6.2.0", "@types/lodash": "^4.14.194", "apisauce": "2.1.6", "babel-plugin-transform-remove-console": "^6.9.4", "clsx": "^1.2.1", "dayjs": "^1.11.7", "geolib": "^3.3.3", "i18n-js": "3.8.0", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", "mobx": "6.6.2", "mobx-react-lite": "3.4.0", "mobx-state-tree": "5.1.6", "nativewind": "^2.0.11", "react-native-accordion-list-view": "^2.0.1", "react-native-alert-notification": "^0.3.4", "react-native-animatable": "^1.3.3", "react-native-app-auth": "^7.1.0", "react-native-calendar-strip": "^2.2.5", "react-native-collapsible": "^1.6.0", "react-native-config": "^1.5.1", "react-native-device-info": "^10.3.0", "react-native-dropdown-picker": "^5.3.0", "react-native-dynamic-search-bar": "^2.0.2", "react-native-encrypted-storage": "^4.0.3", "react-native-fast-image": "^8.6.3", "react-native-geolocation-service": "^5.3.1", "react-native-gesture-handler": "^2.12.1", "react-native-inappbrowser-reborn": "^3.7.0", "react-native-indicators": "^0.17.0", "react-native-keychain": "8.1.1", "react-native-linear-gradient": "^2.6.2", "react-native-localization": "^2.3.1", "react-native-maps": "^1.7.1", "react-native-maps-directions": "^1.9.0", "react-native-pager-view": "^6.1.4", "react-native-paper": "^4.10.1", "react-native-reanimated": "^3.5.2", "react-native-safe-area-context": "^4.4.1", "react-native-safe-area-view": "^1.1.1", "react-native-screens": "^3.19.0", "react-native-shared-element": "0.8.4", "react-native-spinkit": "^1.5.1", "react-native-tab-view": "^3.1.1", "react-native-table-component": "^1.2.2", "react-native-vector-icons": "^9.2.0", "react-native-webview": "^11.23.0", "react-native-youtube-iframe": "^2.2.2", "react-navigation-shared-element": "^3.1.3", "rn-sliding-up-panel": "^2.4.6" }, "devDependencies": { "@babel/core": "^7.20.0", "@babel/preset-env": "^7.20.0", "@babel/runtime": "^7.20.0", "@react-native/babel-preset": "^0.73.18", "@react-native/eslint-config": "^0.73.1", "@react-native/metro-config": "^0.73.2", "@react-native/typescript-config": "^0.73.1", "@types/react": "^18.2.6", "@types/react-test-renderer": "^18.0.0", "babel-jest": "^29.6.3", "eslint": "^8.19.0", "jest": "^29.6.3", "prettier": "2.8.8", "react-test-renderer": "18.2.0", "typescript": "5.0.4", "@rnx-kit/align-deps": "^2.2.2", "@types/i18n-js": "3.8.3", "@types/metro-config": "^0.76.3", "@types/react-native": "~0.70.6", "@types/react-native-indicators": "^0.16.2", "@types/react-native-vector-icons": "^6.4.13", "@typescript-eslint/eslint-plugin": "^5.17.0", "@typescript-eslint/parser": "^5.17.0", "tailwindcss": "3.3.2" }, "engines": { "node": ">=18" }, "rnx-kit": { "kitType": "app", "alignDeps": { "requirements": [ "react-native@0.71" ], "capabilities": [ "animation", "babel-preset-react-native", "core", "core-android", "core-ios", "datetime-picker", "gestures", "jest", "navigation/native", "navigation/stack", "react", "react-dom", "react-test-renderer", "safe-area", "screens", "storage" ] } } } ```

podfile

```javascript # Resolve react_native_pods.rb with node to allow for hoisting require Pod::Executable.execute_command('node', ['-p', 'require.resolve( "react-native/scripts/react_native_pods.rb", {paths: [process.argv[1]]}, )', __dir__]).strip platform :ios, min_ios_version_supported prepare_react_native_project! linkage = ENV['USE_FRAMEWORKS'] if linkage != nil Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green use_frameworks! :linkage => linkage.to_sym end target 'sabisv1' do config = use_native_modules! use_frameworks! :linkage => :static $RNFirebaseAsStaticFramework = true pod 'Firebase' pod 'FirebaseCore' pod 'FirebaseCoreInternal' pod 'GoogleUtilities' pod 'FirebaseCoreExtension' pod 'FirebaseFirestoreInternal' pod 'FirebaseABTesting' pod 'FirebaseInstallations' pod 'FirebaseFirestore' pod 'FirebaseMessaging' pod 'FirebaseRemoteConfig' pod 'FirebaseAuth' pod 'react-native-config', :path => '../node_modules/react-native-config' pod 'react-native-config/Extension', :path => '../node_modules/react-native-config' # react-native-maps dependencies pod 'react-native-maps', path: '../node_modules/react-native-maps' pod 'react-native-google-maps', path: '../node_modules/react-native-maps' pod 'GoogleMaps' pod 'Google-Maps-iOS-Utils' use_react_native!( :path => config[:reactNativePath], # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/.." ) $FirebaseSDKVersion = '10.17.0' # for Firebase target 'sabisv1Tests' do inherit! :complete # Pods for testing end post_install do |installer| # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( installer, config[:reactNativePath], :mac_catalyst_enabled => false ) end end ```

info.plist

```javascript BGTaskSchedulerPermittedIdentifiers $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleDevelopmentRegion en CFBundleDisplayName SABİS CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType APPL CFBundleShortVersionString $(MARKETING_VERSION) CFBundleSignature ???? CFBundleURLTypes CFBundleURLName com.sabisv1 CFBundleURLSchemes com.sabisv1 CFBundleVersion $(CURRENT_PROJECT_VERSION) FirebaseAppDelegateProxyEnabled NSLocationAlwaysAndWhenInUseUsageDescription We use our university API services in order to provide accurate information to our university students. LSRequiresIPhoneOS NSAppTransportSecurity NSAllowsArbitraryLoads NSAllowsLocalNetworking NSLocationWhenInUseUsageDescription You will get zero benefit allowing this app accessing your location. You should never see this alert. If you do alert technical support at destek@sabis.sakarya.edu.tr UIAppFonts Muli-ExtraBold.ttf Muli-Regular.ttf Muli-Black.ttf Muli-BlackItalic.ttf Muli-Bold.ttf Muli-BoldItalic.ttf Muli-ExtraBoldItalic.ttf Muli-ExtraLight.ttf Muli-ExtraLightItalic.ttf Muli-Italic.ttf Muli-Light.ttf Muli-LightItalic.ttf Muli-SemiBold.ttf Muli-SemiBoldItalic.ttf AntDesign.ttf Entypo.ttf EvilIcons.ttf Feather.ttf FontAwesome.ttf FontAwesome5_Brands.ttf FontAwesome5_Regular.ttf FontAwesome5_Solid.ttf Fontisto.ttf Foundation.ttf Ionicons.ttf MaterialCommunityIcons.ttf MaterialIcons.ttf Octicons.ttf SimpleLineIcons.ttf Zocial.ttf UIBackgroundModes fetch processing remote-notification UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities armv7 UIRequiresFullScreen UIStatusBarStyle UIStatusBarStyleDefault UIUserInterfaceStyle Light UIViewControllerBasedStatusBarAppearance ```

appdelegate.m

```javascript // #import "AppDelegate.h" #import // for Firebase #import #import //v #import "RNCConfig.h" #import "RNFBMessagingModule.h" @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [FIRApp configure]; //for Firebase [GMSServices provideAPIKey:@"AIzaSyDooFj9tsiEjseKeo544X7Xs7z5ca89P_Q"]; self.moduleName = @"sabisv1"; self.initialProps = [RNFBMessagingModule addCustomPropsToUserProps:nil withLaunchOptions:launchOptions]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } -(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken: (NSData *)deviceToken { [FIRMessaging messaging].APNSToken = deviceToken; NSString *fcmToken = [FIRMessaging messaging].FCMToken; NSLog(@"++APNST deviceToken : %@", deviceToken); NSLog(@"++FCM device token : %@", fcmToken); } - (BOOL) application: (UIApplication *)application openURL: (NSURL *)url options: (NSDictionary *) options { if ([self.authorizationFlowManagerDelegate resumeExternalUserAgentFlowWithURL:url]) { return YES; } return [RCTLinkingManager application:application openURL:url options:options]; } - (BOOL) application: (UIApplication *) application continueUserActivity: (nonnull NSUserActivity *)userActivity restorationHandler: (nonnull void (^)(NSArray> * _Nullable))restorationHandler { if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) { if (self.authorizationFlowManagerDelegate) { BOOL resumableAuth = [self.authorizationFlowManagerDelegate resumeExternalUserAgentFlowWithURL:userActivity.webpageURL]; if (resumableAuth) { return YES; } } } return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { return [self getBundleURL]; } - (NSURL *)getBundleURL { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } @end ```

index.js

```javascript import {AppRegistry} from 'react-native'; import App from './app/app.tsx'; import {name as appName} from './app.json'; import messaging from '@react-native-firebase/messaging'; import NotifeeManager from './app/components/notification-manager/notifee-manager'; import notifee from '@notifee/react-native'; import firestoreManager from './app/components/notification-manager/firestore-manager'; function HeadlessCheck({isHeadless}) { if (isHeadless) { return null; } return ; } notifee.onBackgroundEvent(async ({type, detail}) => { const {notification, pressAction} = detail; console.log('NOTIFEE BACKGROUNDEVENT', type); }); messaging().setBackgroundMessageHandler(async remoteMessage => { console.log('FIREBASE BACKGROUND MESSAGE'); firestoreManager.saveNotificationLogToFirestore( 'FIREBASE BACKGROUND MESSAGE', JSON.stringify(remoteMessage), ); await NotifeeManager.onMessageReceived(remoteMessage); }); AppRegistry.registerComponent(appName, () => HeadlessCheck); ```

mikehardy commented 8 months ago

Use the REST apis for FCM to post specific JSON to FCM with your device token so that you have control over the message

Make sure content-available is set to true in that JSON

Plug in the device and watch Console.app logs of the device when you send the message, it should show you the FCM JSON landing on the device via APNS

It will likely tell you why it is deciding not to deliver it to the app - usually something about power management and deciding that it doesn't want to waste the battery power of the device by letting your app run, since "content-available" messages are the only way to deliver things silently in the background and they are designed to be just to pre-cache content

The only reliable way to get FCM delivered is to have a notification block. data-only messages will always see hit or miss behavior like this. It likely works in your development setup because you use the app so often the device thinks it is worthy of spending battery power on a well-used app, but the TestFlight version doesn't have that history (and neither will your normal users) so it doesn't deliver to app

alperenkarip commented 8 months ago

@mikehardy Thank you for your time, I tried again with fcm rest api as you said, but the result did not change. When I send the "notification" feature to the data I send, I get a notification, but when I remove it, unfortunately, the notification does not appear. Also, thinking that Testflight does not give priority to it, I tried again by building in release mode and publishing the application, the result is the same :(.

In kill state, you get a notification, but I don't think Notifee is working, firebase is sending notifications.

{
  "priority":"HIGH",
   "content_available": true,
   "mutable_content":true,
   "notification":{
   "message":"s",
   "title":"s",
   "body":"s"
   },
  "data":{
   "title":"xcTitlex",
    "body":"Body",
    "Message":"w",
    "NotificationType":2,
    "GivenName":"ALPEREN",
    "FamilyName":"KARİP",
    "ID":"2"
  },
  "to":"f6qTr9dl305vjPH2GT8LUx:APA91bGL_V-zKx2OBvSrkwZwVBHNzpqnBr9A2AgYxe5g4JfrmNKry9LcKm0lFzmte4iIeakesSPUV0MB_d7FlnGUIP7ripGc_Uz4dyZ7PI0QgFS-_KQRVRFpcc30ah5yo-rdyiGvaLlG"
}

Notifications only come if the application is in the background or in the foreground, but not in kill state.

{
  "priority":"HIGH",
   "content_available": true,
   "mutable_content":true,
  "data":{
   "title":"xcTitlex",
    "body":"Body",
    "Message":"w",
    "NotificationType":2,
    "GivenName":"ALPEREN",
    "FamilyName":"KARİP",
    "ID":"2"
  },
  "to":"f6qTr9dl305vjPH2GT8LUx:APA91bGL_V-zKx2OBvSrkwZwVBHNzpqnBr9A2AgYxe5g4JfrmNKry9LcKm0lFzmte4iIeakesSPUV0MB_d7FlnGUIP7ripGc_Uz4dyZ7PI0QgFS-_KQRVRFpcc30ah5yo-rdyiGvaLlG"
}

RESULT { "multicast_id": 5178755344861181995, "success": 1, "failure": 0, "canonical_ids": 0, "results": [ { "message_id": "1702333746779856" } ] }

mikehardy commented 8 months ago

These two statements are the most important:

Plug in the device and watch Console.app logs of the device when you send the message, it should show you the FCM JSON landing on the device via APNS

and

The only reliable way to get FCM delivered is to have a notification block.

This is by Apple design. It is not in our control or your control

mikehardy commented 8 months ago

It appears based on commentary that the issue is more of a "wish", as in "I have this business requirement that is hard to meet based on operating system constraints and I wish it were possible..."

Unfortunately that doesn't make it actionable for the reasons I've laid out, and like all the other issues (about one a week on average...) that come in looking for reliable iOS deliver of data-only notifications, this is not actionable