'Firebase Cloud Messaging on iOS: 'application:didRegisterForRemoteNotificationsWithDeviceToken' must be present, but is never called

I'm adding Firebase Cloud Messaging in an iOS app (iOS 15.2.1 on a 7th Gen iPad), and I am able to get it to work, but have taken a step that the Firebase docs don't specify, and I don't understand why it's working and why it doesn't work when I don't do this.

First, I'm following these Firebase docs. These docs make no mention of needing to have an application:didRegisterForRemoteNotificationsWithDeviceToken method in the App Delegate. However, if I don't have this method present, I consistently get this error spewed to the console whenever I try to access the token with Messaging.messaging().token:

2022-01-31 22:00:57.448319-0800 authNavFirebaseUISample[4755:1363959] 8.10.0 - [Firebase/Messaging][I-FCM002022] APNS device token not set before retrieving FCM Token for Sender ID 'XXXXXXXXXXXX'. Notifications to this FCM Token will not be delivered over APNS.Be sure to re-retrieve the FCM token once the APNS device token is set.

When I do add in this method, I no longer get that error when accessing the token via Messaging.messaging().token. Furthermore, when this method is present, I can successfully send test notifications via the Notification Composer.

What's particularly strange about all of this is that the actual application:didRegisterForRemoteNotificationsWithDeviceToken is never actually invoked. However, its presence is required and without it, I get that error spew and Notification Composer doesn't work.

I've added relevant code snippets below. Any ideas? Thanks!

App Delegate

class AppDelegate: NSObject, UIApplicationDelegate { 
        
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        
        FirebaseApp.configure()
     
        NotificationHelpers.setupRemoteNotifications(application)
        
        return true
    }
    
    // NOTE: Why is this needed?!?!  
    // If this method isn't present, each time we try to retrieve the FCM (Firebase Cloud
    // Messaging) token, we get:
    //    APNS device token not set before retrieving FCM Token for Sender ID 'XXXXXXXXXXXX'. Notifications to
    //    this FCM Token will not be delivered over APNS.Be sure to re-retrieve the FCM token once the APNS
    //    device token is set.
    // Adding this method prevents that.  However, the method never actually gets executed.  (Try putting a
    // print statement in there, it won't print.)  But if you remove the method, then we get the above error.
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    }
    
    func application(
        _ application: UIApplication,
        configurationForConnecting connectingSceneSession: UISceneSession,
        options: UIScene.ConnectionOptions
    ) -> UISceneConfiguration {
        let sceneConfig = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
        sceneConfig.delegateClass = MySceneDelegate.self
        return sceneConfig
    }
}

NotificationHelpers

class NotificationHelpers {
    static func setupRemoteNotifications(_ application: UIApplication) -> Void {
        
        let delegate = NotificationsDelegate()
        
        // For iOS 10 and above display notification (sent via APNS)
        UNUserNotificationCenter.current().delegate = delegate

        let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
        UNUserNotificationCenter.current().requestAuthorization(
          options: authOptions,
          completionHandler: { _, _ in }
        )

        application.registerForRemoteNotifications()

        Messaging.messaging().delegate = delegate
    }
}

Token accessing code (invoked from button press in app)

    Button("retrieve FCM") {
         // Get token here, but really can be from anywhere
         Messaging.messaging().token { token, error in
             if let error = error {
                 print("Error fetching FCM registration token: \(error)")
             } else if let token = token {
                 print("FCM registration token: \(token)")
             }
         }
    }


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source