Firebase FCM 最初不工作,但如果应用程序打开,则在 3-4 次后工作
Posted
技术标签:
【中文标题】Firebase FCM 最初不工作,但如果应用程序打开,则在 3-4 次后工作【英文标题】:Firebase FCM not working initially but works after 3-4 times if the app is opened 【发布时间】:2017-10-05 05:39:29 【问题描述】:我是 Firebase FCM 集成的新手,我遇到了一些问题。首次安装应用程序时,我没有收到推送通知,但如果我打开和关闭应用程序几次,然后从 Firebase 发送推送通知,我将收到通知,而客户端或服务器代码没有任何更改。谁能帮我解决这个问题?
我在下面附上了我的 appdelegate 代码
import UIKit
import Firebase
import UserNotifications
import FirebaseMessaging
import FirebaseInstanceID
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,UNUserNotificationCenterDelegate,MessagingDelegate
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
FirebaseApp.configure()
if #available(ios 10.0, *)
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: (granted, error) in
if error == nil
UIApplication.shared.registerForRemoteNotifications()
)
else
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
application.registerForRemoteNotifications()
return true
func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String)
print("Firebase registration token: \(fcmToken)")
func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings)
UIApplication.shared.registerForRemoteNotifications()
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
// I have tried both method but none worked and i also tried MessagingAPNSTokenType.sandbox and prod
// Method 1: Messaging.messaging().apnsToken = deviceToken
// Method 2:
Messaging.messaging().setAPNSToken(deviceToken, type: MessagingAPNSTokenType.unknown)
提前致谢
【问题讨论】:
【参考方案1】:我遇到了与您相同的问题,并且存在一个已知问题,即如果没有足够早地调用 UIApplication.shared.registerForRemoteNotifications()
,FCM token
并不总是与 APNs device token
相关联。
根据this GitHub 线程,这个问题已经在FirebaseInstanceID SDK
中修复,应该很快就会出来。
与此同时,您可以:
将您的FirebaseInstanceID pod to 2.0.0
锁定在您的 Podfile 中,或者
确保您在应用启动时调用UIApplication.shared.registerForRemoteNotifications()
,最好在FirebaseApp.configure()
之前调用。
更新:
我刚刚做了一些额外的测试,我注意到这有点随机。对于某些设备,它会立即起作用,而对于某些设备则不能。对于某些设备Single Token push
有效,而不是User Segment
。我设法使它工作,以便您至少通过添加以下内容获得Single Token push
或User Segment
:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
if #available(iOS 10.0, *)
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: _, _ in
)
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
// For iOS 10 data message (sent via FCM)
Messaging.messaging().delegate = self
application.registerForRemoteNotifications()
print("::: registerForRemoteNotifications iOS 10")
else
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
print("::: registerUserNotificationSettings iOS 9")
FirebaseApp.configure()
Messaging.messaging().delegate = self
Messaging.messaging().shouldEstablishDirectChannel = true
if let refreshedToken = InstanceID.instanceID().token()
print("::: InstanceID token: \(refreshedToken)")
NotificationCenter.default.addObserver(self, selector: #selector(tokenRefreshNotification), name: NSNotification.Name.InstanceIDTokenRefresh, object: nil)
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
Messaging.messaging().setAPNSToken(deviceToken, type: .prod)
if let refreshedToken = InstanceID.instanceID().token()
print("InstanceID token: \(refreshedToken)")
问题是,如果您不访问 didRegisterForRemoteNotificationsWithDeviceToken
函数,它将无法工作。
【讨论】:
我没有这样的吊舱。这些是我用过的唯一豆荚。 pod 'Firebase/Core' pod 'Firebase/Messaging' 请帮助 感谢您的回答,但我发现即使使用您的代码,如果通过针对所有 iOS 设备模式发送,我也不会收到来自 Firebase 的通知,但是,如果我使用 FCM 令牌将其发送到特定的它正在工作的注册设备。任何想法为什么会出现这种行为? 是的,我得到了相同的结果,但一段时间后它也适用于所有设备。我认为这将在 iOS 11 中修复,所以请稍等,它可能很快就会起作用。【参考方案2】:Firebase FCM,生成第一个令牌需要一些时间。获得此令牌后,您将开始接收推送通知。检查这些。肯定会很清楚:
-
Check this answer
AppDelegate implementation with FCM
希望这会有所帮助。
【讨论】:
【参考方案3】:通过此更新您的 appdelegate
import FirebaseCore
import FirebaseInstanceID
import FirebaseMessaging
import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
self.fcmInitialSetup(application)
return true
func fcmInitialSetup(_ application: UIApplication)
// [START register_for_notifications]
if #available(iOS 10.0, *)
let uns: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(uns)
application.registerForRemoteNotifications()
else
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
application.registerForRemoteNotifications()
// [END register_for_notifications]
FIRApp.configure()
// Add observer for InstanceID token refresh callback.
NotificationCenter.default.addObserver(self, selector: #selector(self.tokenRefreshNotification), name: NSNotification.Name.firInstanceIDTokenRefresh, object: nil)
if let token = FIRInstanceID.instanceID().token()
sendTokenToServer(token)
func sendTokenToServer(_ currentToken: String)
print("sendTokenToServer() Token: \(currentToken)")
// Send token to server ONLY IF NECESSARY
print("InstanceID token: \(currentToken)")
self.token = currentToken
UserDefaults.standard.set(self.token, forKey: "token")
UserDefaults.standard.synchronize()
if self.token != nil
let userInfo = ["token": self.token]
NotificationCenter.default.post(
name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
// NOTE: Need to use this when swizzling is disabled
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
let tokenChars = (deviceToken as NSData).bytes.bindMemory(to: CChar.self, capacity: deviceToken.count)
var tokenString = ""
for i in 0..<deviceToken.count
tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.unknown)
print("Device Token:", tokenString)
print("FIRInstanceID.instanceID().token() Token:", FIRInstanceID.instanceID().token())
if let tokenData = FIRInstanceID.instanceID().token()
UserDefaults.standard.set(tokenData, forKey: "token")
UserDefaults.standard.synchronize()
let userInfo = ["token": tokenData]
func tokenRefreshNotification(_ notification: Notification)
// NOTE: It can be nil here
// print("Token:\(FIRInstanceID.instanceID().token()!)")
if let refreshedToken = FIRInstanceID.instanceID().token()
print("InstanceID token: \(refreshedToken)")
UserDefaults.standard.set(refreshedToken, forKey: "token")
UserDefaults.standard.synchronize()
print("update now \(self.token)")
if self.token != nil
let userInfo = ["token": self.token]
NotificationCenter.default.post(
name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
// Connect to FCM since connection may have failed when attempted before having a token.
connectToFcm()
// [END refresh_token]
func connectToFcm()
FIRMessaging.messaging().connect (error) in
if (error != nil)
print("Unable to connect with FCM. \(error)")
else
print("Connected to FCM.")
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any])
print(userInfo)
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool
print("Within open URL")
return true
// [START receive_apns_token_error]
func application( _ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError
error: Error )
print("Registration for remote notification failed with error: \(error.localizedDescription)")
// [END receive_apns_token_error]
let userInfo = ["error": error.localizedDescription]
NotificationCenter.default.post(
name: Notification.Name(rawValue: rkey), object: nil, userInfo: userInfo)
func registrationHandler(_ token: String!, error: NSError!)
if (token != nil)
self.token = token!
print("Registration Token: \(self.token)")
UserDefaults.standard.set(self.token, forKey: "token")
UserDefaults.standard.synchronize()
let userInfo = ["token": self.token]
NotificationCenter.default.post(
name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
else
print("Registration to GCM failed with error: \(error.localizedDescription)")
let userInfo = ["error": error.localizedDescription]
NotificationCenter.default.post(
name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
func registerForPushNotifications(_ application: UIApplication)
let notificationSettings = UIUserNotificationSettings(
types: [.badge, .sound, .alert], categories: nil)
application.registerUserNotificationSettings(notificationSettings)
func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings)
if notificationSettings.types != UIUserNotificationType()
application.registerForRemoteNotifications()
// [START receive_message]
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// TODO: Handle data of notification
// Print message ID. add Toast
print(userInfo);
print(application.keyWindow?.visibleViewController() ?? "")
print("Message ID: \(userInfo["gcm.message_id"]!)")
// Print full message.
print("%@", userInfo)
// [END receive_message]
func applicationDidBecomeActive(_ application: UIApplication)
connectToFcm()
// [START disconnect_from_fcm]
func applicationDidEnterBackground(_ application: UIApplication)
// FIRMessaging.messaging().disconnect()
// print("Disconnected from FCM.")
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
// [END disconnect_from_fcm]
// [START ios_10_message_handling]
@available(iOS 10, *)
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
// Print message ID.
print("Message ID: \(userInfo["gcm.message_id"]!)")
// Print message ID. add Toast
// Print full message.
print("%@", userInfo)
// Print full message.
print("%@", userInfo)
extension AppDelegate : FIRMessagingDelegate
// Receive data message on iOS 10 devices.
func applicationReceivedRemoteMessage(_ remoteMessage: FIRMessagingRemoteMessage)
print("%@", remoteMessage.appData)
【讨论】:
【参考方案4】:NotificationCenter.default.addObserver(self selector:#selector(self.getFcmToken), name: .firInstanceIDTokenRefresh, 对象:无)
把这个放在应用程序做finsih启动方法 并制作一个函数
func getFcmToken(notification: NSNotification)
guard let contents = FIRInstanceID.instanceID().token()
else
return
print("InstanceID token: \(contents)")
if let token = FIRInstanceID.instanceID().token()
print(token)
【讨论】:
以上是关于Firebase FCM 最初不工作,但如果应用程序打开,则在 3-4 次后工作的主要内容,如果未能解决你的问题,请参考以下文章