didReceiveRemoteNotification when in background
These kind of question has been asked a number of times but i have some specific situation going on.
When my application is active and I receive a PUSH message i'm successfully able to parse the custom payloads and such.
However when my application is in the background and the PUSH arrives the user has to click on the 'View/Open' button in order to get the didReceiveRemoteNotification
called and the didFinishLaunchingWithOptions
is called after that.
I need to have my application decide if they have to prompt the user with an UIAlert when in the background or suppress the push message based on some local settings.
Any help would be appreciated,
Solution 1:
You app needs to handle all the possible push notification delivery states:
Your app was just launched
Your app was just brought from background to foreground
Your app was already running in the foreground
You do not get to choose at delivery time what presentation method is used to present the push notification, that is encoded in the notification itself (optional alert, badge number, sound). But since you presumably are in control of both the app and the payload of the push notification, you can specify in the payload whether or not there was an alert view and message already presented to the user. Only in the case of the app is already running in the foreground do you know that the user did not just launch your app through an alert or regularly from the home screen.
You can tell whether your app was just brought to the foreground or not in didReceiveRemoteNotification using this bit of code:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if ( application.applicationState == UIApplicationStateActive )
// app was already in the foreground
else
// app was just brought from background to foreground
...
}
Solution 2:
You have to do several things in order to manage the received push notification when the app is in background.
First, in your server side, you have to set {"aps":{"content-available" : 1... / $body['aps']['content-available'] =1;
in the push notification payload.
Second, in your Xcode project, yo have to habilitate "remote notifications". It is made by going to the project's target -> capabilities, then enable the capabilities switch, and check the remote notifications checkbox.
Third, instead of using didReceiveRemoteNotification
, you have to call application:didReceiveRemoteNotification:fetchCompletionHandler:
, this will allow you to do the tasks that you want in the background, at the moment of receiving the notification:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
if(application.applicationState == UIApplicationStateInactive) {
NSLog(@"Inactive - the user has tapped in the notification when app was closed or in background");
//do some tasks
[self manageRemoteNotification:userInfo];
completionHandler(UIBackgroundFetchResultNewData);
}
else if (application.applicationState == UIApplicationStateBackground) {
NSLog(@"application Background - notification has arrived when app was in background");
NSString* contentAvailable = [NSString stringWithFormat:@"%@", [[userInfo valueForKey:@"aps"] valueForKey:@"content-available"]];
if([contentAvailable isEqualToString:@"1"]) {
// do tasks
[self manageRemoteNotification:userInfo];
NSLog(@"content-available is equal to 1");
completionHandler(UIBackgroundFetchResultNewData);
}
}
else {
NSLog(@"application Active - notication has arrived while app was opened");
//Show an in-app banner
//do tasks
[self manageRemoteNotification:userInfo];
completionHandler(UIBackgroundFetchResultNewData);
}
}
Finally, you have to add this notification type: UIRemoteNotificationTypeNewsstandContentAvailability
to the notifications settings when you set it.
Apart from this, if your app was closed when the notification arrived, you have to manage this in didFinishLaunchingWithOptions
, and just if the user taps on the push notification: The way of do it is:
if (launchOptions != nil)
{
NSDictionary *dictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (dictionary != nil)
{
NSLog(@"Launched from push notification: %@", dictionary);
[self manageRemoteNotification:dictionary];
}
}
launchOptions is != nil when you launch the app by tapping on the push notification, if you access it by tapping on the icon, launchOptions will be == nil.
I hope it will be useful. Here it is explained by Apple.
Solution 3:
Pass content-available = 1
with your payload, and will invoke didReceiveRemoteNotification
even in background. e.g.
{
"alert" : "",
"badge" : "0",
"content-available" : "1",
"sound" : ""
}
Solution 4:
One thing to keep in mind, when your push message arrives at the user's iPhone and they click "cancel", except for the icon badge number (they'll be taken care of by the OS), there would be no way for your in-the-background app to know about this push event and take any further actions.
Solution 5:
Word of warning
I think your app logic is basing behavior on custom data in your push notification. This is not what push notifications are intended for. What you should alternatively do on didbecomeactive
in your application is just ask your server for the data you need and was sending as payload anyway, and rely on that instead of your payload.
Because the documentation also states that as best practice. Because Apple does not guarantee your push notification from being received 100% of the times anyway.
Important: Delivery of notifications is a “best effort”, not guaranteed. It is not intended to deliver data to your app, only to notify the user that there is new data available.
However, if you want to have some indication of whether for instance the badge has been changed without relying on a user opening the app by clicking on the badge you could something like this:
A. you add a (correct) badge number to the payload of the push notification sent by your server. It for instance could look like this:
{
"aps" : {
"alert" : "You got your emails.",
"badge" : 9
}
}
B. you keep track of that badge number persistently in your app, for instance by storing it in NSUserDefaults
.
Then in applicationDidBecomeActive
can compare the applicationIconBadgeNumber
property of UIApplication
with your previously stored badge count and see if it has been increased or decreased and do some updates based on that.
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSNumber *badgecount = [[NSUserDefaults standardUserDefaults] objectForKey:@"badgecount"];
if (!badgecount) badgecount = @(0);
if ([UIApplication sharedApplication].applicationIconBadgeNumber != [badgecount integerValue]) {
//store the new badge count
badgecount = [NSNumber numberWithInteger:[UIApplication sharedApplication].applicationIconBadgeNumber];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:badgecount forKey:@"badgecount"];
[defaults synchronize];
// do some stuff here because it's different
}
}