6

I am working on a chat app in react-native iOS. I want to do a badge increment on the main app icon when a notification is received, when the app is killed or force quit by the user. It works well when the app is in the background mode. But the didReceiveRemoteNotification method is not called when the app is inactive. Any idea on this? I added code inside the didReceiveRemoteNotification method of AppDelegate.

Added the following code set in AppDelegate file

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
      [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
    }

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
      [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error];
    }

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
    NSLog(@"APPDELEGATE: didReceiveRemoteNotification:fetchCompletionHandler %@", userInfo);
  int badge = (int) application.applicationIconBadgeNumber;
  if ( application.applicationState == UIApplicationStateInactive ) {
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge+1];
  }
  else if(application.applicationState == UIApplicationStateBackground) {
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge+1];
  }
  else if(application.applicationState == UIApplicationStateActive) {
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge+1];
  }
  completionHandler(UIBackgroundFetchResultNewData);
}
auspicious99
  • 3,902
  • 1
  • 44
  • 58
Beu
  • 1,370
  • 10
  • 23

3 Answers3

1

You need to create a UNNotificationServiceExtension to handle notifications when app is in background/killed.

It's a small application that will be executed on notification reception and has limited time to edit its content before presenting it to the user.

When you receive a notification in it's didReceive(_:withContentHandler:) delegate callback you can modify badge value of UNNotificationContent

  • 1
    I already have UNNotificationServiceExtension. But I have many users in a channel and I don't have the badge count of each user. How can I get the current badge count of the user inside didReceive(_:withContentHandler:)? – Beu May 11 '20 at 04:53
  • 1
    You can only show a total count in a badge. Per user badge count must be part of your main app logic, not local notifications extension logic that con only show a total. So keep track in a shared variable (e.g. in UserDefaults) the total amount and read/update it with extension and update/reset in main app. – LaughtingMan May 12 '20 at 15:34
1

In order to catch the incoming notification when the app is in the background or killed, use a UNNotificationServiceExtension in your project. The Info.plist for the UNNotificationServiceExtension (not the normal Info.plist for the main app; that one has normal things for the main app) might look something like enter image description here

In the - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler of the UNNotificationServiceExtension, you can update the badge the following way:

  1. self.bestAttemptContent = [request.content mutableCopy]; to get the request's content
  2. self.bestAttemptContent.badge = <desired_integer_value> , where <desired_integer_value> would be the integer that you wish to put for the badge count.
  3. self.contentHandler(self.bestAttemptContent); to complete the update of the content.

In many cases, the badge count may need to reflect a value (like number of unread chat messages) for a particular user. For that, you can use a shared user defaults. In fact NSUserDefaults supports the concept of app suite to allow such sharing. See Apple documentation for more details. In particular,

You can use this method when developing an app suite, to share preferences or other data among the apps, or when developing an app extension, to share preferences or other data between the extension and its containing app.

The argument and registration domains are shared between all instances of NSUserDefaults.

In a Constants.h file, have something to track individual counts for each user like

#define NOTIFICATIONS_UNREAD_SHARED  [NSString stringWithFormat:@"notificationsUnread-%@",[mySharedDefaults objectForKey:USERNAME]]

and in your app, you would save the individual counts for each user, to the app suite's shared user defaults, with something like

NSUserDefaults *mySharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.yourCompany.yourAppname"];
if ([[NSUserDefaults standardUserDefaults] objectForKey:USERNAME]) {
    mySharedDefaults setObject:[[NSUserDefaults standardUserDefaults] objectForKey:USERNAME] forKey:USERNAME];
    [mySharedDefaults setObject:[NSNumber numberWithInteger:arrExistingRead.count] forKey:NOTIFICATIONS_READ_SHARED];
    [mySharedDefaults setObject:[NSNumber numberWithInteger:([AppConstant sharedconstant].countObj.arrAllMessages.count - arrExistingRead.count)] forKey:NOTIFICATIONS_UNREAD_SHARED];
    [mySharedDefaults synchronize];
}

Then in your UNNotificationServiceExtension, you would do something like

NSUserDefaults *mySharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.yourCompany.yourAppname"];
if ([mySharedDefaults objectForKey:NOTIFICATIONS_UNREAD_SHARED]) {
    if ([mySharedDefaults objectForKey:USERNAME]) {
        self.bestAttemptContent.badge = [NSNumber numberWithInt:[[mySharedDefaults objectForKey:NOTIFICATIONS_UNREAD_SHARED] intValue]+1];
    } else { // username somehow not set; reset badge to 0
        self.bestAttemptContent.badge = @0;
    }
} else { // notifications unread count not found for this user; reset badge to 0
    self.bestAttemptContent.badge = @0;
}

Troubleshooting

In case the extension doesn't appear to be receiving the push notifications, some things to verify:

Look at the build targets. Besides the main app, there should be one for the extension too. enter image description here

In the settings for the main app, it should associate with the UNNotificationServiceExtension : enter image description here

Community
  • 1
  • 1
auspicious99
  • 3,902
  • 1
  • 44
  • 58
  • Thanks for ur response. I used react-native-user-defaults to store badge count. Now the problem is UNNotificationServiceExtension's didReceiveNotificationRequest method is not fired. – Beu May 13 '20 at 11:21
  • Did you implement the UNNotificationServiceExtension in the same project as your main app? – auspicious99 May 13 '20 at 12:07
  • Yes. I implemented. What should be the value of NSExtensionPrincipalClass in Info.plist? Where can I find my PRODUCT_MODULE_NAME? – Beu May 13 '20 at 12:16
  • I found it under BuildSettings => Packaging – Beu May 13 '20 at 12:36
  • 2
    cool, for the Info.plist of the `UNNotificationServiceExtension`, I just added a screenshot example from one of my real projects. Includes the NSExtensionPrincipalClass – auspicious99 May 13 '20 at 12:38
  • Thanks. I will try and come back. – Beu May 13 '20 at 12:40
  • I have "mutable-content": 1 in my notification payload. Set everything for NotificationServiceExtension. But didReceiveNotificationRequest is not fired. – Beu May 13 '20 at 12:42
  • What are you using in the backend? I heard if it is firebase, then use "mutable_content": 1, not "mutable-content": 1 – auspicious99 May 13 '20 at 12:46
  • Oh, how are you detecting if didReceiveNotificationRequest is fired? The debugging is a bit tricky, as it is like another small app, so if you are debugging your main app, and try to set a breakpoint in didReceiveNotificationRequest, it may not break. But you could try to set the badge? Does the value change? – auspicious99 May 13 '20 at 12:48
  • I am using APNS. not firebase. The badge value has not been changed. I set self.bestAttemptContent.badge = @20; directly to check whether it is working or not. – Beu May 13 '20 at 12:55
  • How about the 3rd step? `self.contentHandler(self.bestAttemptContent);` – auspicious99 May 13 '20 at 13:00
  • Yes. Its there. – Beu May 13 '20 at 13:12
  • Added some things to check for troubleshooting – auspicious99 May 13 '20 at 13:34
  • Oh, back to the Info.plist ; my extension class was called NotificationService.m, therefore the value of NSExtensionPrincipalClass was NotificationService. If your class has a different name, use your class name, right? – auspicious99 May 13 '20 at 14:25
  • 1
    And if you really want to try to debug the extension, see https://stackoverflow.com/questions/38140158/debug-notification-extensions – auspicious99 May 13 '20 at 14:27
  • My extension class name also NotificationService.m only. :( – Beu May 13 '20 at 15:30
  • Do we need any Podfile changes? – Beu May 15 '20 at 05:40
  • 1
    No, it is not dependent on cocoapods. Did you try the extension debugging to be more sure if the didReceiveNotificationRequest method is really not fired? – auspicious99 May 15 '20 at 06:12
  • What deployment target we should give? I have given 10.0 and my device ios version is 13.*. Is this correct? – Beu May 18 '20 at 08:45
  • Deployment target is just the minimum iOS version that it will run on. 10.0 should be fine, for your device. But now that you mentioned it, on my previous app, both the main app and the extension had iOS 10 deployment target. Is your extension having the same? (not sure if it is different, what would be the effect) – auspicious99 May 18 '20 at 09:48
  • same only. both are having 10.0 – Beu May 18 '20 at 10:44
  • I already have provisioning profile for main app.. Do I need to create a separate one for NotificationServiceExtension? – Beu May 18 '20 at 10:45
  • For the signing, I used "Automatically manage signing", so didn't create a separate one for the extension, but when I go and view it, the provisioning profile for the extension is slightly different, e.g., matches the bundle ID ending with NotifServiceExt. Also, only the main app has the capabilities/entitlements for push notifications in the provisioning profile. – auspicious99 May 18 '20 at 10:51
  • Do I need to add app group? – Beu May 19 '20 at 15:11
  • I thought app group was just for sharing the sharedPreference, not for the receiving. But maybe you can add now (since you'll need it for the sharedPreference) and hopefully it would help too. – auspicious99 May 19 '20 at 15:17
0

You need to set the badge field in the payload in the push notification. https://developer.apple.com/documentation/usernotifications/setting_up_a_remote_notification_server/generating_a_remote_notification

You will have to do the calculation server side.

INDAPP
  • 26
  • 1
  • 2