Reputation: 1533
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.
Upvotes: 0
Views: 113
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
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