kometen kometen - 3 months ago 153
iOS Question

Apple remote push notifications appears to behave different in sandbox- and production-mode

While writing my original question I found an answer at Ray Wenderlichs tutorial on push notifications. In the section

Handling Push Notifications
I added the code to AppDelegate.swift as outlined in the first case.

if let notification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary {
updateFromSyncData(userInfo: notification)
}


After this change I can show the text from the push message in production-mode while the app is inactive.

Before I added the code above the payload would be handled while the app was active and inactive in sandbox-mode but only when it was active in production-mode.

When I noticed this I changed the code and tested while my iphone was connected (in sandbox-mode). And when it worked I archived, uploaded and reinstalled the app from testflight. I wandered in this circle a couple of times.

Is this difference in behaviour between sandbox and production by design?

I'm using swift 3 on xcode 8 beta 5 and the iphone is on ios 10 beta 6.

AppDelegate.swift:

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
if (application.applicationState == .inactive) {
completionHandler(.newData)
updateFromSyncData(userInfo: userInfo)
}
if (application.applicationState == .active) {
completionHandler(.newData)
updateFromSyncData(userInfo: userInfo)
}
if (application.applicationState == .background) {
completionHandler(.newData)
updateFromSyncData(userInfo: userInfo)
}
}

func updateFromSyncData(userInfo: NSDictionary) -> Void {
if let aps = userInfo["aps"] as? NSDictionary {
let tabbarController = self.window!.rootViewController as! UITabBarController
let sVC = tabbarController.viewControllers?[1] as! SecondViewController
// Show payload in SecondViewController etc.
}
}

Answer

There are four things you could look at:

  1. Completion Handler: completionHandler(.newData) should be called after processing data and not before. Try:

    updateFromSyncData(userInfo: userInfo)
    completionHandler(.newData)
    

    The app has 30 seconds to process data. Frequent notifications with long data processing could also cause iOS to throttle remote notifications. Though I don't think that is the problem here. From Apple's 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 remote notifications may not always be woken up early to process future notifications.

  1. Main Queue: The application(_:didReceiveRemoteNotification:fetchCompletionHandler:) delegate method gets called on a background thread. Check that the functionality in "// Show payload in SecondViewController etc." is executed on the main queue with dispatch_async(dispatch_get_main_queue() { ... }.
  2. Capabilities: Check in Capabilities if the Push Notifications is enabled and also Remote notifications in Background Modes.
  3. Force Quit: Are you force-quitting the app for testing? The app will not get a background notification in that case. As Apple's documentation says:

...if you enabled the remote notifications background mode, the system launches your app (or wakes it from the suspended state) and puts it in the background state when a remote notification arrives. However, the system does not automatically launch your app if the user has force-quit it. In that situation, the user must relaunch your app or restart the device before the system attempts to launch your app automatically again.