Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App crashes on receiving a notification in the foreground, or when clicking a notification that appeared while the app is in the background #3447

Closed
3 of 7 tasks
samkotlove opened this issue Apr 11, 2020 · 15 comments · Fixed by #3701
Assignees
Labels
platform: ios plugin: messaging FCM only - ( messaging() ) - do not use for Notifications type: bug New bug report

Comments

@samkotlove
Copy link

Issue

When the app is in the foreground, it will crash on receiving a notification. When the app is in the background, it will receive the notification but when clicking it the app crashes. When running from xcode, the following error is shown:

2020-04-11 14:44:14.135 [info][tid:com.facebook.react.JavaScript] Subscribed to topic!
2020-04-11 14:44:15.869600-0400 MYAPP[7218:1839775] -[__NSCFNumber isEqualToString:]: unrecognized selector sent to instance 0xd2d54847f7952b78
2020-04-11 14:44:15.888693-0400 MYAPP[7218:1839775] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber isEqualToString:]: unrecognized selector sent to instance 0xd2d54847f7952b78'
libc++abi.dylib: terminating with uncaught exception of type NSException

and in crashlytics I see

Fatal Exception: NSInvalidArgumentException
0  CoreFoundation                 0x19e3a096c __exceptionPreprocess
1  libobjc.A.dylib                0x19e0b9028 objc_exception_throw
2  CoreFoundation                 0x19e29edcc -[NSOrderedSet initWithSet:copyItems:]
3  CoreFoundation                 0x19e3a5048 ___forwarding___
4  CoreFoundation                 0x19e3a73a0 _CF_forwarding_prep_0
5  MYAPP                          0x1027f6114 +[RNFBMessagingSerializer remoteMessageUserInfoToDict:] + 116 (RNFBMessagingSerializer.m:116)
6  MYAPP                          0x1027f3a54 -[RNFBMessagingUNUserNotificationCenter userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:] + 88 (RNFBMessaging+UNUserNotificationCenter.m:88)
7  MYAPP                          0x1027683b0 FCMSwizzleDidReceiveNotificationResponseWithHandler + 560 (FIRMessagingRemoteNotificationsProxy.m:560)
8  UIKitCore                      0x1a24b5dec -[UIApplication _handleNonLaunchSpecificActions:forScene:withTransitionContext:completion:]
9  UIKitCore                      0x1a2499790 -[UIApplication _callInitializationDelegatesWithActions:forCanvas:payload:fromOriginatingProcess:]
10 UIKitCore                      0x1a249f340 -[UIApplication _runWithMainScene:transitionContext:completion:]
11 UIKitCore                      0x1a1c37bec -[_UISceneLifecycleMultiplexer completeApplicationLaunchWithFBSScene:transitionContext:]
12 UIKitCore                      0x1a20e9518 _UIScenePerformActionsWithLifecycleActionMask
13 UIKitCore                      0x1a1c38724 __101-[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]_block_invoke
14 UIKitCore                      0x1a1c38154 -[_UISceneLifecycleMultiplexer _performBlock:withApplicationOfDeactivationReasons:fromReasons:]
15 UIKitCore                      0x1a1c38540 -[_UISceneLifecycleMultiplexer _evalTransitionToSettings:fromSettings:forceExit:withTransitionStore:]
16 UIKitCore                      0x1a1c37dc4 -[_UISceneLifecycleMultiplexer uiScene:transitionedFromState:withTransitionContext:]
17 UIKitCore                      0x1a1c3c544 __186-[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block_invoke_2
18 UIKitCore                      0x1a210339c ___UISceneSettingsDiffActionPerformChangesWithTransitionContext_block_invoke
19 UIKitCore                      0x1a20157bc +[BSAnimationSettings(UIKit) tryAnimatingWithSettings:actions:completion:]
20 UIKitCore                      0x1a2103354 _UISceneSettingsDiffActionPerformChangesWithTransitionContext
21 UIKitCore                      0x1a1c3c25c __186-[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]_block_invoke
22 UIKitCore                      0x1a210323c _UISceneSettingsDiffActionPerformActionsWithDelayForTransitionContext
23 UIKitCore                      0x1a1c3c0b8 -[_UIWindowSceneFBSSceneTransitionContextDrivenLifecycleSettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:]
24 UIKitCore                      0x1a1aa4434 __64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke
25 UIKitCore                      0x1a1aa2ef8 -[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:]
26 UIKitCore                      0x1a1aa4164 -[UIScene scene:didUpdateWithDiff:transitionContext:completion:]
27 UIKitCore                      0x1a249d730 -[UIApplication workspace:didCreateScene:withTransitionContext:completion:]
28 UIKitCore                      0x1a20376bc -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:]
29 FrontBoardServices             0x1a3595e10 -[FBSSceneImpl _callOutQueue_agent_didCreateWithTransitionContext:completion:]
30 FrontBoardServices             0x1a35bcaa0 __86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke.168
31 FrontBoardServices             0x1a35a0ef4 -[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:]
32 FrontBoardServices             0x1a35bc734 __86-[FBSWorkspaceScenesClient sceneID:createWithParameters:transitionContext:completion:]_block_invoke
33 libdispatch.dylib              0x19e045fd8 _dispatch_client_callout
34 libdispatch.dylib              0x19e048d1c _dispatch_block_invoke_direct
35 FrontBoardServices             0x1a35e3254 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__
36 FrontBoardServices             0x1a35e2f00 -[FBSSerialQueue _queue_performNextIfPossible]
37 FrontBoardServices             0x1a35e346c -[FBSSerialQueue _performNextFromRunLoopSource]
38 CoreFoundation                 0x19e31c108 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
39 CoreFoundation                 0x19e31c05c __CFRunLoopDoSource0
40 CoreFoundation                 0x19e31b7c8 __CFRunLoopDoSources0
41 CoreFoundation                 0x19e316694 __CFRunLoopRun
42 CoreFoundation                 0x19e315f40 CFRunLoopRunSpecific
43 GraphicsServices               0x1a85a6534 GSEventRunModal
44 UIKitCore                      0x1a24a1580 UIApplicationMain
45 MYAPP                          0x102721b4c main + 14 (main.m:14)
46 libdyld.dylib                  0x19e194e18 start

Based on the above stack trace, the error is being thrown from RNFBMessagingSerializer.m and specifically on line 116:

116: if (apsDict[@"mutable-content"] != nil && [apsDict[@"mutable-content"] isEqualToString:@"1"]) {
117:   message[@"mutableContent"] = @([RCTConvert BOOL:apsDict[@"mutable-content"]]);
118: }

When I comment out this check the app doesn't crash (the notifications dont show when the app is in the foreground but Im unable to determine if that's intended behavior). I've tried this on version 6.4.0 and 6.4.1-alpha.0, the problem happens on both.


Project Files

Javascript

Click To Expand

package.json:

{
  "name": "MYAPP",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "start": "react-native start",
    "adb": "adb reverse tcp:8081 tcp:8081"
  },
  "rnpm": {
    "assets": [
      "./src/fonts/"
    ]
  },
  "dependencies": {
    "@expo/react-native-action-sheet": "^3.2.0",
    "@react-native-community/async-storage": "github:react-native-community/async-storage",
    "@react-native-community/netinfo": "^4.4.0",
    "@react-native-firebase/app": "6.4.1-alpha.0",
    "@react-native-firebase/crashlytics": "6.4.1-alpha.0",
    "@react-native-firebase/messaging": "6.4.1-alpha.0",
    "@react-navigation/core": "^3.5.1",
    "amazon-cognito-identity-js": "^3.1.3",
    "aws-amplify": "^2.2.6",
    "luxon": "^1.21.3",
    "react": "16.8.1",
    "react-native": "0.61.4",
    "react-native-admob": "^2.0.0-beta.6",
    "react-native-admob-native-ads": "^0.1.1",
    "react-native-config": "^0.11.7",
    "react-native-device-info": "^4.0.1",
    "react-native-elements": "^1.2.6",
    "react-native-fs": "^2.16.1",
    "react-native-gesture-handler": "^1.5.2",
    "react-native-iap": "^4.0.7",
    "react-native-music-control": "^0.11.0",
    "react-native-screens": "^2.4.0",
    "react-native-sound": "^0.11.0",
    "react-native-svg": "^9.12.0",
    "react-native-unimodules": "^0.6.0",
    "react-native-vector-icons": "^6.6.0",
    "react-native-xml2js": "^1.0.3",
    "react-navigation": "^3.13.0",
    "react-redux": "^7.1.1",
    "redux": "^4.0.4",
    "superagent": "^5.2.2"
  },
  "devDependencies": {
    "@babel/core": "^7.5.0",
    "@babel/runtime": "^7.5.0",
    "@react-native-community/eslint-config": "^0.0.3",
    "babel-jest": "^24.1.0",
    "babel-preset-fbjs": "^3.3.0",
    "jest": "^24.1.0",
    "metro-react-native-babel-preset": "^0.51.1",
    "react-test-renderer": "16.8.1"
  },
  "jest": {
    "preset": "react-native"
  }
}

firebase.json for react-native-firebase v6:

{
  "react-native": {
    "crashlytics_debug_enabled": true
  }
}

iOS

Click To Expand

ios/Podfile:

  • I'm not using Pods
  • I'm using Pods and my Podfile looks like:
platform :ios, '9.0'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'

target 'MYAPP' do
  # Pods for MYAPP
  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'

  pod 'Google-Mobile-Ads-SDK'
  pod 'GoogleMobileAdsMediationFacebook'
  pod 'GoogleMobileAdsMediationMoPub'
  pod 'GoogleMobileAdsMediationChartboost'
  pod 'GoogleMobileAdsMediationAppLovin'
  pod 'GoogleMobileAdsMediationInMobi'
  pod 'InMobiSDK'

  pod 'RNFBApp', :path => '../node_modules/@react-native-firebase/app'

  pod 'RNFS', :path => '../node_modules/react-native-fs'

  pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-community/async-storage'

  pod 'react-native-music-control', :path => '../node_modules/react-native-music-control'

  pod 'react-native-admob-native-ads', :path => '../node_modules/react-native-admob-native-ads'

  pod 'RNFBCrashlytics', :path => '../node_modules/@react-native-firebase/crashlytics'

  pod 'RNFBMessaging', :path => '../node_modules/@react-native-firebase/messaging'

  post_install do |installer|
    installer.pods_project.targets.each do |target|
      if target.name == 'react-native-config'
        phase = target.project.new(Xcodeproj::Project::Object::PBXShellScriptBuildPhase)
        phase.shell_script = "cd ../../"\
                             " && RNC_ROOT=./node_modules/react-native-config/"\
                             " && export SYMROOT=$RNC_ROOT/ios/ReactNativeConfig"\
                             " && export BUILD_DIR=$RNC_ROOT/ios/ReactNativeConfig"\
                             " && ruby $RNC_ROOT/ios/ReactNativeConfig/BuildDotenvConfig.ruby"
  
        target.build_phases << phase
        target.build_phases.move(phase,0)
      end
    end
  end

  use_native_modules!
end

target 'MYAPP-tvOS' do
  # Pods for MYAPP-tvOS

  target 'MYAPP-tvOSTests' do
    inherit! :search_paths
    # Pods for testing
  end

end

AppDelegate.m:

/**
 * 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 "MoPub.h"

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

#import <Firebase.h>


@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  //  if ([FIRApp defaultApp] == nil) {
      [FIRApp configure];
  //  }
  [[UIApplication sharedApplication] registerForRemoteNotifications];

  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"MYAPP"
                                            initialProperties:nil];
  
  MPMoPubConfiguration *sdkConfig =
  [[MPMoPubConfiguration alloc] initWithAdUnitIdForAppInitialization:@"e121b73e18444d1d94212190f9d8ca75"];
  [[MoPub sharedInstance] initializeSdkWithConfiguration:sdkConfig completion: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;
}

//// Required to register for notifications
//- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
//{
//  [RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];
//}
//// Required for the register event.
//- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
//{
//  [FIRMessaging messaging].APNSToken = deviceToken;
//  [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
//}
//// Required for the registrationError event.
//- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
//{
//  [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
//}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

@end


Environment

Click To Expand

react-native info output:

System:
    OS: macOS Mojave 10.14.5
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
    Memory: 221.97 MB / 16.00 GB
    Shell: 3.2.57 - /bin/bash
  Binaries:
    Node: 10.15.0 - /usr/local/bin/node
    Yarn: 1.19.1 - /usr/local/bin/yarn
    npm: 6.4.1 - /usr/local/bin/npm
    Watchman: 4.9.0 - /usr/local/bin/watchman
  SDKs:
    iOS SDK:
      Platforms: iOS 13.2, DriverKit 19.0, macOS 10.15, tvOS 13.2, watchOS 6.1
  IDEs:
    Android Studio: 3.5 AI-191.8026.42.35.5791312
    Xcode: 11.3.1/11C504 - /usr/bin/xcodebuild
  npmGlobalPackages:
    react-native-cli: 2.0.1
    react-native: 0.61.2
  • Platform that you're experiencing the issue on:
    • 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:
    • 6.4.0 6.4.1-alpha.0
  • Firebase module(s) you're using that has the issue:
    • messaging
  • Are you using TypeScript?
    • N


@mikehardy
Copy link
Collaborator

My guess is that if you reach into that .m file inside node_modules and test-hacked the test you found that crashed so it looked just like the one above it, it would work. Here's the one above it and the one you mention:

// message.contentAvailable
if (apsDict[@"content-available"] != nil) {
message[@"contentAvailable"] = @([RCTConvert BOOL:apsDict[@"content-available"]]);
}
// message.mutableContent
if (apsDict[@"mutable-content"] != nil && [apsDict[@"mutable-content"] isEqualToString:@"1"]) {
message[@"mutableContent"] = @([RCTConvert BOOL:apsDict[@"mutable-content"]]);
}

The hypothesis based on the invalid selector error (isEqualToString does not exist on object of type '__NSCFNumber') that it was parsed into a number already instead of a string, and so will likely work like the 'content-available' apsDict value there. Very easy to test at least - please try it and report back?

@samkotlove
Copy link
Author

samkotlove commented Apr 11, 2020

Hi @mikehardy, thanks for the quick reply!

My guess is that if you reach into that .m file inside node_modules and test-hacked the test you found that crashed so it looked just like the one above it, it would work.

Great point, just tested it with the change below and it works

// message.mutableContent 
 if (apsDict[@"mutable-content"] != nil) { 
   message[@"mutableContent"] = @([RCTConvert BOOL:apsDict[@"mutable-content"]]); 
 } 

Is this something that I've misconfigured?

@mikehardy
Copy link
Collaborator

@Ehesp or @Salakar I think this is just a parsing issue, perhaps mutable-content was not checked the same way content-refresh was in your testing so it slipped through?

@samkotlove you can see from my hypothesis and question right here that I think it's just a bug. I'd follow this issue and use the fabulous / every project should use it / I am not kidding https://github.com/ds300/patch-package to turn your node_modules hack into a little patch that you can then commit and will be installed correctly every time you install packages until this is resolved for real

@samkotlove
Copy link
Author

@mikehardy Great suggestion, sure beats my bash scripts that I have for this sort of thing!

@Salakar
Copy link
Member

Salakar commented Apr 11, 2020

Interesting, when I tested this originally it came through as a string for me which I thought was weird but gave it no further thought, maybe an API difference somewhere - guess we need to check the type also then

Will get a fix up on Monday's RC. Thanks for debugging and providing a thorough issue report - makes our life easier

@Salakar Salakar self-assigned this Apr 11, 2020
@Salakar Salakar added plugin: messaging FCM only - ( messaging() ) - do not use for Notifications platform: ios type: bug New bug report labels Apr 11, 2020
@enzymevc
Copy link

Hi @mikehardy, thanks for the quick reply!

My guess is that if you reach into that .m file inside node_modules and test-hacked the test you found that crashed so it looked just like the one above it, it would work.

Great point, just tested it with the change below and it works

// message.mutableContent 
 if (apsDict[@"mutable-content"] != nil) { 
   message[@"mutableContent"] = @([RCTConvert BOOL:apsDict[@"mutable-content"]]); 
 } 

Is this something that I've misconfigured?

It worked for me. Thanks for finding and fixing this issue (it got me crazy for the last couple of days).

@ahanusek
Copy link

ahanusek commented Apr 14, 2020

I have a similar issue probably connected with this bug. After adding additional data to the notification on the server-side, iOS app crash after receiving new notification (in the foreground or after clicking on a notification while the app is in the background)

await messaging.sendMulticast({
      tokens,
      notification: content.generic,
      webpush: content.web, <=== I've added this additional data to my notifications
      data,
    });

"@react-native-firebase/analytics": "^6.4.0",
    "@react-native-firebase/app": "^6.4.0",
    "@react-native-firebase/auth": "^6.4.0",
    "@react-native-firebase/database": "^6.4.0",
    "@react-native-firebase/dynamic-links": "^6.4.0",
    "@react-native-firebase/firestore": "^6.4.0",
    "@react-native-firebase/functions": "^6.4.0",
    "@react-native-firebase/messaging": "^6.4.0",
    "@react-native-firebase/storage": "^6.4.0",

@Nehal-29
Copy link

Nehal-29 commented Apr 14, 2020

@samkotlove Hey how did you manage to get notification when app is in foreground?
Did you set {UNUserNotificationCenterDelegate,MessagingDelegate } delegate in AppDelegate or handled it react in code ?

I'm getting notification when my app is in background but not when it's in foreground.
react-native-firebase v ~ 5.6.0
Can you help me here ?

@samkotlove
Copy link
Author

@samkotlove Hey how did you manage to get notification when app is in foreground?
Did you set {UNUserNotificationCenterDelegate,MessagingDelegate } delegate in AppDelegate or handled it react in code ?

I'm getting notification when my app is in background but not when it's in foreground.
react-native-firebase v ~ 5.6.0
Can you help me here ?

Still working on it. As @mikehardy recommended in other tickets I’m trying react-native-push-notifications (which just uses @react-native-community/push-notifications-ios under the hood for iOS). There’s a solution I’ve found that has you hide foreground and background pushes and send a local notification when receiving a fcm message. It sort of works but has problems, such as I can’t seem to access data when a user clicks the fake, local notification. I’ll update when i commit to a solution.

@samkotlove
Copy link
Author

samkotlove commented Apr 14, 2020

@Nehal-29 After more thought (and reading quite a few old github issues on this repo, on react-native-push-notification, and @react-native-community/push-notification-ios) I'm completely going against what I previously said. The solution is essentially:

  • always sending silent notifications
  • picking those up on the react-native-firebase/messaging notification handlers
  • if a title and body are included, immediately send a localNotification ( using the library react-native-push-notification )

If you just want push notifications, that seems to be all that's required. My use case required navigating to certain screens in the app depending on the push notification, so it required a couple additional steps:

  • in the didReceiveNotificationResponse function of your native code (usually inside AppDelegate.m in most examples), adding an additional property (in my case called openedInForeground) before calling the RNCPushNotificationIOS didReceiveRemoteNotification function.
  • using the react-native-push-notification onNotification handler, checking for this property and responding accordingly

The reason you add that property is that the onNotification handler is called twice, once when you receive the notification and if/when you tap on it. The additional property creates a way to distinguish between when the notification is displayed (which is not useful in our case since we triggered it by sending a localNotification) and when that notification is tapped.

PushService.js (this is the file that handles push notifications, I initialize it in my App.js constructor. There's quite a few people that say this needs to exist outside the react lifecycle, so take this specific implementation with that in mind):

export default class PushService  {
  static configure() {
    PushNotification.configure({
      onRegister: ...
      },
      // Called when a remote or local notification is opened or received
      onNotification: (notification) => {
        // If it was opened do some work
        if (notification.data.openedInForeground) {
          if (notification.data.path) {
            console.log('------navigate to', notification.data.path);
          }
        }
      },
    });

    this.requestUserPermission();
    this.setListeners();
  }

  static setListeners() {
    try {
      const handleIncomingMessage = async remoteMessage => {
        const { data: { body, title, ...userInfo }} = remoteMessage;
        PushNotification.localNotification({ title, message: body, userInfo } });
      };
      messaging().onMessage(handleIncomingMessage);
      messaging().setBackgroundMessageHandler(handleIncomingMessage);
    } catch(e) {
      console.log('error setting message listener', e);
    }
  }
  ...
}

and then in your native code(probably AppDelegate.m):

// Called when notification is tapped
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
  NSLog(@"push-notification didReceiveNotificationResponse");

  NSMutableDictionary *userData = [NSMutableDictionary dictionaryWithDictionary:response.notification.request.content.userInfo];
  [userData setObject:@YES forKey:@"openedInForeground"];
  [RNCPushNotificationIOS didReceiveRemoteNotification:userData];
  completionHandler();

I haven't finished my android implementation, so it's entirely possible the javascript code may need to change or have different logic depending on the OS. I also haven't done extremely rigorous testing so there may be issues still (i.e. anecdotally,. if the app was force closed it doesn't always seem to trigger notifications). But if those issues seem rare I may deploy a version of this with metrics tracking notifications received versus notifications sent, to determine how well it delivers

@mikehardy
Copy link
Collaborator

@samkotlove that is great knowledge sharing and matches my experience - for complete control + success always send silent push, handle in messaging, then use a local notification library for control+reaction. The note about differentiating between "notification displayed" and "notification tapped" should be helpful for everyone!

@samkotlove
Copy link
Author

samkotlove commented Apr 19, 2020

Not totally related but for anybody reading this in the future, if you're using this library with react-native-push-notification as I described above, do not set the senderId inside the configuration function for the react-native-push-notification library. There seems to be a conflict when both libraries are registering and reading notifications

@actuosus
Copy link

Here is patch file for patch-package module. mutable-content value can be NSNumber when a notification is sent using Google FCM API and the mutable-content is required to be a number or you will have Value for APS key [mutable-content] is either 0 or 1. error.

cheeaun added a commit to cheeaun/checkweather-sg-native that referenced this issue May 10, 2020
- also fixes the condition for adding secrets
@stale
Copy link

stale bot commented May 22, 2020

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 stale bot added the Type: Stale Issue has become stale - automatically added by Stale bot label May 22, 2020
@mikehardy
Copy link
Collaborator

@Salakar / @Ehesp I think this is a valid issue (with a few duplicates) but this one has a patch-package file attached? Possibly useful

@stale stale bot removed the Type: Stale Issue has become stale - automatically added by Stale bot label May 27, 2020
Salakar pushed a commit that referenced this issue May 28, 2020
Currently receiving notifications with images will crash the app on iOS (Fixes #3447, #3616).

Patch proposed by @actuosus, thanks to him 🙌 #3447 (comment)

[publish]
stefkampen pushed a commit to stefkampen/react-native-firebase that referenced this issue Jun 6, 2020
…e#3701)

Currently receiving notifications with images will crash the app on iOS (Fixes invertase#3447, invertase#3616).

Patch proposed by @actuosus, thanks to him 🙌 invertase#3447 (comment)

[publish]
meenabassem added a commit to meenabassem/react-native-firebase that referenced this issue Jun 18, 2020
hmhm2292 pushed a commit to hmhm2292/react-native-firebase that referenced this issue Jul 13, 2021
…e#3701)

Currently receiving notifications with images will crash the app on iOS (Fixes invertase#3447, invertase#3616).

Patch proposed by @actuosus, thanks to him 🙌 invertase#3447 (comment)

[publish]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
platform: ios plugin: messaging FCM only - ( messaging() ) - do not use for Notifications type: bug New bug report
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants