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

πŸ”₯ iOS Background Notifications Not Being Displayed #3805

Closed dooleyb1 closed 4 years ago

dooleyb1 commented 4 years ago

Issue

I am currently performing a version upgrade from "react-native-firebase": "^5.5.5" to the latest libraries following the migration documentation (https://rnfirebase.io/migrating-to-v6). I have successfully migrated and tested all other Firebase libraries that my application uses however, I am having an issue with background notifications not being displayed on iOS devices using the messaging() library. Foreground notifications work as expected on both Android & iOS and background notifications also work on Android as expected.

Firstly, when my app is initialised, the following code is ran:

// Check if user has Push Notifications enabled
  async checkPushNotificationPermission() {
    crashlytics().log('[Initialising][checkPushNotificationPermission] - Checking users push notification permissions...');

    return new Promise(async (resolve, reject) => {
      try {

        let registerResult = await messaging().registerDeviceForRemoteMessages();

        const authorizationStatus = await messaging().requestPermission();

        if (authorizationStatus === messaging.AuthorizationStatus.AUTHORIZED) {
          console.log('User has notification permissions enabled.');
        } else if (authorizationStatus === messaging.AuthorizationStatus.PROVISIONAL) {
          console.log('User has provisional notification permissions.');
        } else {
          console.log('User has notification permissions disabled');
        }

        crashlytics().log(`[Initialising][checkPushNotificationPermission] - authorizationStatus == ${authorizationStatus}`);
        resolve();
      } catch (error) {
        console.error(error);
      }
    })
  }

And following this the Firebase messaging listeners are created. In particular the background listener below is not being triggered:

messaging().setBackgroundMessageHandler(async remoteMessage => {
    console.log('Message handled in the background!', remoteMessage);
   //showAlert('Background Notification', 'Background Notification');
});

However, the foreground listener such as below fires as expected when the app is running in the foregroud:

messaging().onMessage((remoteMessage) => {
    console.log(`messaging.onMessage`);
    console.log(remoteMessage);
    //showAlert('messaging.onMessage', 'messaging.onMessage');
});

Happy to provide any more necessary/useful debugging information upon request.

Note: I have read through the following issues and applied many of the suggested possible solutions and have yet to come across one that works for me to fix this issue.


Project Files

Javascript

Click To Expand

#### `package.json`: ```json { "name": "bounceapp", "version": "31.0.0", "private": true, "devDependencies": { "babel-jest": "24.9.0", "babel-plugin-module-resolver": "^3.2.0", "fs-extra": "^6.0.1", "jest": "^24.9.0", "metro-react-native-babel-preset": "0.56.0", "react-test-renderer": "16.9.0", "replace-in-file": "^3.4.2" }, "scripts": { "androidOld": "react-native run-android", "android": "npx jetify && npx react-native run-android", "ios": "react-native run-ios --simulator=\"iPhone 11\"", "ios-7": "react-native run-ios --simulator=\"iPhone 7\"", "ios-6s": "react-native run-ios --simulator=\"iPhone 6s\"", "ios-5": "react-native run-ios --simulator=\"iPhone 5s\"", "ios-X": "react-native run-ios --simulator=\"iPhone X\"", "ios-release": "react-native run-ios --configuration Release --simulator=\"iPhone X\"", "ios-staging": "react-native run-ios --configuration Staging --simulator=\"iPhone X\"", "brandons-phone": "react-native run-ios --configuration Debug --device \"brandons-phone\"", "apk": "cd android && ./gradlew assembleRelease", "android-build": "cd android && ./gradlew bundleRelease", "start": "react-native start", "hard-reinstall": "rm -rf node_modules && rm -rf ios/Pods && rm -f ios/Podfile.lock && rm -rf ios/build && npm install && cd ios && pod install && cd .." }, "jest": { "preset": "react-native" }, "dependencies": { "@freakycoder/react-native-helpers": "^0.1.0", "@react-native-community/datetimepicker": "^2.1.0", "@react-native-community/netinfo": "^5.3.0", "@react-native-firebase/app": "^7.2.1", "@react-native-firebase/auth": "^8.0.5", "@react-native-firebase/crashlytics": "^7.1.5", "@react-native-firebase/messaging": "^7.1.5", "@react-native-firebase/remote-config": "^7.1.4", "@rimiti/react-native-toastify": "^1.2.0", "axios": "^0.18.0", "jail-monkey": "^2.3.2", "jetifier": "^1.6.3", "lodash": "^4.17.11", "lottie-ios": "^3.1.3", "lottie-react-native": "^3.3.2", "moment": "^2.24.0", "native-base": "^2.12.1", "prop-types": "^15.7.2", "react": "^16.9.0", "react-native": "^0.61.0", "react-native-animatable": "^1.3.3", "react-native-app-intro-slider": "^3.0.0", "react-native-camera": "^3.16.0", "react-native-code-push": "^6.0.0", "react-native-collapsible": "^1.5.1", "react-native-country-picker-modal": "^1.10.0", "react-native-device-info": "^5.4.3", "react-native-drag-sort": "^1.3.6", "react-native-elements": "^1.2.7", "react-native-fs": "^2.16.6", "react-native-gesture-handler": "^1.5.3", "react-native-gradient-header": "^0.2.0", "react-native-image-picker": "^2.3.1", "react-native-keyboard-aware-scroll-view": "^0.9.1", "react-native-linear-gradient": "^2.5.6", "react-native-modal": "^11.5.6", "react-native-modal-datetime-picker": "^8.1.1", "react-native-navigation": "4.6.0", "react-native-permissions": "^2.0.9", "react-native-phone-input": "^0.2.4", "react-native-picker-select": "^6.3.3", "react-native-progress": "git+https://github.com/oblador/react-native-progress.git", "react-native-qrcode-scanner": "^1.3.1", "react-native-qrcode-svg": "^6.0.3", "react-native-segmented-control-tab": "^3.4.1", "react-native-snap-carousel": "^3.8.4", "react-native-ssl-pinning": "^1.4.1", "react-native-svg": "^12.0.3", "react-native-swipe-gestures": "git+https://github.com/thegamenicorus/react-native-swipe-gestures.git", "react-native-switch-toggle": "^1.1.0", "react-native-toast-native": "git+https://github.com/onemolegames/react-native-toast-native", "react-native-vector-icons": "^6.6.0", "react-native-zoom-image": "^0.1.2", "react-redux": "^6.0.0", "redux": "^4.0.1", "redux-thunk": "^2.3.0" } } ``` #### `firebase.json` for react-native-firebase v6: ```json { "react-native": { "crashlytics_ndk_enabled": true, "crashlytics_debug_enabled": true, "crashlytics_disable_auto_disabler": false, "crashlytics_auto_collection_enabled": true, "messaging_ios_auto_register_for_remote_messages": true } } ```

iOS

Click To Expand

#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like: ```ruby # Uncomment the next line to define a global platform for your project platform :ios, '9.0' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' target 'bounceapp' do # Uncomment the next line if you're using Swift or would like to use dynamic frameworks # use_frameworks! # React Native v0.60 pods pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector" pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec" pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired" pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety" pod 'React', :path => '../node_modules/react-native/' pod 'React-Core', :path => '../node_modules/react-native/' pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules' pod 'React-Core/DevSupport', :path => '../node_modules/react-native/' pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS' pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation' pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob' pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image' pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS' pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network' pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings' pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text' pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration' pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/' pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact' pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi' pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor' pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector' pod 'ReactCommon/jscallinvoker', :path => "../node_modules/react-native/ReactCommon" pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon" pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga' pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' # Pods for bounceapp pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons' pod 'Fabric', '~> 1.10.2' permissions_path = '../node_modules/react-native-permissions/ios' pod 'Permission-Camera', :path => "#{permissions_path}/Camera.podspec" pod 'Permission-Notifications', :path => "#{permissions_path}/Notifications.podspec" use_native_modules! pod 'jail-monkey', :path => '../node_modules/jail-monkey' pod 'react-native-image-picker', :path => '../node_modules/react-native-image-picker' post_install do |installer| installer.pods_project.targets.each do |target| # The following is needed to ensure the "archive" step works in XCode. # It removes React & Yoga from the Pods project, as it is already included in the main project. # Without this, you'd see errors when you archive like: # "Multiple commands produce ... libReact.a" # "Multiple commands produce ... libyoga.a" targets_to_ignore = %w(React yoga) if targets_to_ignore.include? target.name target.remove_from_project end end end end ``` #### `AppDelegate.m`: ```objc /** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #import "AppDelegate.h" #import #import #import #import #import #import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [FIRApp configure]; [[UNUserNotificationCenter currentNotificationCenter] setDelegate:self]; NSURL *jsCodeLocation; #ifdef DEBUG jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; #else jsCodeLocation = [CodePush bundleURL]; #endif [ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions]; return YES; } @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 10.15.1 CPU: (8) x64 Intel(R) Core(TM) i7-8557U CPU @ 1.70GHz Memory: 51.00 MB / 8.00 GB Shell: 5.7.1 - /bin/zsh Binaries: Node: 12.13.1 - /usr/local/bin/node Yarn: 1.19.2 - /usr/local/bin/yarn npm: 6.12.1 - /usr/local/bin/npm SDKs: iOS SDK: Platforms: iOS 13.2, DriverKit 19.0, macOS 10.15, tvOS 13.2, watchOS 6.1 Android SDK: API Levels: 23, 26, 28, 29 Build Tools: 28.0.3, 29.0.2 System Images: android-29 | Google APIs Intel x86 Atom, android-29 | Google Play Intel x86 Atom IDEs: Android Studio: 3.5 AI-191.8026.42.35.5791312 Xcode: 11.3/11C29 - /usr/bin/xcodebuild npmPackages: react: ^16.9.0 => 16.13.1 react-native: ^0.61.0 => 0.61.5 npmGlobalPackages: react-native-cli: 2.0.1 ``` - **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:** - `See package.json for submodule version numbers` - **`Firebase` module(s) you're using that has the issue:** - `Messaging` - **Are you using `TypeScript`?** - `No`


mikehardy commented 4 years ago

background listener needs to be set as early in init as possible, like in index.js or App.js, it must be before you use AppRegistry to add the app view etc - if I understand your provided information correctly, it may not be in the correct place in app init https://rnfirebase.io/messaging/usage#background--quit-state-messages

petetastic commented 4 years ago

I'm getting the same exact issue. Foreground notifications work, but background notifications don't do anything (I receive the notification, but clicking on it launches the app without doing anything). I'm working on a real device and have put my handler as early as possible, in index.js like this:

import {AppRegistry} from 'react-native';
import messaging from '@react-native-firebase/messaging';
import App from './App';
import {name as appName} from './app.json';

// Register background handler
messaging().setBackgroundMessageHandler(async remoteMessage => {
  console.log('Message handled in the background!', remoteMessage);
});

AppRegistry.registerComponent(appName, () => App);

When I click on the notification, I get this from XCode:

nw_read_request_report [C1] Receive failed with error "Software caused connection abort"
nw_read_request_report [C1] Receive failed with error "Software caused connection abort"
Connection 2: received failure notification
nw_flow_add_write_request [C2 192.168.86.242:8081 failed channel-flow (satisfied (Path is satisfied), interface: en0, ipv4, dns)] cannot accept write requests
nw_write_request_report [C2] Send failed with error "Socket is not connected"
mikehardy commented 4 years ago

In my experience with messaging, no one has the exact same issue. My eyes glaze over now when I see that. I literally stop reading. Make no assumptions about your issue if you want to fix it, because every issue seems to be some unique failure mode.

Perhaps this is a difference between direct channel and APNs, see my comment here https://github.com/invertase/react-native-firebase/issues/3530#issuecomment-646837346

ferocedev commented 4 years ago

Me, too. It does not work on background only on ios. It works when you run the app.

스크란샷 2020-06-22 α„‹α…©α„Œα…₯ᆫ 10 18 02

--- package.json "@react-native-community/push-notification-ios": "1.2.2", "@react-native-firebase/app": "7.2.1", "@react-native-firebase/messaging": "7.1.5", "react": "16.11.0", "react-native": "0.62.2", "react-native-push-notification": "3.5.2"

ErickArciniega commented 4 years ago

any update?

dooleyb1 commented 4 years ago

Update:

@mikehardy You are correct, I had initialised my background listeners after the AppRegistry of views etc. I have moved this to the earliest point possible in the initialisation of my application and it seems to not have changed much.

I also added the code suggested by @samparmenter here ([FIRMessaging messaging].autoInitEnabled = YES;) to my AppDelegate.m file which didn't seem to help also.

The code I am using to send the message to device is using the Firebase Admin Node.JS SDK and is as follows:

  let fcmToken = userData['fcmToken'];

  console.log(`Sending message to users device...`);

  // See - https://firebase.google.com/docs/reference/admin/node/admin.messaging.Messaging#send
  let messagingConditionResponse = await messaging.sendToDevice(
    // Device registration token (fcmToken)
    fcmToken,
    // MessagingPayload
    {
      data: {
        "Test data key": "Test data value"
      },
      notification: {
        title: "New Rewards Available!",
        body: "Check out the Shop in-app to see our new rewards!"
      }
    },
    // MessagingOptions
    {
      contentAvailable: true,
      dryRun: false,
      mutableContent: false,
      priority: "high"
    }
  );

  console.log(`Finished sending message...`);
  console.log(messagingConditionResponse);

This message is being received in the foreground no problem however nothing is being displayed whilst in the background. When I bring the app to the foreground the onMessage handler then fires picking up the message.

I have been running on device (iPhone 7) and the app does not produce any output to the XCode console whilst running in the background or quit.

nicoceledon commented 4 years ago

Just to clear something, you mean that that the notification doesn't show on the notification area on iOS?

dooleyb1 commented 4 years ago

Just to clear something, you mean that that the notification doesn't show on the notification area on iOS?

The notification is not displayed i.e on the top of the mobile screen when any notification is received. This happens in both the background and quit state.

nicoceledon commented 4 years ago

Do you have an apple developers license?

Wmeng98 commented 4 years ago

I'm having a similar issue. I can receive foreground notifications via onMessage handler and my background callback handler is also called on background messages via setBackgroundMessageHandler.

However, the notification banner isn't being shown nor does it show up in the notification tray on IOS. Everything works fine on Android as well :).

If both my foreground/background handlers are called as expected, I must've setup IOS cloud messaging correctly to some extent.

Really loving this module so far though!

mikehardy commented 4 years ago

@Wmeng98 I believe that is functioning as the upstream SDKs define it should function. For local notification handling on foreground you need a local notification package. Those are related but both enormous sets of functionality largely separate from react-native-firebase so this module doesn't cover them.

Wmeng98 commented 4 years ago

@Wmeng98 I believe that is functioning as the upstream SDKs define it should function. For local notification handling on foreground you need a local notification package. Those are related but both enormous sets of functionality largely separate from react-native-firebase so this module doesn't cover them.

Thanks for the quick reply @mikehardy! Yep, I'm aware that local notification handling isn't covered by the module. My concerning issue is that a notification banner isn't shown when the the app is in the background. However, the setBackgroundMessageHandler is still called as expected. Which is kind of crazy.

I've also tested push notifications via curl to apns push server

curl -v  \
     -d '{"aps":{"alert":"hello", "badge":10}}' \
     -H "apns-topic: <bundle id>"  \
     -H "authorization: bearer  <JWT token>"  \
     --http2 \
https://api.sandbox.push.apple.com/3/device/<APNS device token>

Funny thing is I can see the badge update but no notification banner while in background state of the app. If the message handler is being called as expected, then the message must've been received by the app from APNS. Maybe I could add handlers in the AppDelegate.m to log and double check but any tips on debugging notification banner issues?

Thanks!

P.S My device is running iOS 13.5.1.

mikehardy commented 4 years ago

Maybe it is just a matter of setting the right APNS headers, a priority or critical or similar? I'm not good enough to know it off the top of my head but there are mentions of APNS headers related to notification displaying or not all over - should pop up in a search and might work with experimentation

Wmeng98 commented 4 years ago

Yea, adding the right APNS headers helped me get the setBackgroundMessageHandler callback working on app background.

After some more googling, It turns out I hadn't requested the right permissions using firebase.messaging().requestPermission(). I never bothered confirming that the proper permission settings were requested since the dialog never showed up and my message handlers were being called.

If you want to check authorization natively on IOS (objective-c), include the following in your AppDelegate.m

// Make sure to import UserNotifications
#import <UserNotifications/UserNotifications.h>

// Include inside didFinishLaunchingWithOptions
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert)
            completionHandler:^(BOOL granted, NSError * _Nullable error) {
                if (!error) {
                    NSLog(@"request succeeded!");
                }
            }];

Though this should be done via the requestPermission() method in firebase messaging. Maybe try to explicitly enable alert, badge, and sound settings? They are true by default. https://rnfirebase.io/messaging/ios-permissions

Thanks again for the quick feedback @mikehardy πŸ‘

mikehardy commented 4 years ago

I actually encourage everyone to use react-native-permissions - https://github.com/react-native-community/react-native-permissions/ - it is by far the best way to manage permissions IMHO - glad you got your messages through, I want to pass the credit for idea of the headers right along to @forbesgillikin I got the idea from there (but didn't have the link handy) and now there's even a PR #3882 - this stuff is just plain difficult, but we're getting through it together. Cheers

Shweta-Porwal commented 4 years ago

I tried the options suggested by Wmeng98 but still no luck. I am able to get notifications in foreground but it is not working in background. One more observation : I kept the app in background, send a notification , now when I open the app onMessage() handler is getting called which is not expected as my app was in background. I tried this with two different version of react-native-firebase/messaging. 6.4.0-rc4 and 7.3.2. With both versions I am getting same behaviour. It would be really helpful if someone let me know what I am missing here to add background notification for iOS ?

Wmeng98 commented 4 years ago

You added the proper apns headers to your message as suggested above @Shweta-Porwal? How are you sending push notifications? FCM console? Admin SDK?

Shweta-Porwal commented 4 years ago

I am sending it from FCM console. I believe from FCM console we don't have to write any header, right ? Also do we need to add react-native-push-notification framework for this to work?

On Tue, Jul 7, 2020 at 8:11 AM Wilson Meng notifications@github.com wrote:

You added the proper apns headers to your message as suggested above @Shweta-Porwal https://github.com/Shweta-Porwal? How are you sending push notifications? FCM console? Admin SDK?

β€” You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/invertase/react-native-firebase/issues/3805#issuecomment-654564265, or unsubscribe https://github.com/notifications/unsubscribe-auth/AM3FEDNJ5LXNV77QM77GOZTR2KDMTANCNFSM4OCYWU2Q .

mikehardy commented 4 years ago

The only way to properly test cloud messaging is to craft the JSON yourself and send it via firebase-admin, it's not that with the console "we don't have to write any header", it's that with the console you don't even get the chance, so you don't stand a chance of getting things exactly right

Shweta-Porwal commented 4 years ago

{ "to" : "myDeviceFCMToken", "notification" : { "body" : "Test Message", "title" : "React Native Firebase", "content_available" : true, "priority" : "high" }, "data" : { "body" : "Test Message", "title" : "React Native Firebase", "content_available" : true, "priority" : "high" }, "aps" : { "content_available" : true } } This is the message body which I am sending from postman and getting notifications on my app when app is in foreground. But nothing comes when app is in background.

mikehardy commented 4 years ago

@Shweta-Porwal read the comments above carefully, confirm you have the permission granted per above and follow the link to the PR for the documentation and add the appropriate headers - there is already info in this thread that seems important and is not included in the JSON you posted, which makes me think this will be an inefficient use of my time helping troubleshoot - don't do that or I will not pay attention going forward

forbesgillikin commented 4 years ago

Your request is malformed and does not include the APNs headers that are necessary to push data-only messages in the background on iOS. Your request should look like

{
"to" : //device fcm token,
"notification" : {
"data" : {
"body" : "Test Message",
"title" : "React Native Firebase",
},
"aps" : {
"content_available" : 1
}
}

With

"apns-push-type": "background",
"apns-priority": "5",
"apns-topic": // your app bundle identifier

As headers in the request.

Using Postman will require a different json block than what is described in the RNFirebase docs. Either use the Node Admin SDK and use the RNFirebase documentation or become familiar with the FCM and APNs documentation

harrydema commented 4 years ago

I am gettting crazy to make it work on background or quit mode on IOS. The notifications do work on foreground.

Android notifications work perfect.

This is my code:

AppDelegate.m

#import "AppDelegate.h"

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>

#import <Firebase.h>

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
{
  if ([FIRApp defaultApp] == nil) {
    [FIRApp configure];
  }
  [FIRMessaging messaging].autoInitEnabled = YES;
  #ifdef FB_SONARKIT_ENABLED
    InitializeFlipper(application);
  #endif

  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"TestPN"
                                            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

index.js

/**
 * @format
 */

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
import messaging from '@react-native-firebase/messaging';
import notifee, { AndroidImportance } from '@notifee/react-native';

async function onMessageReceived(message) {
  await notifee.createChannel({
    id: 'default',
    name: 'Default',
    importance: AndroidImportance.HIGH,
  });

  notifee.displayNotification(JSON.parse(message.data.notifee));

  notifee.incrementBadgeCount();
}

notifee.setBadgeCount(0);

messaging().requestPermission();

if (!messaging().isDeviceRegisteredForRemoteMessages) {
  messaging().registerDeviceForRemoteMessages();
}

messaging().onMessage(onMessageReceived);
messaging().setBackgroundMessageHandler(onMessageReceived);

AppRegistry.registerComponent(appName, () => App);

App.js

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 *
 * @format
 * @flow strict-local
 */

import React, { useEffect } from 'react';
import messaging from '@react-native-firebase/messaging';
import {
  SafeAreaView,
  StyleSheet,
  ScrollView,
  View,
  Text,
  StatusBar,
} from 'react-native';
import Clipboard from '@react-native-community/clipboard'

import {
  Header,
  LearnMoreLinks,
  Colors,
  DebugInstructions,
  ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';

const App: () => React$Node = () => {
  useEffect(() => {
    // Get the device token
    messaging()
      .getToken()
      .then(token => {
        return Clipboard.setString(token);
      });

    // Listen to whether the token changes
    return messaging().onTokenRefresh(token => {
      return Clipboard.setString(token);
    });
  }, []);
  return (
    <>
      <StatusBar barStyle="dark-content" />
      <SafeAreaView>
        <ScrollView
          contentInsetAdjustmentBehavior="automatic"
          style={styles.scrollView}>
          <Header />
          {global.HermesInternal == null ? null : (
            <View style={styles.engine}>
              <Text style={styles.footer}>Engine: Hermes</Text>
            </View>
          )}
          <View style={styles.body}>
            <View style={styles.sectionContainer}>
              <Text style={styles.sectionTitle}>Step One</Text>
              <Text style={styles.sectionDescription}>
                Edit <Text style={styles.highlight}>App.js</Text> to change this
                screen and then come back to see your edits.
              </Text>
            </View>
            <View style={styles.sectionContainer}>
              <Text style={styles.sectionTitle}>See Your Changes</Text>
              <Text style={styles.sectionDescription}>
                <ReloadInstructions />
              </Text>
            </View>
            <View style={styles.sectionContainer}>
              <Text style={styles.sectionTitle}>Debug</Text>
              <Text style={styles.sectionDescription}>
                <DebugInstructions />
              </Text>
            </View>
            <View style={styles.sectionContainer}>
              <Text style={styles.sectionTitle}>Learn More</Text>
              <Text style={styles.sectionDescription}>
                Read the docs to discover what to do next:
              </Text>
            </View>
            <LearnMoreLinks />
          </View>
        </ScrollView>
      </SafeAreaView>
    </>
  );
};

const styles = StyleSheet.create({
  scrollView: {
    backgroundColor: Colors.lighter,
  },
  engine: {
    position: 'absolute',
    right: 0,
  },
  body: {
    backgroundColor: Colors.white,
  },
  sectionContainer: {
    marginTop: 32,
    paddingHorizontal: 24,
  },
  sectionTitle: {
    fontSize: 24,
    fontWeight: '600',
    color: Colors.black,
  },
  sectionDescription: {
    marginTop: 8,
    fontSize: 18,
    fontWeight: '400',
    color: Colors.dark,
  },
  highlight: {
    fontWeight: '700',
  },
  footer: {
    color: Colors.dark,
    fontSize: 12,
    fontWeight: '600',
    padding: 4,
    paddingRight: 12,
    textAlign: 'right',
  },
});

export default App;

Capabilities eanbled image

Postman request (Added all the content_available possibilities as I was. not sure which one. should I use)

{
    "to" : "dKxdtiyWmUDCqt-0m0GTmF:APA91bFYwxxNrrjOE1MxbCTHcl6hMu7h6eabECZBlSD_DpVufesvsw7BkToRJ_v9rJEmPxklAwOS4sdPujYQGXW92bQ9r8g4t-LrAYm5PbKBjjNLXX6CfT-mA3hRFkKrhcA11UxmMfDh",
    "priority": "high",
    "content_available": true,
    "content-available": true,
    "aps" : {
        "content_available" : 1
    },
    "data": {
        "notifee": {
          "title": "Test",
          "subtitle": "Test",
          "body": "This message was sent via FCM! 5",
          "android": {
            "channelId": "default",
            "importance": 4,
            "groupId": "1234"
          }
     }
  }
}

Permissions are enabled image

harrydema commented 4 years ago

I posted my problem in new ticket:

https://github.com/invertase/react-native-firebase/issues/3969

stale[bot] commented 4 years 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 the community's attention?

This issue will be closed in 15 days if no further activity occurs. Thank you for your contributions.

davx1992 commented 4 years ago

@mikehardy having same issue. Errors are thrown when app is backgrounded. When I send first message, it is not shown and this error is thrown. When next message is sent, previous notification is handled by background handler, so it is one message lag. When I open app latest message is handled. Strangest part here is, that when I enable Background location tracking - Expo Location package, and repeat the same, no error is thrown and each message is handled. Have googled everywhere but no luck to identify what is the problem.

Maybe you can suggest me something, were can I look or troubleshoot this. As of now receiving these errors: nw_read_request_report [C2] Receive failed with error "Software caused connection abort" Having these packages: "@react-native-firebase/app": "^8.4.0", "@react-native-firebase/iid": "^7.4.1", "@react-native-firebase/messaging": "^7.8.1",

forbesgillikin commented 4 years ago

@mikehardy having same issue. Errors are thrown when app is backgrounded. When I send first message, it is not shown and this error is thrown. When next message is sent, previous notification is handled by background handler, so it is one message lag. When I open app latest message is handled. Strangest part here is, that when I enable Background location tracking - Expo Location package, and repeat the same, no error is thrown and each message is handled. Have googled everywhere but no luck to identify what is the problem.

Maybe you can suggest me something, were can I look or troubleshoot this.

As of now receiving these errors: nw_read_request_report [C2] Receive failed with error "Software caused connection abort"

Having these packages:

    "@react-native-firebase/app": "^8.4.0",

    "@react-native-firebase/iid": "^7.4.1",

    "@react-native-firebase/messaging": "^7.8.1",

This error looks related to Xcode not Firebase.

https://stackoverflow.com/questions/43527115/xcode-error-connecting-to-simulator-software-caused-connection-abort

Also, iOS simulators will not handle notifications. They must be tested on a real device.

davx1992 commented 4 years ago

@forbesgillikin checked link u provided. As I see I have only one version Xcode, also done mac restart, so no success on this. Were running app on device, not simulator.

mikehardy commented 4 years ago

Maybe use this pull request for inspiration and add to the constant wait even more? https://github.com/invertase/react-native-firebase/commit/415dba496ddf0551019e1bcfea4080809c300980

davx1992 commented 4 years ago

@mikehardy @compojoom so make some investigation, and understood that issue is with this line:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) (6 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [[RNFBRCTEventEmitter shared] sendEventWithName:@"messaging_message_received_background" body:[RNFBMessagingSerializer remoteMessageUserInfoToDict:userInfo]]; });

when I take it out, and leave without dispatch_after it works. Also what I found out, that dispatch_after is not triggered on first message, after it is backgrounded. I am not skilled with Obj.c. maybe you have any idea, what could cause such behaviour?

mikehardy commented 4 years ago

I would like to keep information related to that line centralized on the issue I just cross-posted to. It is a specific issue. Other people seeing problems by just missing content-available etc are much simpler cases and handled already on this thread

stale[bot] commented 4 years 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 the community's attention?

This issue will be closed in 15 days if no further activity occurs. Thank you for your contributions.

stale[bot] commented 4 years ago

Closing this issue after a prolonged period of inactivity. If this is still present in the latest release, please feel free to create a new issue with up-to-date information.

codefreak13 commented 4 years ago

I got this working on iOS 13 and 14 by changing the p8 APN auth certificate on firebase console cloud messaging to p12 certificate generated from the Push Notifications feature under the app's provisioning profile in the apple developer account

furqan-fabtechsol commented 3 months ago

iOS Background Notifications Not Being Displayed in React Native App Description I'm currently developing a React Native application and using Firebase Cloud Messaging (FCM) to handle notifications. The notifications work perfectly when the app is in the foreground. However, when the app is in the background, notifications are received, but the data payload is not being delivered to the app.

Environment React Native Version: 0.73.6 Firebase Version: 20.3.0 (app), 20.3.0 (messaging) @notifee/react-native Version: 7.8.2 Xcode Version: (specify your version) iOS Version: (specify the iOS version you are testing on) Testing on Real Device: Yes Issue Details Foreground Notifications: Working as expected, receiving both notification and data payloads. Background Notifications: Notifications are received, but the data payload is not being delivered or handled by the app. Steps Taken AppDelegate Configuration: Configured as per the official documentation for handling notifications. Permissions and Background Modes: Enabled background modes and configured permissions as required. Testing: Verified on a real iOS device. Despite these steps, the data payload is not being delivered in the background. Any guidance or solutions to resolve this issue would be greatly appreciated.

mohamedshawky982 commented 3 months ago

For me when sending data-only notifications and the app is in the background it doesn't work on a simulator but works on Real Devices.

Satyam-code143 commented 2 months ago

Hi @furqan-fabtechsol , Have you found the solution?

Hi @mohamedshawky982 , Can you please share the data-only object body that you used to send from the backend? Also, the frontend changes that you made in index.js and if some changes made in Appdelegate.mm.

Thank you!

furqan-fabtechsol commented 2 months ago

Hi @furqan-fabtechsol , Have you found the solution?

Hi @mohamedshawky982 , Can you please share the data-only object body that you used to send from the backend? Also, the frontend changes that you made in index.js and if some changes made in Appdelegate.mm.

Thank you!

My payload is as given below

def send_multicast_message(data, offline_users):
    tokens = get_tokens(offline_users)
    print(tokens)
    title = data.get("type", "")
    if title not in ["ChatSeen", "ChatDelivered"]:
        content_available = True if title in ["MessageReact", "ChatMessage"] else False
        print(content_available,"--")
        data_str = json.dumps(data)
        message_data = {
            "data": data_str,  # Custom content_type for the message
            "priority": "high"  # Custom priority key
            }  # Convert the data to a JSON string
        apns_config = APNSConfig(
            # headers={
            # "apns-priority": "10",  # '10' for high priority, '5' for normal
            # "apns-expiration": "0"  # Immediate expiration if not delivered
            #     },
            payload=APNSPayload(
                aps=Aps(
                    content_available=content_available,
                    mutable_content=True,  # Allows modification on the device side
                ),
                custom_data=message_data,
            ),
        )
        if tokens:
            multicast_message = MulticastMessage(
                # notification=messaging.Notification(
                #     title="title",
                #     body="body",
                # ),
                tokens=tokens,  # List of device tokens
                data=message_data,
                apns=apns_config,
            )
            response = messaging.send_multicast(multicast_message)
            print(response)
        else:
            print("No tokens found")
    else:
        print("Message type ChatDelivered and ChatSeen does not require a notification")

Apdelegate.mm file is as given below

#import "AppDelegate.h"

#import <React/RCTBundleURLProvider.h>
#import <React/RCTLinkingManager.h>
#import <Firebase.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  self.moduleName = @"main";
 [FIRApp configure];
  // You can add your custom initial props in the dictionary below.
  // They will be passed down to the ViewController used by React Native.
  self.initialProps = @{};

  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
  return [self bundleURL];
}

- (NSURL *)bundleURL
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

// Linking API
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
}

// Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
  BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
  return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result;
}

// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
  return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}

// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
  return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
}

// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
  return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}

@end

Index.js File

import registerRootComponent from 'expo/build/launch/registerRootComponent';
import notifee, { EventType } from '@notifee/react-native';
import App from './App';
import { onDisplayNotification } from './app/modules/pushNotificationHelper';
import messaging from '@react-native-firebase/messaging';

// Set up background message handler
messaging().setBackgroundMessageHandler(async (remoteMessage) => {

  console.log('Message handled in the background! 1', typeof (remoteMessage.data.data));
  });

function HeadlessCheck({ isHeadless }) {
  if (isHeadless) {
    // App has been launched in the background by iOS, ignore
    return null;
  }

  return <App />;
}

registerRootComponent(App);

I still not found solution stuck on it for a long time "react-native": "0.74.1", "@react-native-firebase/app": "^20.3.0", "@react-native-firebase/messaging": "^20.3.0",

Satyam-code143 commented 2 months ago

Hi @furqan-fabtechsol ,

Thank you for the response!

Even I am in the same state. Without a solution.

Need to check with @mohamedshawky982 once. Hi @mohamedshawky982 , please help us with the configs.

DhruvBachani commented 2 months ago

+1

miladnikad commented 1 month ago

Guys, I found a solution. sendMulticast is deprecated and you should use sendEachForMulticast instead.

Satyam-code143 commented 1 month ago

Hi @miladnikad ,

I have been using the sendEachForMulticast itself. Can you share your message body? I would really want to know the structure of your data only message body.

Thanks!