Search code examples
iosswiftfirebaseswiftuiapple-push-notifications

SwiftUI iOS - firebase push notifications error: APNS device token not set before retrieving FCM Token for Sender ID


Up until a year or something, push notifications worked and I've stopped paying attention to them, so I don't know if I'm getting the error since swift/swiftui/xcode/ios update or what ever. This is the error:

APNS device token not set before retrieving FCM Token for Sender ID 'xxxxxxxxxxx'.Be sure to re-retrieve the FCM token once the APNS device token is set.

What I've tried:

Based on all kind of answers for same question.

  • Revoking the APN token and replacing the old one in firebase project settings

  • Updating to latest Firebase SDK

  • Following different tutorials of this implementation, including trying out different AppDelegate classes from those

  • Setting FirebaseAppDelegateProxyEnabled to false

According to a comment here: https://github.com/firebase/firebase-ios-sdk/issues/12445 the reason for that is bad timing in which the functions are called but I don't really understand what exactly he means. The code snipped he provides is something what I'm using anyway many seconds after the app started and the token is nil

This is the latest AppDeligate that I'm trying out:

class AppDelegate: NSObject, UIApplicationDelegate {
    
    let gcmMessageIDKey = "gcm.message_id"
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        
        FirebaseApp.configure()
        UNUserNotificationCenter.current().delegate = self
        
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in
        }
        
        application.registerForRemoteNotifications()
        Messaging.messaging().delegate = self

        return true
    }
    
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }
        
        guard let aps = userInfo["aps"] as? [String: AnyObject] else {
            completionHandler(.failed)
            return
        }
        print("got something, aka the \(aps)")

        // Print full message.
        print(userInfo)

        completionHandler(UIBackgroundFetchResult.newData)
    }
    
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Unable to register for remote notifications: \(error.localizedDescription)")
    }

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        Messaging.messaging().apnsToken = deviceToken
    }
      
}

extension AppDelegate: UNUserNotificationCenterDelegate {
  // Receive displayed notifications for iOS 10 devices.
  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              willPresent notification: UNNotification,
                              withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions)
                                -> Void) {
    let userInfo = notification.request.content.userInfo

    // With swizzling disabled you must let Messaging know about the message, for Analytics
    // Messaging.messaging().appDidReceiveMessage(userInfo)

    // ...

    // Print full message.
    print(userInfo)

    // Change this to your preferred presentation option
      completionHandler([[.banner, .badge, .sound]])
  }

  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              didReceive response: UNNotificationResponse,
                              withCompletionHandler completionHandler: @escaping () -> Void) {
    let userInfo = response.notification.request.content.userInfo

    print(userInfo)

    completionHandler()
  }
}

extension AppDelegate: MessagingDelegate {
  // [START refresh_token]
  func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
    print("Firebase registration token: \(String(describing: fcmToken))")

    let dataDict: [String: String] = ["token": fcmToken ?? ""]
    NotificationCenter.default.post(
      name: Notification.Name("FCMToken"),
      object: nil,
      userInfo: dataDict
    )

  }

}

I hope someone has an idea what's wrong.


Solution

  • The solution was to use it on appear, like so:

    @main
    struct ExampleApp: App {
    
        @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
        
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .onAppear(perform: {
                        appDelegate.app = self
                    })
            }
        }
    }
    

    And inside AppDeligate it should start like this:

    class AppDelegate: NSObject, UIApplicationDelegate {
        
        var app: ExampleApp?