I want to do a badge increment on the main app icon while receiving a notification in inactive mode of the app
Asked Answered
T

3

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);
}
Tynes answered 4/5, 2020 at 4:40 Comment(1)
Please add some code with explanation, so we can understand.Castor
F
3

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

Frederik answered 12/5, 2020 at 7:29 Comment(25)
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.Tynes
Did you implement the UNNotificationServiceExtension in the same project as your main app?Frederik
Yes. I implemented. What should be the value of NSExtensionPrincipalClass in Info.plist? Where can I find my PRODUCT_MODULE_NAME?Tynes
I found it under BuildSettings => PackagingTynes
cool, for the Info.plist of the UNNotificationServiceExtension, I just added a screenshot example from one of my real projects. Includes the NSExtensionPrincipalClassFrederik
Thanks. I will try and come back.Tynes
I have "mutable-content": 1 in my notification payload. Set everything for NotificationServiceExtension. But didReceiveNotificationRequest is not fired.Tynes
What are you using in the backend? I heard if it is firebase, then use "mutable_content": 1, not "mutable-content": 1Frederik
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?Frederik
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.Tynes
How about the 3rd step? self.contentHandler(self.bestAttemptContent);Frederik
Yes. Its there.Tynes
Added some things to check for troubleshootingFrederik
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?Frederik
And if you really want to try to debug the extension, see #38140658Frederik
My extension class name also NotificationService.m only. :(Tynes
Do we need any Podfile changes?Tynes
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?Frederik
What deployment target we should give? I have given 10.0 and my device ios version is 13.*. Is this correct?Tynes
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)Frederik
same only. both are having 10.0Tynes
I already have provisioning profile for main app.. Do I need to create a separate one for NotificationServiceExtension?Tynes
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.Frederik
Do I need to add app group?Tynes
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.Frederik
A
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

Anstice answered 10/5, 2020 at 12:30 Comment(2)
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:)?Tynes
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.Anstice
T
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.

Turnbuckle answered 9/5, 2020 at 13:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.