172
votes

I noticed that on iOS 11 beta 2, the silent notifications are not delivered to the application:didReceiveRemoteNotification:fetchCompletionHandler regardless the state of the app (background / foreground).

I implemented the UIApplicationDelegete method application:didReceiveRemoteNotification:fetchCompletionHandler and I send the following silent push

{  
  "aps": {  
    "content-available": 1  
  },  
  "mydata": {  
    "foo": "bar"  
  }  
} 

but the delegate method is not called on iOS 11.

It works fine on other versions of iOS and the documentation section Configuring a Silent Notification does not mention that anything else should be done.

Is this a bug in iOS 11 or did I miss something new in iOS 11?

Please note that I'm not talking about or using the UserNotification framework which should not be needed for sending silent pushes.

Here is a sample project that illustrates the issue (you'll have to set your own bundle id)

When you lunch the sample project and send a above payload to the app, you can use the macOS console to see that the push is correctly delivered to the device but not to the app.

UPDATE 10.08

It appears that the behaviour is random. Sometimes after restarting the device, the payload is delivered correctly but it stops working after a while.

As you can see on the following screenshot, the push marked as 1 is delievered only to the device and the push 2 (after device restart) is also delivered to the app.

enter image description here

UPDATE 14.08 - iOS 11 Beta 6

Still the same behaviour. Another thing that is supposed to work but does not is the following. When the application's scheme is set to "Wait for executable to be launched", a silent push is supposed to wake up the app and start it in background.

enter image description here

UPDATE 21.08 - iOS 11 Beta 7

Still the same behaviour and not updates from Apple in the bug report.

UPDATE 29.08 - iOS 11 Beta 8

Still the same problem. The steps to reproduce I use now are the following:

  • In the Xcode project scheme, select "Wait for executable to be launched"
  • Add a breakpoint in the didReceiveRemoteNotification: fetchCompletionHandler
  • Start the app on device
  • Send the above silent push

Expected: The app is brought from suspended state to background and the didReceiveRemoteNotification: fetchCompletionHandler is called

Actual: nothing happens

UPDATE 06.09 - iOS 11 Beta 10

I'm still having the same buggy behaviour. The ticket from Apple was updated with the following answer:

Apple Developer Relations September 6 2017, 10:42 PM Engineering has provided the following feedback regarding this issue:

We were able to get the sample app running and test the behavior. We didn’t see any issues when we tested this as described.

Pushes aren't guaranteed to arrive to the app when it is running in the background, and the logs here indicate we don't believe the app is being used enough to launch it.

We do see us delivering pushes from time to time when conditions are good.

We believe this is behaving correctly.

Update 11.09

My Apple bug report was closed and marked as duplicate of 33278611 which remains open

UPDATE 13.09 - iOS 11 GM

Thanks to kam800's comments (see below) I did more testing and came up with those observations:

There seem to be a new daemon in iOS 11 dasd DuetActivitySchedulerDaemon that either completely discards the data push or delays the data push delivery:

Delivery postponed

Console Logs

default 13:11:47.177547 +0200   dasd    DuetActivitySchedulerDaemon CANCELED: com.apple.pushLaunch.net.tequilaapps.daylight:C03A65 <private>!   lifecycle   com.apple.duetactivityscheduler
default 13:11:47.178186 +0200   dasd    DuetActivitySchedulerDaemon Removing a launch request for application <private> by activity <private>   default com.apple.duetactivityscheduler
default 12:49:04.426256 +0200   dasd    DuetActivitySchedulerDaemon Advancing start date for <private> by 6.5 minutes to Wed Sep 13 12:55:31 2017   default com.apple.duetactivityscheduler
default 13:21:40.593012 +0200   dasd    DuetActivitySchedulerDaemon Activity <private>: Optimal Score 0.6144 at <private> (Valid Until: <private>)  scoring com.apple.duetactivityscheduler
default 13:21:40.594528 +0200   dasd    DuetActivitySchedulerDaemon Setting timer (isWaking=1, activityRequiresWaking=0) between <private> and <private> for <private>  default com.apple.duetactivityscheduler

Postponed delivery issues

  • When the data push delivery is postponed and the app is launched, the data push is delivered only when the delivery date is reached which can be several minutes in the future. This defeats completely the purpose of using data pushes to keep the new app's content ready for the next launch. I quote here once again Apple's documentation:

"Silent notifications improve the user experience by helping you keep your app up-to-date, even when it is not running."

  • When two data pushes are sent to a suspended app they are postponed by iOS 11 instead of waking up the app directly. When delivery time is reached, only the last data push is delivered! The previous pushes are lost and not delivered via the delegate method resulting in a data loss.

Delivery cancelled

Console Logs

default 13:35:05.347078 +0200   dasd    DuetActivitySchedulerDaemon com.apple.pushLaunch.net.tequilaapps.daylight:C03A65:[
    {name: ApplicationPolicy, policyWeight: 50.000, response: {Decision: Must Not Proceed, Score: 0.00}}
 ], FinalDecision: Must Not Proceed}    scoring com.apple.duetactivityscheduler

Cancelled delivery issues

Well in this case, the data push is completely lost and never delivered on iOS 11 while it was delivered correctly on iOS 10.

UPDATE 19.09 - iOS 11 GM

I also noticed that when the application is in the foreground and the notification is not delivered to the app, I see the following logs in the console:

default 08:28:49.354824 +0200   apsd    apsd    <private>: Received message for enabled topic '<private>' onInterface: NonCellular with payload '<private>' with priority 10 for device token: NO   courier-oversized   com.apple.apsd

fault   08:33:18.128209 +0200   dasd    Foundation  <NSXPCConnection: 0x151eee460> connection from pid 55: Exception caught during decoding of received message, dropping incoming message.
Exception: Exception while decoding argument 0 (#2 of invocation):
Exception: value for key 'NS.objects' was of unexpected class 'NSNull'. Allowed classes are '{(
    NSArray,
    NSData,
    NSString,
    NSNumber,
    NSDictionary,
    NSUUID,
    _DASActivity,
    NSSet,
    _DASFileProtection,
    NSDate,
    NWParameters,
    NWEndpoint
)}'.    general com.apple.foundation.xpc
14
still not fixed in Beta 8, when I look in the console I see the following error: <NSXPCConnection: 0x123f43620> connection from pid 58: Exception caught during decoding of received message, dropping incoming message. Exception: Exception while decoding argument 0 (#2 of invocation): Exception: value for key 'NS.objects' was of unexpected class 'NSNull'. Allowed classes are '{( NWParameters, NWEndpoint, NSArray, NSData, NSString, NSNumber, NSDictionary, NSUUID, _DASActivity, NSSet, _DASFileProtection, NSDate )}'.Thomas Einwaller
I'm getting the same result with iOS 11 (not before), if I send with the push with "content-available": 1, and the app is in foreground, the callback will not be fired.GoRoS
After testing with the new iOS11.1 beta 1 it appears that this has been fixed now and is working as it was before on iOS 10Lee
Whatsapp seem to have had a similar issue whatsappen.com/news/5465/… Something about users that "habitually force close their apps" which is most developers...toxaq
And exactly the same with the public release of 11.1. If you are using silent pushes and your app is in the foreground, don't expect them to be delivered depending upon several things, but primarily battery levels, even if the device is plugged in to a power supply.Gruntcakes

14 Answers

31
votes

So the release notes of iOS 11.1 beta 1 say

iOS 11.1 beta 1 was just released and they mention: "Notifications Resolved Issues • Silent push notifications are processed more frequently. (33278611)

I did some tests and it seems to be indeed fixed:

Suspended State

When I launch the app in a suspended mode and send a silent push, the app is brought back to background and the didReceiveRemoteNotification:fetchCompletionHandler delegate is called.

Foreground State

In the same way, when the application is in foreground and a silent push is sent, the delegate seems to be called as expected. This was randomly not working in previous iOS 11 versions so I will confirm this after more testing.

18
votes

Just wanted to add my 2 cents in here as I've been hit by this issue too and I've noticed that Apple has closed several radars on this issue saying they couldn't reproduce. An interesting thing I found is that the pushes will get delivered if the app is backgrounded while it's attached to the debugger.

If I kill the debugger, unplug my phone, launch the app, and send the silent push payload I see the app NOT getting woken. I do see the in the Console log that the system cancels delivery of the payload to my app.

I've submitted a radar with a small sample app that reproduces the problem. I've also explicitly noted in the radar that the person working on my ticket must not be running the app attached to the debugger to reproduce the issue. Here's the link: https://bugreport.apple.com/web/?problemID=34461063

Hopefully this will cause some progress to be made on this issue.

14
votes

Looks like a new behaviour of iOS 11. iOS 11 beta 10 provides some descriptive logs regarding this issue:

default 23:18:51.806011 +0200   dasd    com.apple.pushLaunch.com.acme.Acme:F7E7D0:[
    {name: ApplicationPolicy, policyWeight: 50.000, response: {Decision: Can Proceed, Score: 0.50}}
    {name: BatteryLevelPolicy, policyWeight: 1.000, response: {Decision: Can Proceed, Score: 0.87, Rationale: [{batteryLevel == 62}]}}
    {name: DeviceActivityPolicy, policyWeight: 5.000, response: {Decision: Can Proceed, Score: 0.20}}
 ] sumScores:52.279483, denominator:81.410000, FinalDecision: Can Proceed FinalScore: 0.642175}
default 23:18:51.806386 +0200   dasd    'com.apple.pushLaunch.com.acme.Acme:F7E7D0' has compatibility score of 1.000000 with 'com.apple.CFNetwork-cc-111-79:E7272D'. Relaxing scores.
default 23:18:51.806855 +0200   dasd    'com.apple.pushLaunch.com.acme.Acme:F7E7D0' CurrentScore: 0.642175, ThresholdScore: 0.738454 DecisionToRun:0

Looks like every silent push is delivered to the iOS, but dasd daemon uses couple of policies to decide if silent push should be delivered to the app (e.g. battery level). I managed to receive one silent push yesterday night, but my iPhone was connected to charger at that time – probably BatteryLevelPolicy score was high enough to receive that one silent push.

Apple gives no official information about this iOS-side behaviour, there is only information about server-side throttling:

Silent notifications are not meant as a way to keep your app awake in the background, nor are they meant for high priority updates. APNs treats silent notifications as low priority and may throttle their delivery altogether if the total number becomes excessive. The actual limits are dynamic and can change based on conditions, but try not to send more than a few notifications per hour.

I keep my fingers crossed they changed that behaviour, because that would fix my app :) On the other hand, this change is good – one among many things making iPhone battery lasting longer that Android phones.

9
votes

iOS 11.1 beta release notes include: Notifications Resolved Issues Silent push notifications are processed more frequently. (33278611)

7
votes

iOS 11.1 Beta 2 also contains

Notifications
Resolved Issues
• Silent push notifications are processed more frequently. (33278611)

in Release Notes - will test it now.

UPDATE - 11.10.2017 - iOS 11.1 Beta 2

After using our App for 2 days in "real world scenarios" it looks like there is a real improvement in this version of iOS. I am cautiously starting to believe this is fixed.

7
votes

Apple Developer Relations just added a comment to my radar:

We believe this issue is resolved in the latest iOS 11.2 beta.

Please test with the latest iOS beta. If you still have issues, please update your bug report with any relevant logs or information that could help us investigate.

https://developer.apple.com/download/

currently installing iOS 11.2 beta - will test silent push behaviour

3
votes

I had similar issue with my app, till iOS 10 I was getting push notifications and application:didReceiveRemoteNotification:fetchCompletionHandler were getting called correctly.But when updated to iOS 11 push notifications stopped working.

Issue with my code was , even though i was using content-available:1 and mutable-content:1 in push notification payload,the Background Fetch option was not turned on. But It was working perfectly till iOS 10.

Make sure you turned ON both these capabilities.

After Turning Background Fetch capability ON it is working Now

3
votes

iOS 11.4.1, Swift 4

I was having issues with silent pushes not arriving (from CloudKit) and I tried everything everyone has mentioned here. Then I decided to try setting a blank alertBody to my CKNotificationInfo() objects like this:

let info = CKNotificationInfo()
info.shouldSendContentAvailable = true
info.alertBody = ""

This made the pushes get sent at a higher priority (but they were still silent pushes) and I no longer got the error in my device logs that the push was being ignored.

I hope that helps someone. :)

2
votes

So this is indeed a bug in iOS 11 and it's now fixed in iOS 11 beta 3. The application:didReceiveRemoteNotification:fetchCompletionHandler is now called correctly when a silent push is received both in foreground or in background.

UPDATE

No it is not fixed and is still happening in iOS beta 3 and 4

2
votes

As a workaround We're adding a "notification" key and inside a "title" with empty string as a value. This is wake up the didReceive callback in the appDelegate.

1
votes

As of writing this answer I am facing the exact same issue as Bill Dunay's answer.

My requirement was to receive silent notifications when the app is in foreground and nothing when the app is in the background/not running. And my workaround was this. I do not use badges hence setting it to zero is not an issue for me.

{
    "aps" : {
        "badge" : 0,
        "sound" : ""
    },
    "mydata": {  
        "foo": "bar"  
    }  
}

Please note I am deliberately not using "content-available". Setting that causes the iOS optimization logic to kick in delaying/cancelling the delivery of the notification.

1
votes

I have been getting the same issue for some notifications (not necessarily silent).

After reviewing all the updates and answers I am able to add two updates that may help:

  • I found that accessing UIApplication.shared.isRegisteredForRemoteNotifications method while receiving a notification causes the application to stall without reporting anything to Xcode. Check if you are running some code after you receive the notification that accesses the method. (isRegisteredForRemoteNotifications locking UI with semaphore_wait_trap).

    • I discovered that I had a push notification parsing error on the console due to the "title-loc-args" : [3333] not accepting 3333 literally but accepting it as a string "title-loc-args" : ["3333"]. This made my entire interface stall after I access the method above, only on iOS 11, it works on iOS 12.
  • I also found out that, with the exact same code, it works without any issue on iOS 12.0 (16A5366a). But on iOS 11 it is happening.

1
votes

In my case silent notifications was used to update ui after job was done on server site so it was pain in ass to have non relevant content in app. Because our payload for silent notification contains even title and body I implement these methods to get working notifications in active / inactive app, not charging and with Background App Refresh turned off and even in Low Power state.

To get this working I add delegate and create extension with UNUserNotificationCenterDelegate protocol and willPresent notification (iOS 10+) method, that is triggered every time with correct payload. To not show notification when app is active just call completion with badge or sound. I ended up with something like this

    import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    // MARK: - Lifecycle
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        UNUserNotificationCenter.current().delegate = self
        return true
    }

    //this was only method to handle notifications before
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        //process silent notification
        completionHandler(UIBackgroundFetchResult.newData)
    }
}

extension AppDelegate : UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        //proces notification when app is active with `notification.request.content.userInfo`
        if UIApplication.shared.applicationState == .active {
            completionHandler(.badge)
        }else {
            completionHandler(.alert)
        }
    }
}

And to get working these states when app is in background and silent notification don't call my methods I get notifications from notification center directly in applicationDidBecomeActive by:

UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in
            debugLog(message: "unprocessed notification count: \(notifications.count)")
            if notifications.count > 0 {
                notifications.forEach({ (notification) in
                    DispatchQueue.main.async {
                        //handle `notification.request.content.userInfo`
                    }
                })
            }
        }
0
votes

In my case, "Background App Refresh" was turned off in iPhone settings. Because of this push notification was delivered to the device but not on the app. Turning Background App Refresh on receives the silent push in the app.

This may not be the actual answer for this question, just in case if anyone need to check.