24

I want to implement background refresh functionality in my app for when a push is received. Just before the push notification is displayed to the user I want to download the new messages from my backend (Parse.com) and save them to an array. I am following a guide from here: http://developer.xamarin.com/guides/ios/application_fundamentals/backgrounding/part_3_ios_backgrounding_techniques/updating_an_application_in_the_background/

I'm not sure how accurate this guide is. It states: iOS 7 (and greater) extends ordinary push notifications by giving applications a chance to update content in the background before notifying the user, so that the user can open the application and be presented with new content immediately.

So I tried implementing my background push like this:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))handler
{


    if([[userInfo objectForKey:@"aps"] objectForKey:@"content-available"]){

        NSLog(@"Doing the background refresh");
        UINavigationController *navigationController=(UINavigationController *)[[[UIApplication sharedApplication] keyWindow] rootViewController];

        MyViewController *myViewController = (MyViewController *)[[navigationController viewControllers] objectAtIndex:1];

        [myViewController.currentUser refreshMessagesArrayWithCompletionHandler:^(BOOL successful, BOOL newMiaos) {

            NSLog(@"messages refreshed the array now has %lu messages",(unsigned long)[myViewController.currentUser.messages count]);
            handler(UIBackgroundFetchResultNewData);
        }];
    }
}

The background refresh is called and the push is displayed, however the push notification does not wait for the background task to finish. It is just displayed as soon as it is received. Is this correct functionality? The tutorial above suggests that a notification won't be displayed until the background task is completed.

I then went about trying a silent notification, this triggers the app to download the messages in the background when the push is received but no notification is displayed. So I do this by firing a local notification instead after the download completes. Is this really the correct way of doing it? Do traditional apps such as whatsapp trigger a background refresh using a silent notification and then fire a local one? Seems a bit hacky. Surely the idea of a background push is to get the data ready before showing the notification but it doesn't quite work like that..

The other thing I noticed is that silent notifications are rate limited they have a lower priority than a typical push notification so surely this hinders the efficiency of the app also...

Any pointers on this would be really appreciated. Just trying to get my head around if I'm approaching this the right way or not. All seems very hacky...

casillas
  • 16,351
  • 19
  • 115
  • 215
Kex
  • 8,023
  • 9
  • 56
  • 129
  • i have applied this but , application didReceiveRemoteNotification: did not open from a push notification when the app was in background . do you know why ? and what method to use when we want to receive push notification and want to call some procedures while app is in background ? ... i am getting notification messages but could not enter into the procedure until we press and open the app . any other way of doing this process ? – Moxarth Jul 13 '17 at 10:52

2 Answers2

19

I've been struggling with the same task in my messaging app. We wanted users to see the message right before user taps the notification. What we faced with:

  • The payload size limitation. iOS 7 can have only 256 bytes for payload
  • single silent notifications will not start an app, if it is not running
  • content-available notifications without alert body may even not be delivered to the device
  • Background fetch is not controlled by your app, so you may never receive the desired signal, so we cannot rely on this feature. But this may be helpful as an additional way to achieve what we want
  • iOS 8 has a lot of space for payload - 2KB
  • if you send alert body and content-available - it will be delivered in most cases and an app is able to process it

So we came to the only acceptable solution: we decided to make this feature in ios8+ only. We send visible push notifications with content-available key which allows us to process the notification payload if the process is running/frozen and both be able to present the notification if the app is not running. If an app receives push notification, it takes alert text body and writes it into local database, so the user is able to read it in conversation. According to our stats, the average size of the message is not more than 200 symbols, so most of the time no extra requests are required. If the message is longer than 200 symbols, we extend payload body with extra parameter which is used to request text body in push notification processing. The user will see a cropped version of text, but after a request is done, we rewrite a message in local database with the received value.

So, that technique allows us to show a received message to the user immediately in most of the cases + if the app was not running, we make a request to our server to fetch missing messages right after application start. This is the fastest and the most acceptable case we could get on iOS. Hope my experience will help you to implement what you want.

Sega-Zero
  • 3,034
  • 2
  • 22
  • 46
  • Hey, thanks for this. Just wanted to ask a couple of things. You said Silent notifications without an alert body may even not be delivered to a device. A silent notification without an alert body is what a silent notification is no? From the Apple documentation it would seem that silent and standard notifications have the same priority. – Kex Jun 15 '15 at 08:22
  • wrote that at night =) I meant `content-available` key, of course. Silent notifications may not be delivered to device an some cases. For example, if there was an airplane mode on, after switching it off, you will not receive all missing notifications, you will instead receive only last one. And it's not a silent one. Push notifications with both body and `content-available` will be delivered in most cases. – Sega-Zero Jun 15 '15 at 09:16
  • Say you are sending push notifications with content-available = 1 and remove the alert, this is how you would send a silent notification. However this requires the user to have the background modes enabled. How can we get any message to the device if backgrounding is turned off? We can't determine if backgrounding is turned on or off for the receiving user and decide if to send a normal or silent push. How do you get around that? – Kex Jun 15 '15 at 10:17
  • As i said above, we just send a normal push notification with the alert body WITH `content-availiable` key. That notification will be delivered to device even if background mode is turned off by user, an app just wont be able to process it. But the user will definitely see it. In that case there isn't much we can do, so we're waiting for foreground and get all missed messages from our server. – Sega-Zero Jun 15 '15 at 10:25
  • Ah, one last thing then I will get out of your hair. So if doing it with alert body and content-available does that mean if the user opens the push notification very fast the background fetch may not be finished and there will be a delay in the foreground when loading the app message? – Kex Jun 15 '15 at 10:37
  • No. Your app will receive that signal immediately when notification occurs. No matter how fast user is, the processor is faster =) If you write alert's body right into database - the user will see it right after tapping the notification. In a rare cases (when message length is more than 200 symbols. You may choose a different length btw, 2 KB of payload is a lot of space) the user may see a cropped version of text for a short period of time, which will then be replaced by server answer. My stats shows that even on edge, a short http request will be fast enough even on edge network. – Sega-Zero Jun 15 '15 at 10:47
  • Got it. Thanks for your detailed answer. Hopefully this will be helpful for others too. Nice UI design on 4talk btw. – Kex Jun 15 '15 at 11:03
  • i have applied this but , application didReceiveRemoteNotification: did not open from a push notification when the app was in background . do you know why ? and what method to use when we want to receive push notification and want to call some procedures while app is in background ? ... i am getting notification messages but could not enter into the procedure until we press and open the app . any other way of doing this process ? – Moxarth Jul 13 '17 at 10:50
  • first of all, you need `application:didReceiveRemoteNotification:fetchCompletionHandler` in order to be able to process remote notifications. second, add `content-available: 1` to your push json payload, so they could be processed by application immediately. call completion handler after you process the payload and call your code, but remember: you have a very small amount of time to do that – Sega-Zero Jul 13 '17 at 10:56
  • application:didReceiveRemoteNotification:fetchCompletionHand‌​ler did not get called until we tap on the notification message . we tap on the notification message and app gets open , then only we could get inside of application:didReceiveRemoteNotification:fetchCompletionHand‌​ler . so without taping on that how to fetch message ? – Moxarth Jul 13 '17 at 12:45
  • Did you added the content-available field to payload? – Sega-Zero Jul 13 '17 at 12:56
  • yes . i have tried that also . but same . i am getting notification messages . but could not get inside the method application:didReceiveRemoteNotification:fetchCompletionHand‌​ler , until user taps on the notification . – Moxarth Jul 14 '17 at 05:00
  • You should be able to process the push payload if you add this field correclty, check that twice. Also, this will happen only if the app is already launched and living in the background. If you expect system to launch your app - that's possible only for voip notifications. – Sega-Zero Jul 14 '17 at 07:27
  • i am receiving like this notification.payload" = "{\"content-available\":1}"; . is this correct one ? because still i could not enter into that method . – Moxarth Jul 17 '17 at 13:13
  • Have you enabled the background mode in capabilities? – Sega-Zero Jul 17 '17 at 13:50
  • yes. after that only i am receiving the notifications . – Moxarth Jul 18 '17 at 06:36
9

You mixed a few of things together.

From a quick look at your link, this is a guide for xamarin. There might be some correct info there but if you are not using xamarin I'd search for another tutorial.

A good approach would be to send a silent notification to the user and triggering a local notification when it's done (which is not hacky at all).

This is how whatsApp is making it work:

While whatsApp is in the background, a single push notification is received, (for example "5") That msg will not be shown to the user.

whatsApp receives it in the method application:didReceiveRemoteNotification:fetchCompletionHandler: and checks against their servers if there are any notifications prior to "5" that the user didn't receive. If that's the case, they will pull that data from their servers and will present it to the user using local notifications which is basically just a way to present data and not related to APNS at all.

You can read the full answer & context in another answer I wrote here

Segev
  • 19,035
  • 12
  • 80
  • 152
  • 1
    Do silent notifications have a lower priority than standard APNS? I know that standard APNS are a "best effort" thing, but am worried that silent notifications are even lower priority than this and won't go through. – Kex Jun 15 '15 at 08:07
  • According to apple Delivery of all notifications is a “best effort”. I'm not aware of priority differentiation between notifications types – Segev Jun 15 '15 at 08:14
  • 1
    Yeah that's what I read too. Think it's that xamarin document confusing things. Using silent notifications requires the app to have the background mode permission, is this right? Would that mean that if the user disables it all the notifications will be rendered completely useless? e.g. alert="" for a silent notification so no message will be displayed at all because we can't reference anything on a server without a background mode and hence have nothing to put in the push. – Kex Jun 15 '15 at 08:20
  • Here's a good tutorial just for that: http://www.g8production.com/post/70884102379/ios7-multitasking-the-background-fetch – Segev Jun 15 '15 at 08:26
  • Thanks. I see there are two methods one for handling standard push one for silent. But if you are sending a silent push and background modes are disabled surely just nothing happens as content-set=1 so AppDelegate.m would interpret it as a silent notification but can't do anything as the background mode is off. Or will it call - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo? – Kex Jun 15 '15 at 08:38
  • 1
    To be able to process silent push notifications, you need `application:didReceiveRemoteNotification:fetchCompletionHandler:` only + `remote-notification` key to `UIBackgroundModes`. You are the one who set the priority, but if you use pase.com, it will lower the priority for silent notifications. – Sega-Zero Jun 15 '15 at 09:19