React-native-push-notification: onNotification not being called when app was killed on iOS

Created on 3 Jul 2020  路  43Comments  路  Source: zo0r/react-native-push-notification

Bug

I am sending local notifications using react-native-background-fetch. This works good. When the user clicks on a notification I redirect him to a certain screen. This always works in Android.
For iOS it is working when the app is in foreground or background. When the app was killed and the user clicks on a notification, the app launches but the onNotification handler is not being called.
I call PushNotification.configure from index.js, so timing should not be the issue.

I followed the fix here to get onNotification to work in foreground and background.

Environment info

react-native info output:

 info 
  React Native Environment Info:
    System:
      OS: macOS 10.15.5
      CPU: (12) x64 Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz
      Memory: 4.00 GB / 32.00 GB
      Shell: 5.7.1 - /bin/zsh
    Binaries:
      Node: 10.16.3 - ~/.nvm/versions/node/v10.16.3/bin/node
      npm: 6.13.6 - ~/.nvm/versions/node/v10.16.3/bin/npm
      Watchman: 4.9.0 - /usr/local/bin/watchman
    SDKs:
      iOS SDK:
        Platforms: iOS 13.5, DriverKit 19.0, macOS 10.15, tvOS 13.4, watchOS 6.2
      Android SDK:
        API Levels: 23, 26, 27, 28, 29
        Build Tools: 23.0.1, 26.0.3, 27.0.3, 28.0.3, 29.0.3
        System Images: android-29 | Google APIs Intel x86 Atom, android-29 | Google APIs Intel x86 Atom_64
    IDEs:
      Xcode: 11.5/11E608c - /usr/bin/xcodebuild
    npmPackages:
      react: 16.8.3 => 16.8.3 
      react-native: 0.59.5 => 0.59.5 
    npmGlobalPackages:
      react-native-cli: 2.0.1

Library version: the development branch of this repo for the latest Android notification action fixes

Steps To Reproduce

  1. Configure the onNotification handler in index.js
  2. Send a push notification while the app is killed
  3. Check if the onNotification handler was called when the notification is clicked

Describe what you expected to happen:

onNotification should be called when coming from Foreground/Background or when the app was killed

All 43 comments

Hi,
The behavior of iOS depends on when you put in AppDelegate,
Check the iOS repository for this.

Okay I will recreate the issue there!

I just saw your issue with AppDelegate, the Apple documentation say that:

application didReceiveLocalNotification:(UILocalNotification *)notification

Is deprecated, and there is other issues on ios repo, to repace with something like 芦聽willPresentNotification聽禄.
But local notification in background doesn鈥檛 work

It works fine for me in the background. Just not when the app is killed.
Or what do you mean with with "local notification in background doesn鈥檛 work"?

In the new Notification SDK of Apple, local notifications doesn鈥檛 trigger the application if the user doesn鈥檛 interact with the notification .

Hi again, just to be sure, can you provide the content of .configure() ?

Reading your case and the version you are using, there might be a side affect of an Android change.

https://github.com/zo0r/react-native-push-notification/blob/dev/CHANGELOG.md

Now local scheduled notifications trigger onNotification before display #574.

Sorry @Dallas62, I was very busy the whole week.
My .configure looks like this:

PushNotification.configure({
  onNotification: handleNotificationClick,
  onAction: handleNotificationActionClick
});

function handleNotificationClick(notification) {
  const id = Platform.OS === "ios" ? notification.data.id : notification.id;

  RootNavigation.navigate("Question", {
    showBackButton: false,
    notificationId: id
  });
}

function handleNotificationActionClick(notification) {
  let vote;
  if (notification.action === "Yes") {
    vote = 1;
  } else if (notification.action === "No") {
    vote = 3;
  } else {
    showToast("Unknown action clicked. Please vote manually.");
    PushNotification.invokeApp(notification);
    return;
  }

  // Do some stuff while the app should not launch...
}

For Android everything works fine (including the actions). iOS works fine if the app was in fore- or background.
When the app was killed in iOS, the whole app launches and onNotification is not triggered.

@Dallas62 any news? 馃槆

Hi @mathiasmoeller
Will try to look at it soon

Thanks a lot! 馃檹

@Dallas62 any news? 馃槆 馃槵

Hi @mathiasmoeller, I'm on it 馃槈

Hi @mathiasmoeller
Can you provide a reproductive example ?
I'm not able to reproduce it... 馃槥

Hi again @mathiasmoeller
I probably found a bug in the way to get the Initial Notification when: PushNotification.configure({popInitialNotification: true /* default */})
You can quickly test: #1516

File & Line:
https://github.com/zo0r/react-native-push-notification/blob/271461ee5039de11ce24d07285ff470d622f88ae/index.js#L121

AppState.currentState is not passed in the current version, the function need to be changed too (to get the this context).

@Dallas62 I will test your fix ASAP! I think I will have time on Thursday. I will let you know if it fixes it

@Dallas62 I will try to test it today.
Should I install PR #1516 to test it? And should I set popInitialNotification to true or false? To be honest I didn't even understand what this flag is supposed to do ;)

I have some important Android fixes to do but I hope I will be able to do it in the afternoon.

Hi @mathiasmoeller
You can test the fix by installing this PR and focus on this part as the PR may contains breaking changes (data structure).
You can set popInitialNotification to true, this allow onNotification to be triggered when the application is opened using a notification.

Sadly I still could not get it to work when the app is killed.
I tried it with registering PushNotification.configure in index.js and with PushNotificationIOS.getInitialNotification also in index.js. Both did not work

PushNotificationIOS.getInitialNotification is not working ?
This is really strange, can you provide AppDelegate ?

Yes it does not return anything.
This is my AppDelegate:

/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
#import <Firebase.h>
#import <GoogleMaps/GoogleMaps.h>
#import "AppDelegate.h"

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

#import <Fabric/Fabric.h>
#import <Crashlytics/Crashlytics.h>
#import "Orientation.h"

#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [FIRApp configure];
  [GMSServices provideAPIKey:@"XXX"];
  NSURL *jsCodeLocation;

  [Fabric with:@[[Crashlytics class]]];


  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"iosl"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  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];

  // Define UNUserNotificationCenter
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge)completionHandler:^(BOOL granted, NSError * _Nullable error) {
    if (granted) {
      center.delegate = self;
    }
  }];

  return YES;
}

//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}

// 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];
}
// Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
 [RNCPushNotificationIOS didReceiveLocalNotification:notification];
}
// IOS 10+ Required for local notification tapped event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
  [RNCPushNotificationIOS didReceiveNotificationResponse:response];
  completionHandler();
}

/* part of react-native-orientation package, see https://github.com/yamill/react-native-orientation#Configuration */
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
  return [Orientation getOrientation];
}

@end

This is really strange, I think this is a problem with the AppDelegate or project configuration, but not sure.
Some people seems to encounter this bug only in debug, is it your case ?

Take care, in both issue (this and ios one) you let an API key.

I found something, maybe it's useful.
I installed the current develop branch since you merged #1516.
I am using react-native-boundary to monitor geofences and send local notifications in case of an event.
The following happens:

  • The app is killed
  • The user leaves a geofence and react-native-boundary triggers some code inside my app
  • Therefore the app starts in background
  • While doing this, it runs PushNotificationIOS.getInitialNotification() which is in my index.js file. It resolves to null (since there is no notification yet)
  • My code triggers a local notification using react-native-push-notification
  • The user clicks on this notification
  • PushNotificationIOS.getInitialNotification() in index.js is not triggered again
  • The onNotification handler which was registered in index.js also does not get called

Does this maybe describe the situation a bit better? (I know its not the most simple one 馃槈 )

@Dallas62 I tried the new 5.0.1 release.
I guess instead of PushNotificationIOS.getInitialNotification I should use PushNotification.popInitialNotification now right?
I added it to my index.js file.
But the behaviour stays the same as described in my last comment. It gets called when the app starts in background due to geofencing events and a local notification is shown. During this app start PushNotification.popInitialNotification is called and the callback is called with undefined. When the user clicks the notification, the app comes to foreground but PushNotification.popInitialNotification is not called again.

Any ideas? Thank you so much already!

Hi @mathiasmoeller
If the app is considered already started, the initial notification should be always undefined (in case it's not a notification that start the app).
What's strange in your case is that the notification doesn't trigger onNotification.

I guess instead of PushNotificationIOS.getInitialNotification I should use PushNotification.popInitialNotification now right?
I added it to my index.js file.

Yes, but it's to unify notification format, result is based on PushNotificationIOS.getInitialNotification.

I don't know which accuracy you expect between geolocalisation and notification, but did you tried to schedule a notification ?
This will probably let the background process exit before triggering the notification. Starting with 60s to be sure that the application is exited then reduce the timing.
It's not the expected behavior but a workaround until we found a solution.

@Dallas62 thanks for the clarification.

60s should not be an issue for my use case I think. I could try that! But I'd prefer to get onNotification to work :grin: I don't get why it works when the app is in the background after being opened regularly but not when it is in background after starting in this headless background mode.

@Dallas62 maybe I have made some mistakes while setting up @react-native-community/push-notification-ios.
Do I have to follow the whole setup guide for this library or does react-native-push-notification already contain the important parts and setup?

Actually there is nothing for iOS on the repository (expect JS implementation).
So for iOS it's the setup of RNC package.
A mistake in the installation can be the root cause, or a missing notification method in the AppDelegate. But when I look at your AppDelegate, I don't see anything special.

If you have a small reproducible example, I can try to look directly.

I can try to set up an example. But I don't think I have time for that in the next days. Let's see.

I made another interesting observation:
App starts in foreground, moved to background (e.g. by pressing the home button), a notification is shown, the notification is clicked -> onNotification gets called

App starts in background, a notification is shown (not clicked), I open the app manually, I click the notification now -> onNotification is not called.

So the problem seems to be that when the app starts in background, the listeners do not get attached properly?

I have logs that tell me that PushNotification.configure gets called when starting in background. So this part seems fine, any ideas?

You can try to attach listener on iOS library:
https://github.com/react-native-community/push-notification-ios#addeventlistener

  • localNotification
  • notification

If it's triggered; there is an issue on this library or the use of .configure()
Else, this is due to a bug in iOS library or mis-configuration
In any case, this will help for investigations

I tested it with the ios implementation. The behaviour is exactly the same. So I guess it is for sure not an issue of react-native-push-notification.

scheduling the notification sadly does not help :/

Okay I build a sample project. This issue is: there it works...... FML! I did everything exactly the same way as I did it in my project.

I found another log in XCode:

2020-08-14 13:05:42.436163+0200 iosl[37997:1282420] -[RNFirebaseNotifications sendJSEvent:name:body:] [Line 411] Multiple notification open events received before the JS Notifications module has been initialised

This sounds like it is an issue. Problem is, that I call PushNotification.configure directly in index.js. There is no earlier point in time....
Do you maybe have any idea what it could be @Dallas62 ? Sorry for spamming you so much :grin:

Hi @mathiasmoeller
This message doesn't come from this library or the iOS one. It's probably related to the invertase one.

Had the same issue.

only happened on simulator but works fine on real device.

try to stop debugging or remove the app from simulator and re install it again.

@stevenasi thanks for the suggestion! It's just hard to test without the simulator since my events are triggered by background events :grin: But I will give it a try for sure!

@stevenasi oh wow for me it is even worse. On the real device not even while the app is in fore- or background the notification handler is called! ARGH!

@mathiasmoeller did you try to uninstall the app from the device and re-install it again because that what was causing the problem in my case.

was this ever resolved? having this issue when I send a silent notification, the app wakes up from a kill state but doesn't call onNotification. This works well in android

Also having a similar issue. Nothin gets called anything from a killed state.

I could confirm this bug, it seems to work in release build but not in debug. What strange is in release build the payload that is passed down doesn't include title and message only the action property.

After 2 days i fined the answer.
https://github.com/react-native-push-notification-ios/push-notification-ios/issues/24#issuecomment-548398814
when onRegister Called PushNotificationIOS.getInitialNotification returned the notification data

Hi Friends, i have found solution. Please check below methods.

Add this code in below file.
AppDelegate.m

create new file methods in same class....

-(void) callNotification: (NSDictionary*) userInfo {
[RNCPushNotificationIOS didReceiveRemoteNotification:userInfo];
}

Modify below File from framework
RNCPushNotificationIOS.m

  • (void)handleRemoteNotificationReceived:(NSNotification *)notification {
    .............
    remoteNotification[@"notificationData"] = notification.userInfo[@"notification"];
    .............
    }

Update above line on this methods.

Now Check, you will data on onNotification method......

Hope this work for you....

Was this page helpful?
0 / 5 - 0 ratings

Related issues

atteia picture atteia  路  4Comments

anativ picture anativ  路  3Comments

cidevant picture cidevant  路  3Comments

DanDance picture DanDance  路  3Comments

sungjinoh picture sungjinoh  路  3Comments