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

Push notification in foreground iOS RN 0.60+ #63

Open
jhondavila opened this issue Jan 28, 2020 · 37 comments
Open

Push notification in foreground iOS RN 0.60+ #63

jhondavila opened this issue Jan 28, 2020 · 37 comments

Comments

@jhondavila
Copy link

jhondavila commented Jan 28, 2020

I would like to share a solution that I found with notifications in the foreground for iOS ... lines below my AppDelegate.m

#import "AppDelegate.h"

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

@import Firebase;

@implementation AppDelegate

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {

    if ([FIRApp defaultApp] == nil) {
    [FIRApp configure];
    }

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

    NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
    [ReactNativeNavigation bootstrap:jsCodeLocation launchOptions:launchOptions];

    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];
    }

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

@EnD

@jhondavila jhondavila reopened this Jan 28, 2020
@jhondavila jhondavila changed the title Push notification in foreground iOS RN 0.60 > Push notification in foreground iOS RN 0.60+ Jan 28, 2020
@blastering66
Copy link

what is the update ?? did you solve it ??
Foreground is fix ?

@nabeelItilite
Copy link

same issue

@dominiczaq
Copy link

I have working solution, I'll post it later today.

@dominiczaq
Copy link

Please note that UNUserNotificationCenter is available in iOS 10+, so if your app needs to support iOS 9, you'll need to wrap appropriate parts of code in conditional blocks.
More about UNUserNotificationCenter in the documentation: https://developer.apple.com/documentation/usernotifications/unusernotificationcenter?language=objc


AppDelegate.h

  1. Add new import to the imports section in the top of the file
    #import <UserNotifications/UserNotifications.h>
  2. Declare that your app implements UNUserNotificationCenterDelegate in @implements block

@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate> // Add UNUserNotificationCenterDelegate to the list, in my case it looks like this


AppDelegate.m

  1. Add new import to the imports section in the top of the file
    #import <UserNotifications/UserNotifications.h>

  2. Add following code to the bottom of didFinishLaunchingWithOptions method (before return):

UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
  1. Add willPresentNotification method. Code below will present all notifications in the foreground.
// Manage notifications while app is in the foreground
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}

In the willPresentNotification method you can control which notifications to show. I.e. in our app we only present one type of notifications in the foreground. The type of notification is added to apns payload.
Example:

// Manage notifications while app is in the foreground
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  NSDictionary *userInfo = notification.request.content.userInfo; // read apns payload
  NSString *notificationType = userInfo[@"notificationType"]; // check your notification type
  if (![notificationType  isEqual: @"some custom type"]) { // we silence all notifications which do not match our custom notification type
    completionHandler(UNNotificationPresentationOptionNone);
    return;
  }
  // custom notification type is displayed normally
  completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}

Hope this helps

@dominiczaq
Copy link

dominiczaq commented Feb 9, 2020

Can we close this issue now or maybe we should add info above to the docs for people with the same issue?

@tegozen
Copy link

tegozen commented Feb 12, 2020

this solved my problem

@CoverID
Copy link

CoverID commented Feb 27, 2020

Version 1.0.6

I got an error at: (void)didReceiveRemoteNotification:(NSDictionary *)notification
Error message: There is no completion handler with notification id: xxx-xxx-xxx

// Called when a user taps on a notification in the foreground
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
  NSMutableDictionary *userData = [NSMutableDictionary dictionaryWithDictionary:response.notification.request.content.userInfo];
  [userData setObject:@YES forKey:@"userInteraction"];
  [RNCPushNotificationIOS didReceiveRemoteNotification:userData]; <<---- this
}

@brianomchugh
Copy link

Is there any way to conditionally hide remote notifications in the foreground (for instance if you are already looking at the chat thread that the message applies to)? I find that if I add willPresentNotification, notifications are shown in the foreground always and onNotification is only fired when the user clicks it. If I remove it, notifications are hidden in the foreground, but onNotification fires right away. I tried to send a local notification in this scenario, but of course it is not shown in the foreground either.

@dominiczaq
Copy link

dominiczaq commented Mar 4, 2020

@brianomchugh

Is there any way to conditionally hide remote notifications in the foreground (for instance if you are already looking at the chat thread that the message applies to)

If you would read carefully my message (#63 (comment)) you would find the example at the very bottom.
Just pass UNNotificationPresentationOptionNone to completionHandler if you would like to not present particular notification.

@brianomchugh
Copy link

@dominiczaq

Yes I saw that example-- your condition is some value in the notification payload.
I am more talking about some condition in the javascript code. For instance, in the iOS Messages app, a notification is shown when the app is in the foreground and you are viewing a different conversation, but not if you are already viewing that conversation or the conversation list. Im trying to figure out how to replicate that but it doesn't seem possible without a native solution.

@dominiczaq
Copy link

It's quite possible. When you want to block notification you need to save that information in the variable in the native side. When notification is received just read that variable and decide based on that.

@brianomchugh
Copy link

OK this is what I came up with based off of your help @dominiczaq . I don't know much on the native side, so this is hacky-- but it might help others looking to selectively block push notifications in the foreground:

//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  NSDictionary *userInfo = notification.request.content.userInfo; // read apns payload
  NSString *newConversationKey = [userInfo objectForKey:@"from_msg"];
 
  //read stored focusedConversationKey from user defaults
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSString *previousConversationKey = [defaults objectForKey:@"focusedConversationKey"];
  
  //if notification applies to conversation that is already in focus
  if([newConversationKey isEqualToString:previousConversationKey]) {
    NSLog(@"Already focused on conversation. Hiding Push Notification");

    //add "conversationAlreadyFocused" value to notification, so this case can be tested for in the javascript onNotification handler
    NSMutableDictionary *userInfoMutable = [userInfo mutableCopy];
    userInfoMutable[@"conversationAlreadyFocused"] = @"true";
    userInfo = [NSDictionary dictionaryWithDictionary:userInfoMutable];
    
    //Still call the javascript onNotification handler so it can display the new message right away
    [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo];
    
    //hide push notification
    completionHandler(UNNotificationPresentationOptionNone);
  } else {
    NSLog(@"New notification does not apply to currently focused conversation. Showing Push Notification.");
    completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
  }
  return;
}

@kyytiPetteri
Copy link

kyytiPetteri commented Apr 9, 2020

I still can't get foreground notifications to work:

When API sends a notificationRequest APN it does trigger a notification and localNotification also gets shown but only when app is in the background. I would like to show all notifications when app is foregrounded.

"react-native": "^0.61.5"
"@react-native-community/push-notification-ios": "^1.1.1"

AppDelegate.h:

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

@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate>
...

AppDelegate.m:

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

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;
return YES;
}

// Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
 [RNCPushNotificationIOS didReceiveLocalNotification:notification];
}

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

@end

@abhisekm
Copy link

If you are using firebase and it is not working, you might wanna look at invertase/react-native-firebase#3367

direct link to comment

@wbercx
Copy link

wbercx commented Apr 27, 2020

I still can't get foreground notifications to work:

@kyytiPetteri I followed every instruction to the T several times over, and still couldn't get it to work. After a day and a half of messing around, I right-clicked userNotificationCenter to jump to its definition so I could have a look. Much to my surprise, it presented two definitions. One of which was RNFB.

🎉 Instant clarity! I've upgraded from RN 0.59 to 0.61.5 months ago, and then to 0.62.2 recently. React Native Firebase (which I only use for Android...) had auto-linked into my iOS build.

I skipped auto-linking for the related packages, nuked and reinstalled my pods, and my foreground notifications now work as intended again.

@keithhackbarth
Copy link
Contributor

For those of you upgrading from version 1.1.1 to 1.2.0 please be aware that there was some breaking changes introduced (and not yet documented with how foreground notifications are handled. Specifically, before 1.2.0, native notifications would not be handled if app was in foreground and now they are. If you'd like to keep legacy behavior, I recommend changing your willPresentNotification as follows:

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

    //Still call the javascript onNotification handler so it can display the new message right away
    NSDictionary *userInfo = notification.request.content.userInfo;
    [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo];

    //hide push notification
    completionHandler(UNNotificationPresentationOptionNone);
}

@kovkev
Copy link

kovkev commented May 29, 2020

Hi, prior to implementing the additions in this page, I could not get onNotification to trigger. After keithhackbarth comment, I got the onNotification to trigger, but I also get the error There is no completion handler with notification id . I am not using react-native-firebase. I don't know what this error means.

@kovkev
Copy link

kovkev commented May 29, 2020

It seems like if I comment out notification.finish(PushNotificationIOS.FetchResult.NoData), I do not get that issue. However, it shows as required in the documentation of react-native-push-notification which I use: https://github.com/zo0r/react-native-push-notification#usage

@thomas-negrault
Copy link

thomas-negrault commented Jun 2, 2020

I thought I got it to work with @keithhackbarth code but now I have the same issue as @kovkev.
"There is no completion handler with notification id"
I just want to have the following behavior:

  • App in background: display the notification and triggers onNotification when the user tap on it. (OK)
  • App in foreground: do not display the notification but triggers onNotification in order to silently update the app data.(KO)

I agree, it's working without the notification.finish line. I used:

    onNotification: function (notification) {
      console.log(notification);
      if (!notification.foreground) {
        notification.finish(PushNotificationIOS.FetchResult.NoData);
      }
    },

But I don't know if this can cause any issue

@Abhishek-Sankey
Copy link

I've also updated this library and followed the same steps of migration/installation. Now, getting push notifications when app is inactive or in background but not when app is in foreground.
I think 'willPresentNotification' is not getting triggered, tried to do some NSLogs in that method but couldn't find anything on console.

@jSkrills
Copy link

jSkrills commented Jun 3, 2020

I have also tried all of these workarounds in this thread and several others and I am still not seeing onNotification triggered while the app is in the foreground/background. It is only triggered when the app is completely killed and a notification is selected.

@thomas-negrault
Copy link

I set it up with the last version and this part work for me, when the app is in background/foreground and I click the notification, onNotification is triggered !

@jSkrills
Copy link

jSkrills commented Jun 3, 2020

@thomas-negrault what do you mean the last version? Are you meaning the suggestion from @keithhackbarth and also removing the notification.finish line? Or are you talking about an actual specific version of push-notification-ios or react-native-push-notification?

@thomas-negrault
Copy link

It seems some people in this thread have the issue after updating from an old version of this library to the last one, and that’s what causing the issue. In my case it’s a recent install from scratch. Maybe some change require by an old version of this library is now causing issues.

@lowell-list-cambia
Copy link

@keithhackbarth's fix with a small change worked for me. I just gave it an empty completion handler to avoid the "There is no completion handler with notification id" error:

// Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center
      willPresentNotification:(UNNotification *)notification
        withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  // Still call the JS onNotification handler so it can display the new message right away
  NSDictionary *userInfo = notification.request.content.userInfo;
  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo
                                fetchCompletionHandler:^void (UIBackgroundFetchResult result){}];

  // hide push notification
  completionHandler(UNNotificationPresentationOptionNone);
}

@thomas-negrault
Copy link

Thanks a lot @lowell-list-cambia it's working fine with your code !

@hanayashiki
Copy link

I hope the willPresentNotification will call a js function to check if we display it or not.

@khaledBou
Copy link

I tried all of the solutions but i can't show the notifications in the foreground .
My full code is
`/**

  • 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 <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>

// Implement UNUserNotificationCenterDelegate to receive display notification via APNS for devices
// running iOS 10 and above.
@interface AppDelegate ()
@EnD

@implementation AppDelegate

NSString *const kGCMMessageIDKey = @"gcm.message_id";

  • (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    // Use Firebase library to configure APIs
    // [START configure_firebase]
    if ([FIRApp defaultApp] == nil) {
    [FIRApp configure];
    }
    // [END configure_firebase]

    // [START set_messaging_delegate]
    [FIRMessaging messaging].delegate = self;
    // [END set_messaging_delegate]

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

    // ####################################
    if ([UNUserNotificationCenter class] != nil) {
    // iOS 10 or later
    // For iOS 10 display notification (sent via APNS)
    [UNUserNotificationCenter currentNotificationCenter].delegate = self;
    UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert |
    UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
    [[UNUserNotificationCenter currentNotificationCenter]
    requestAuthorizationWithOptions:authOptions
    completionHandler:^(BOOL granted, NSError * _Nullable error) {
    // ...
    }];
    } else {
    // iOS 10 notifications aren't available; fall back to iOS 8-9 notifications.
    UIUserNotificationType allNotificationTypes =
    (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
    UIUserNotificationSettings *settings =
    [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
    [application registerUserNotificationSettings:settings];
    }

    [application registerForRemoteNotifications];
    // ####################################

    // Define UNUserNotificationCenter
    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
    }

  • (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
    NSLog(@"FCM registration token: %@", fcmToken);
    // Notify about received token.
    NSDictionary *dataDict = [NSDictionary dictionaryWithObject:fcmToken forKey:@"token"];
    [[NSNotificationCenter defaultCenter] postNotificationName:
    @"FCMToken" object:nil userInfo:dataDict];
    // TODO: If necessary send token to application server.
    // Note: This callback is fired at each app startup and whenever a new token is generated.
    }

// Receive displayed notifications for iOS 10 devices.
// Handle incoming notification messages while app is in the foreground.

  • (void)userNotificationCenter:(UNUserNotificationCenter *)center
    willPresentNotification:(UNNotification *)notification
    withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
    NSDictionary *userInfo = notification.request.content.userInfo;

    // With swizzling disabled you must let Messaging know about the message, for Analytics
    [[FIRMessaging messaging] appDidReceiveMessage:userInfo];

    // Print message ID.
    if (userInfo[kGCMMessageIDKey]) {
    NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]);
    }

    // Print full message.
    NSLog(@"%@", userInfo);

    // Change this to your preferred presentation option
    completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionAlert);
    }

// 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];
    NSLog(@"APNs device token retrieved: %@", deviceToken);
    }

// [START receive_message]
// 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
    {
    // Print message ID.
    if (userInfo[kGCMMessageIDKey]) {
    NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]);
    }
    // Print full message.
    NSLog(@"%@", userInfo);

    completionHandler(UIBackgroundFetchResultNewData);

    [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
    }
    // [END receive_message]

// Required for the registrationError event.

  • (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
    {
    [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
    NSLog(@"Unable to register for remote notifications: %@", error);
    }
    // IOS 10+ Required for localNotification event
  • (void)userNotificationCenter:(UNUserNotificationCenter *)center
    didReceiveNotificationResponse:(UNNotificationResponse *)response
    withCompletionHandler:(void (^)(void))completionHandler
    {
    [RNCPushNotificationIOS didReceiveNotificationResponse:response];
    completionHandler();
    }
    // IOS 4-10 Required for the localNotification event.
  • (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
    {
    [RNCPushNotificationIOS didReceiveLocalNotification:notification];
    }

@EnD
`

@fatihyildizhan
Copy link

fatihyildizhan commented Oct 30, 2020

I have the same error with v1.7.1. willPresentNotification is not triggering.

- AppDelegate.h

#import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UNUserNotificationCenter.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate>

@property (nonatomic, strong) UIWindow *window;

@end
- AppDelegate.m

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

@sahil290791
Copy link

sahil290791 commented Nov 30, 2020

@lowell-list-cambia For me, this error keeps popping up. Any idea what could cause this?

In the JS part, i am using react-native-push-notifications

onNotification: (notification) => {
        // console.log("NOTIFICATION:", notification);
        // process the notification
       // call notification.finish
        notification.finish(PushNotificationIOS.FetchResult.NoData);
      },

AppDelegate.m

// Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center
      willPresentNotification:(UNNotification *)notification
        withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  // Still call the JS onNotification handler so it can display the new message right away
  NSDictionary *userInfo = notification.request.content.userInfo;
  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo
                                fetchCompletionHandler:^void (UIBackgroundFetchResult result){}];

  // hide push notification
  completionHandler(UNNotificationPresentationOptionNone);
}

Error

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSObject userNotificationCenter:willPresentNotification:withCompletionHandler:]: unrecognized selector sent to instance 0x2817366a0'
terminating with uncaught exception of type NSException

@olgaababic
Copy link

These are my dependencies:

react-native - 0.63.3
react- 16.13.1
react-native-push-notification - 7.0.0
@react-native-community/push-notification-ios - 1.8.0

onNotification gets triggered and willPresentNotification get triggered. But, I still notification doesn't get showed when app is in foreground. In background everything works fine...

@hichemBAALI
Copy link

I still can't get foreground notifications to work:

@kyytiPetteri I followed every instruction to the T several times over, and still couldn't get it to work. After a day and a half of messing around, I right-clicked userNotificationCenter to jump to its definition so I could have a look. Much to my surprise, it presented two definitions. One of which was RNFB.

🎉 Instant clarity! I've upgraded from RN 0.59 to 0.61.5 months ago, and then to 0.62.2 recently. React Native Firebase (which I only use for Android...) had auto-linked into my iOS build.

I skipped auto-linking for the related packages, nuked and reinstalled my pods, and my foreground notifications now work as intended again.

Your answer helped me a lot. In my case, I was using "react-native-firebase": "^5.6.0", as an additional dependency for some reason that i really don't know.
So,

  • Removing "react-native-firebase": "^5.6.0" from package.json
  • Removing node_modules
  • npx install
  • cd ios && pod install && cd ..

SOLVED THE PROBLEM. Now am getting the notification event fired.

@robbycp
Copy link

robbycp commented Jan 26, 2021

I follow ios basic installation guide as in the documentation here. For you with issue willPresentNotification not firing in AppDelegate.m, I follow this answer and works.

If you use react-native-push-notification, using firebase messaging to send the notification, and using onNotification with this strategy, try to exclude gcm.message_id, fcm_options, and google.c.a.e field from userInfo when you want to show in foreground.

function showNotification(notifId, data) {
  let notifData = {
    id: notifId,
    ...data,
  }

  if (Platform.OS === 'ios') {
    /* iOS only properties */
    const {
      fcm_options: fcmOptions,
      'gcm.message_id': gcmMessageId,
      'google.c.a.e': googleCAE,
      ...otherUserInfoData
    } = notifData.userInfo
    notifData = {
      ...notifData,
      userInfo: {
        ...otherUserInfoData,
      },
    }
  }
  PushNotification.localNotification(notifData)
}

PushNotification.configure({
  onNotification(notification) {
      if (notification.userInteraction) {
        navigateFromNotification(notification.data)
      } else if (notification.foreground) {
        showNotification(notification.id, notification.data)
      }

      // (required) Called when a remote is received or opened, or local notification is opened
      notification.finish(PushNotificationIOS.FetchResult.NoData)
  },
}

Background and quit state are working well in android and ios. Only foreground ios not showing.
My Stack :

  • "@react-native-community/push-notification-ios": "~1.8.0",
  • "@react-native-firebase/messaging": "~7.3.2",
  • "react-native-push-notification": "~6.1.3",

@eyalyoli
Copy link

It seems to be that we are all lost and we'll continue to be until this issue is well documented!
I'm using RN v0.63 and had the same issues that everyone is having here and in other 14506 issues on this repo and on react-native-push-notification. The problem in the documentation lies in the integration between both libs.

Tested with:
react-native: 0.63
@react-native-community/push-notification-ios: 1.8.0
react-native-push-notification: 7.2.0
Using Firebase Cloud Messaging (FCM) for android & ios

The solution:

  1. What @lowell-list-cambia suggested here forces onNotification to trigger on foreground (which wasn't the case for ios - android worked fine).
  2. If you wish to show a notification in the foreground, you should keep the line:
    completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
    instead of:
    completionHandler(UNNotificationPresentationOptionNone);
    because it seems that using UNNotificationPresentationOptionNone hides locally triggered notifications too.

Notes:

  1. The best way to show push notifications in the foreground is handling it manually by calling PushNotification.localNotification(...) inside onNotification - because only then you will have a choice if to show or not.
  2. Watch out for a loop when triggering a local notification inside onNotification (it will re-trigger onNotification), I just used a local notification marker in the notification data and did what @robbycp said (removing irrelevant props from the notification).
  3. Don't forget to define the channeId for android local notifications to work.

I'll be posting it on react-native-push-notification also.

@naojamg
Copy link

naojamg commented Mar 12, 2021

Please note that UNUserNotificationCenter is available in iOS 10+, so if your app needs to support iOS 9, you'll need to wrap appropriate parts of code in conditional blocks.
More about UNUserNotificationCenter in the documentation: https://developer.apple.com/documentation/usernotifications/unusernotificationcenter?language=objc

AppDelegate.h

1. Add new import to the imports section in the top of the file
   `#import <UserNotifications/UserNotifications.h>`

2. Declare that your app implements `UNUserNotificationCenterDelegate` in `@implements` block

@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate> // Add UNUserNotificationCenterDelegate to the list, in my case it looks like this

AppDelegate.m

1. Add new import to the imports section in the top of the file
   `#import <UserNotifications/UserNotifications.h>`

2. Add following code to the bottom of `didFinishLaunchingWithOptions` method (before return):
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
1. Add `willPresentNotification` method. Code below will present all notifications in the foreground.
// Manage notifications while app is in the foreground
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}

In the willPresentNotification method you can control which notifications to show. I.e. in our app we only present one type of notifications in the foreground. The type of notification is added to apns payload.
Example:

// Manage notifications while app is in the foreground
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  NSDictionary *userInfo = notification.request.content.userInfo; // read apns payload
  NSString *notificationType = userInfo[@"notificationType"]; // check your notification type
  if (![notificationType  isEqual: @"some custom type"]) { // we silence all notifications which do not match our custom notification type
    completionHandler(UNNotificationPresentationOptionNone);
    return;
  }
  // custom notification type is displayed normally
  completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
}

Hope this helps

this just works great 💯

@ctrleffive
Copy link

For those of you upgrading from version 1.1.1 to 1.2.0 please be aware that there was some breaking changes introduced (and not yet documented with how foreground notifications are handled. Specifically, before 1.2.0, native notifications would not be handled if app was in foreground and now they are. If you'd like to keep legacy behavior, I recommend changing your willPresentNotification as follows:

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

    //Still call the javascript onNotification handler so it can display the new message right away
    NSDictionary *userInfo = notification.request.content.userInfo;
    [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo];

    //hide push notification
    completionHandler(UNNotificationPresentationOptionNone);
}

if I ever see you in person, I'll definitely give you a treat.
man! you saved me from a terrible headache. thank you!

@mcastillo86
Copy link

For those of you upgrading from version 1.1.1 to 1.2.0 please be aware that there was some breaking changes introduced (and not yet documented with how foreground notifications are handled. Specifically, before 1.2.0, native notifications would not be handled if app was in foreground and now they are. If you'd like to keep legacy behavior, I recommend changing your willPresentNotification as follows:

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

    //Still call the javascript onNotification handler so it can display the new message right away
    NSDictionary *userInfo = notification.request.content.userInfo;
    [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo];

    //hide push notification
    completionHandler(UNNotificationPresentationOptionNone);
}

I've spent weeks thinking what was wrong with my code even though I followed the examples thoroughly. I can't believe this is not documented yet. Thanks man! You saved me 👍 🎉 !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests