didRegisterForRemoteNotificationsWithDeviceToken() not called if requestAuthorization() is not called

If I run the following code in didFinishLaunchingWithOptions()

   UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
        if granted {
            DispatchQueue.main.async {
                application.registerForRemoteNotifications()
            }
        }
    } 

Then the result is that didRegisterForRemoteNotificationsWithDeviceToken() gets called.

However if I change the code to be just:

                DispatchQueue.main.async {
                    application.registerForRemoteNotifications()
                }

Or as as its already running on main in this scenario, then just

application.registerForRemoteNotifications()

Then didRegisterForRemoteNotificationsWithDeviceToken() does NOT get called, but also neither does didFailToRegisterForRemoteNotificationsWithError().

Obtaining a push token is supposed to be independent of the user granting notifications permissions, so why am I not observing that behavior?

I only observe this behavior when running on hardware, when running on the simulator both forms of the code work. Yet its nothing to do with my phone not being able to contact the Apple servers etc. - if I change the code back and forth back and forth between the two then if 100% works when using requestAuthorization() and 100% doesn't when not using it.

There's nothing additional or out of the ordinary with the code, its standard app delete template stuff.

Why isn't it getting a push token when requestAuthorization() isn't used?

(I've tried adding an async delay to calling registerForRemoteNotifications(), but it made no difference).

Answered by Engineer in 857295022

This is expected behavior. If the app is not going to be able to do anything with a received push notification, then it will not be given a push token.

For a push token to be useful, the app has to either of:

  • be authorized to show notifications for visible notifications
  • be able to process the notifications in the background for silent notifications

Without neither of these, sent notifications will have no function, so the system decides to not serve a token.


Argun Tekant /  DTS Engineer / Core Technologies

This can be reproduced from scratch in just 2 minutes: create a brand new iOS app and give it the push notification capability.

Change the app delegate template code to this:

@main class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UNUserNotificationCenter.current().delegate = self

  //     UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
   //         NSLog("Notification permission granted: \(granted)")
   //         DispatchQueue.main.async {
                application.registerForRemoteNotifications()
  //          }
  //     }
    NSLog("didFinishLaunchingWithOptions returning")
    return true
}

// Called when an APNS token has been sucessfully obtained
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    NSLog("Apn Push token: \(token)")
}

// Called when APN token request fails
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    NSLog("❗️Failed to register for remote notifications: \(error)")
}

Neither didRegisterForRemoteNotificationsWithDeviceToken() nor didFailToRegisterForRemoteNotificationsWithError() get called.

Now uncomment the code and didRegisterForRemoteNotificationsWithDeviceToken() will get called.

So based on this experiment, requestAuthorization() is a requirment. Now where does it state that in the Apple documentation, it doesn't, how does this code differ from what's in the Apple documentation, it doesn't.

https://developer-mdn.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/HandlingRemoteNotifications.html#//apple_ref/doc/uid/TP40008194-CH6-SW4

Ok, so found that that if you don't have the background capability of push notifications to your project then you have to call requestAuthorization() in order to get the push token, as described above.

But when you add that background capability, then you can obtain the token without calling requestAuthorization().

What's with that?

This is expected behavior. If the app is not going to be able to do anything with a received push notification, then it will not be given a push token.

For a push token to be useful, the app has to either of:

  • be authorized to show notifications for visible notifications
  • be able to process the notifications in the background for silent notifications

Without neither of these, sent notifications will have no function, so the system decides to not serve a token.


Argun Tekant /  DTS Engineer / Core Technologies

Although, this could have been better documented, and an error could be returned to the app for clarity to the behavior.

Please do feel free to file a Bug Report about those.


Argun Tekant /  DTS Engineer / Core Technologies

@Argun Tekant

Thank you, however having to obtain notification authorization for background silent notifications seems to be contradictory to this logic?

The app can consume and do something with the push without it resulting in a notification.

didRegisterForRemoteNotificationsWithDeviceToken() not called if requestAuthorization() is not called
 
 
Q