Stanley Stanley - 2 months ago 584
iOS Question

Push Notifications not being received on iOS 10, but working on iOS 9 and before

I have several apps that were written in Swift 2.2, compiled with Xcode 7.3 and is live on the App Store. The apps utilize Push Notifications and is working fine in iOS 9.3 and earlier.

On devices that have been upgraded to iOS 10, however, my apps don't receive any Push Notifications. Devices that are still running iOS 9 are still receiving notifications.

Thinking it might be a certificate or entitlement issue, I have tried the following:
Upgraded one of my apps to Swift 2.3, added the APS Environment Entitlement and compiled it in Xcode 8 but this made no difference.

In my AppDelegate I still have the existing methods to register for push notifications that includes:

let notificationSettings = UIUserNotificationSettings(forTypes: [.Badge, .Sound, .Alert], categories:nil)
application.registerUserNotificationSettings(notificationSettings)


This registration seems to be successful even on iOS 10 since Application didRegisterForRemoteNotificationsWithDeviceToken is then called, so I am receiving a token from APNS.

The problem is that when I send a push notification to this device, Application didReceiveRemoteNotification is never called.

Now, here it is said that the methods on UIApplicationDelegate is deprecated on iOS 10 and I should implement userNotificationCenter(:didReceive:withCompletionHandler:) and userNotificationCenter(:willPresent:withCompletionHandler:)

The problem is that I am not ONLY targeting iOS 10. I still need the app to work on iOS 8 and 9 so I doubt that it's the correct approach to implement those methods.

How do I get push notifications for an existing app to continue working on devices that have been upgraded to iOS 10? Do I need to rewrite code? Do I just need to update some certificates or entitlements and recompile in Xcode 8 with some "new" settings?

Answer

Ok I have figured it out. I now have my original Push Notifications that was working on iOS 9 working on iOS 10 with Xcode 8 and Swift 2.3.

I implemented this by making the following changes to my AppDelegate:

1) On my project settings, under the Capabilities Tab, I scroll down to "Push Notifications" and turn it to "ON". This automatically generates an entitlements file that contains the key "APS Environment" and with value "development".

2) In AppDelegate.swift I make several code changes. I start by importing the UserNotifications framework:

import UserNotifications

Then I have AppDelegate implement the UNUserNotificationCenterDelegate protocol:

class AppDelegate: /*Some other protocols I am extending...*/, UNUserNotificationCenterDelegate {

Then I add the following method:

func registerForPushNotifications(application: UIApplication) {

    if #available(iOS 10.0, *){
        UNUserNotificationCenter.currentNotificationCenter().delegate = self
        UNUserNotificationCenter.currentNotificationCenter().requestAuthorizationWithOptions([.Badge, .Sound, .Alert], completionHandler: {(granted, error) in
            if (granted)
            {
                UIApplication.sharedApplication().registerForRemoteNotifications()
            }
            else{
                //Do stuff if unsuccessful...
            }
        })
    }

    else{ //If user is not on iOS 10 use the old methods we've been using
        let notificationSettings = UIUserNotificationSettings(
            forTypes: [.Badge, .Sound, .Alert], categories: nil)
        application.registerUserNotificationSettings(notificationSettings)

    }

}

Then I call this newly created function inside didFinishLaunchingWithOptions:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
...
    registerForPushNotifications(application)
...
}

I leave my didRegisterForRemoteNotificationsWithDeviceToken method unchanged:

func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
    //Implement this. Do something with the token I receive. Send it to my push notification server to register the device or something else I'd like to do with the token.
 }

Now I implement two new methods for iOS 10 to handle the receiving of Push Notifications:

@available(iOS 10.0, *)
func userNotificationCenter(center: UNUserNotificationCenter, willPresentNotification notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {
    //Handle the notification
}

@available(iOS 10.0, *)
func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {
    //Handle the notification
}

I did not remove any of the methods I have previously implemented for Push Notifications in iOS 9.