TheGreatCornholio
TheGreatCornholio

Reputation: 1533

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.

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.

Upvotes: 0

Views: 113

Answers (2)

TheGreatCornholio
TheGreatCornholio

Reputation: 1533

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?

Upvotes: 0

eemmrrkk
eemmrrkk

Reputation: 1710

    @UIApplicationMain

    class AppDelegate: UIResponder, UIApplicationDelegate {

    
    var window: UIWindow?
    
   

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        print("...... \(#function)")
        
        
        FirebaseApp.configure()
        
        
        let chatViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "WelcomeViewController") as! WelcomeViewController
        
        
        let mainNavController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MainNavControllerBitch") as! UINavigationController
        
        window = UIWindow(frame: UIScreen.main.bounds)
        let viewController = chatViewController // Replace with your root view controller
        
        let navigationController = UINavigationController(rootViewController: viewController)

        
        //window?.rootViewController = navigationController
        window?.rootViewController = mainNavController
        window?.makeKeyAndVisible()
        
        
        
        let db = Firestore.firestore()
        
        print("databse > ", db)
        
        IQKeyboardManager.shared.isEnabled = true
        IQKeyboardManager.shared.enableAutoToolbar = false
        IQKeyboardManager.shared.resignOnTouchOutside = true
        
        
        UNUserNotificationCenter.current().delegate = self
                let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
                UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { granted, error in
                    print("Notification permission granted: \(granted)")
                }
        
                
                Messaging.messaging().delegate = self
        
        
        return true
    }

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

    }

    extension AppDelegate: MessagingDelegate {
        func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
            if let fcmToken = fcmToken {
                print("Firebase registration token: \(fcmToken)")
                UserDefaults.standard.set(fcmToken, forKey: "fcmToken")
                saveFCMToken(fcmToken: fcmToken)
            } else {
                print("Failed to fetch FCM token")
            }
        }
    }

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.alert, .badge, .sound])
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        let identifier = response.actionIdentifier
        
        
        
        if identifier == "action1" {
            navigateToProfile()
        } else if identifier == "action2" {
            navigateToUsers()
        }
        
        completionHandler()
    }
    
}

    extension AppDelegate {
        
        func navigateToProfile() {
            if let navigationController = window?.rootViewController as? UINavigationController {
                
                let profileVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ProfileViewController")
                navigationController.pushViewController(profileVC, animated: true)
                
            }
        }
        
        func navigateToUsers() {
            
            print("... window root ", window?.rootViewController)
            
            if let navigationController = window?.rootViewController as? UINavigationController {
                let usersVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "UsersViewController")
                navigationController.pushViewController(usersVC, animated: true)
            }
            
        }
        
        
        func saveFCMToken(fcmToken: String) {
            guard let userId = Auth.auth().currentUser?.uid else {
                print("user not authenticated")
                return
            }
            
            let data: [String: Any] = [
                "tokens": FieldValue.arrayUnion([fcmToken])
            ]
            
            Firestore.firestore().collection("deviceTokens").document(userId).setData(data, merge: true) { error in
                if let error = error {
                    print("error writing on FCM token to Firebase: \(error.localizedDescription)")
                } else {
                    print("FCM token saved successfully")
                }
            }
            
        }
    }

This is a dummy AppDelegate code for working iOS FCM Messaging example, please examine it carefully, in your code

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


    }

this block is missing, you have to manually set apnsToken variable

Upvotes: 1

Related Questions