Silent push notifications only delivered if device is charging and/or app is foreground

IosIphoneBackgroundApple Push-NotificationsSilent Notification

Ios Problem Overview


I have implemented silent push notifications but I have noticed some strange behaviour. The silent push notifications are handled via:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler

The silent push messages only seem to be received if the device is charging (ie cable connected) and/or if my app is foreground.

If I disconnect the device from the charger (or Mac) then the silent push notifications are no longer received unless the app is foreground.

I get non-silent push notifications normally in both cases.

If I plug in the USB cable again, then I get the expected behaviour and silent push notifications are received irrespective of whether the app is foreground or background.

I am using UILocalNotification so I know what is being received.

The fact that it all works fine with the device connected suggests that my silent pushes notifications are configured correctly and that the app has the correct background modes set in the plist etc.

This behaviour is repeatable on iPhone 5s, 6 and iPad 2 all running either IOS 8 or 8.1.

Has anyone else experienced this? It should be easy to reproduce. Why should the simple act of plugging a device into a charger change the ability to receive silent push notifications?

Ios Solutions


Solution 1 - Ios

We have experienced the same behavior and have been trying to understand why iOS decides to deliver some notifications and not others.

What we have worked out so far is:

  • Messages will get received more reliably in background when on wifi than on cellular data. In fact, when on a cellular network (3g/4g), if your signal strength isn't strong enough, iOS receives the push message, but will not wake up your app. We posted in the apple forums about it here: https://devforums.apple.com/message/1069814#1069814. We also opened up a support ticket, and the support team told us to lodge it as a bug report, which we did a couple of weeks ago and are still waiting to hear back.

  • When you receive a push message, you need to call the fetchCompletionHandler as soon as possible. Technically you have 30 seconds to perform background processing, but iOS has in place a formula whereby the more frequently you send push messages and depending on the amount of time you spend processing those message before returning the app to suspended state, iOS can reduce the amount of times your app gets woken up in the future.

See here from Apple didReceiveRemoteNotification:fetchCompletionHandler: documentation:

> As soon as you finish processing the notification, you must call the > block in the handler parameter or your app will be terminated. Your > app has up to 30 seconds of wall-clock time to process the > notification and call the specified completion handler block. In > practice, you should call the handler block as soon as you are done > processing the notification. The system tracks the elapsed time, power > usage, and data costs for your app’s background downloads. Apps that > use significant amounts of power when processing push notifications > may not always be woken up early to process future notifications.

In our testing, we have been sending frequent silent push notifications to our app (every 10 - 30 seconds). And the app is awake for about 3 seconds before we put it back to sleep. We have definitely noticed over time a degradation in the frequency in which our app gets woken up to the point where iOS will only wake up the app every 15 - 30 minutes. So there seems to be some sort of decay/throttling formula in place, but we cannot find any documentation on how it exactly works. We have requested this formula and the variables from apple as a support request, but they said "The information you are requesting is not publicly available" and again asked us to file a bug report.

So, hopefully this is helpful? We are still trying to learn more ourselves which is why I found this question :)

Solution 2 - Ios

With iOS8 background push delivery to apps has changed. A background push will now only be delivered to the app under certain circumstances. Apple have not stated explicitly what these circumstances exactly are but from my extensive experimentation it basically comes down to if the phone is being charged or not. There are some other variables at play (such as network type, device type, wifi enabled) but the major major major factor is whether the device is being charged or not when the push arrives.

If the phone is being charged via a direct mains power supply or indirectly by being connected by USB to a computer then the background pushes will get delivered to the app the vast majority of the time. But disconnect the phone from the power supply or USB and the background push will almost never get delivered to the app, even if the phone's batter has a 100% charge.

You can very easily test this for yourself just by sending some pushes while the phone is being charged versus while its not. BUT you must take into account that background pushes with a development build and using the sandbox environment DO NOT behave the same as background pushes with a production build and a production environment, the background pushes are actually more likely to be delivered to the app in development then they are in production so it is vital you test using a production build and Apple's production environment to see the actual results.

Note there are two steps the push delivery, the first is it needs to get delivered to the phone itself, the second is once the phone has it, it then needs to get delivered by the OS to the app. In iOS7 things such as turing on Wifi made the chances of the push getting to the phone increase. With iOS8 however even though the push is successfully being delivered to the phone, the OS is not forwarding it on to a background app if the phone isn't being charged. This means the phone gets the notification and holds on to it, sometimes for several hours, before it might forward it to the app if the phone isn't being charged.

Solution 3 - Ios

I had experienced the same problem and the reason behind not receiving push notification while the app is not charging is that when the Low Power Mode is enabled from Settings > Battery it disables background-fetch feature for all applications.

Which prevents device from receiving push notification.

This link might be useful. Apple Documentation

Solution 4 - Ios

I also noticed the same and wasted some time figuring out. See https://stackoverflow.com/a/31237889/1724763

If you turned off Bg App Refresh, silent remote push will be dropped silently (the irony).

However, my observation is that if you connect to Xcode via cable, somehow the Bg App Refresh setting is ignored and all silent push for your app works.

I highly suspect this is an undocumented feature: charging causes the Bg App Refresh setting to be ignored.

Solution 5 - Ios

It's not working because you have enabled the wrong background mode in the plist. You need to enable the remote-notification tag (App downloads content in response to push notifications), not fetch. Fetch is used for something else. You may also need to use the content available key in your JSON payload, e.g.,

{
   "aps": {
      "content-available": 1
    },
    "yourdatakey":{data}
}

Solution 6 - Ios

I hope you are using APNS delivering priority as "CONSERVE_POWER" (5), try to change it as "IMMEDIATE" (10)

Solution 7 - Ios

I've been experiencing this problem for some time, and am very grateful for this question and @Kevin D. sharing their understanding. I'm beginning to think that https://stackoverflow.com/a/30834566/1449799 and https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW4 (see priority in one of the tables) are describing why my app is having trouble:

> It is an error to use this priority for a push that contains only the content-available key.

To send the notifications, I'm using node-apn where the default (which i also need) is to set the priority to max (10 [beware, it looks like only 10 and 5 are correct values at this time]), but since i wanted a silent notification, i don't have alert, badge, or sound set.

Solution 8 - Ios

IF YOUR APP IS NOT VoIP YOU CAN'T FOLLOW THIS ANSWER [your app will be rejected]

I found another solution that is worked for me using PushKit Framework

VoIP pushes provide additional functionality on top of the standard push that is needed to VoIP apps to perform on-demand processing of the push before displaying a notification to the user

When I send VOIP Push the App wakes up whatever the state of the Application and can perform any operations

Register for VOIP PushNotification in didFinishLaunchingWithOptions

 PKPushRegistry *pushRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];
pushRegistry.delegate = self;
pushRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];


- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type{
if([credentials.token length] == 0) {
    NSLog(@"voip token NULL");
    return;
}

NSString *originalToken=[NSString stringWithFormat:@"%@",credentials.token];
NSString *token = [originalToken stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@"<>"]];
token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
NSLog(@"PushCredentials: %@",token);}

then you can handle any background fetch in this function once you receive VOIP PushNotification

-(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type

NOTE: you must use certificate that enable VoIP Services Certificate

enter image description here

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1024447View Question on Stackoverflow
Solution 1 - IosKevin D.View Answer on Stackoverflow
Solution 2 - IosGruntcakesView Answer on Stackoverflow
Solution 3 - IosManan DevaniView Answer on Stackoverflow
Solution 4 - Iosuser1724763View Answer on Stackoverflow
Solution 5 - IosjrlockeView Answer on Stackoverflow
Solution 6 - IosMohamed yaseenView Answer on Stackoverflow
Solution 7 - IosMichaelView Answer on Stackoverflow
Solution 8 - IoskhaledView Answer on Stackoverflow