React-native-notifee: App crashes after pressing notification

Created on 8 Oct 2020  路  14Comments  路  Source: notifee/react-native-notifee

Our app sometimes crashes after pressing a local notification while the app is running in the background. The app terminates due to uncaught exception 'NSInternalInconsistencyException', reason: 'Call must be made on main thread'. This only happens on devices running iOS 14.

The stack trace points towards Notifee's didReceiveNotificationResponse handler. Does that handler submit something to a background queue?

Screenshot 2020-10-08 at 09 03 19

Library versions
react-native: 0.63.3
@notifee/react-native: 0.13.2
@react-native-firebase/messaging: 7.9.0

iOS bug under-review

All 14 comments

Thanks for reporting this @photoroulette ! It is under review this very moment

@photoroulette thanks for the information. I see you mentioned it happens sometimes. Does it happen for a particular notification with certain properties like actions or attachments? Trying to reproduce locally but can't recreate the error. Could this be related, https://github.com/invertase/react-native-firebase/issues/3944?

Actually, seconds later after posting this, it crashed 馃槰

Unfortunately, we haven't figured out a way to reproduce this consistently.

The issue you linked to is certainly relevant, as some users in the discussion are reporting similar errors. We have tried making sure that the completion handler is executed on the main queue (as suggested), but that didn't help. That left us to think that Notifee's handler submits something to a queue that is (at least sometimes) run on a background thread. Did you find any calls to dispatch_after?

Thanks for looking into it!

Looks like this error is actually happening inside RNFB, Notifee is just delegating to RNFB's delegate implementation, here's the code in Notifiee executed by the first line in the highlighted red box in your screenshot:

if (_originalDelegate != nil &&
             originalUNCDelegateRespondsTo.didReceiveNotificationResponse) {
    [_originalDelegate userNotificationCenter:center
               didReceiveNotificationResponse:response
                        withCompletionHandler:completionHandler];
}

_originalDelegate being RNFB in this case.

In our case, I think it's the other way around: The event is first handled by RNFB, then delegated to Notifee before it crashes. So _originalDelegate in that code should not be referencing RNFB.

Is that everything Notifee's handler is doing? I was at least expecting some logic to emit relevant events (onForegroundEvent etc.)?

Steps to reproduce:

  1. Open the app
  2. Display a local notification
  3. Press the local notification
  4. Send the app to the background
  5. Wait 30 seconds
  6. Open the app again
  7. App crashes

Tested this with a clean project (react-native init) and Notifee's example code, running on an iPhone 6S and iPhone XS running iOS 14.0.1.

Thanks, I think I've fixed this, ensuring the completionHandler will run on the main queue. Will be doing some final checks and aiming for a release today.

Release v0.14.0 has the bug fix. Thanks for your help, helping us debug this.

Hi @helenaford , i'm facing the same issue but i don't use react-native-notifee, i just use react-native-firebase (v5).

So what do you do for fix the bug ?

Thank you.

Hi, it was pretty much what I said above, making sure the completionHandler is run on the main queue.

From looking at this file, I can't see anything that would cause the same bug. But, you could try changing how the completionHandler is called in didReceiveNotificationResponse:

// from
 completionHandler();
        // to
         dispatch_async(dispatch_get_main_queue(), ^{
          completionHandler();
         });

But, I highly recommend to update, I've been there myself it may seem like a lot of work but it's worth it, especially with Apple updating their rules for the App Store constantly.

You can either upgrade to use RNFB with Notifee or use one of the other Open Source alternatives listed here.

I try to change this :

In the file : AppDelegate.m

-(void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {        
  [[RNFirebaseMessaging instance] didReceiveRemoteNotification:response.notification.request.content.userInfo];
  dispatch_async(dispatch_get_main_queue(), ^{
    completionHandler();
  });
}

But i always the same error.

I replace dispatch_async with dispatch_sync and works fine !

hi @helenaford, can you please help me, i'm facing same issue, and i applied the solutions that you mentioned yet i'm still getting same error "Call must be made on main thread, was thrown while invoking complete on target RNFirebaseNotifications"

this is my AppDelegate.m

// 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
{
 [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
 [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// IOS 10+ Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
  [RNCPushNotificationIOS didReceiveNotificationResponse:response];
  [RnAlarmNotification didReceiveNotificationResponse:response];
  dispatch_sync(dispatch_get_main_queue(), ^{
      completionHandler();
    });
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
       willPresentNotification:(UNNotification *)notification withCompletionHandler: (void (^)(UNNotificationPresentationOptions options))completionHandler {
  [RnAlarmNotification didReceiveNotification:notification];
  completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}

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

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

  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    center.delegate = self;

  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

I found this block in FNFirebaseNotifications.m

// Handle notification messages after display notification is tapped by the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
#if defined(__IPHONE_11_0)
         withCompletionHandler:(void(^)(void))completionHandler NS_AVAILABLE_IOS(10_0) {
#else
         withCompletionHandler:(void(^)())completionHandler NS_AVAILABLE_IOS(10_0) {
#endif
     NSDictionary *message = [self parseUNNotificationResponse:response];

     NSString *handlerKey = message[@"notification"][@"notificationId"];

     [self sendJSEvent:self name:NOTIFICATIONS_NOTIFICATION_OPENED body:message];
     if (handlerKey != nil) {
         completionHandlers[handlerKey] = completionHandler;
     } else {
         completionHandler();
     }
}

After i added the async in the last if else the issue remained, then after changing async to sync, m getting this error "Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1029b4be0)" on the dispatch_sync line.

That鈥檚 how i added it

if (handlerKey != nil) {
         dispatch_sync(dispatch_get_main_queue(), ^{
         completionHandlers[handlerKey] = completionHandler;
         });
     } else {
         dispatch_sync(dispatch_get_main_queue(), ^{
         completionHandler();
         });
     }
Was this page helpful?
0 / 5 - 0 ratings