Firebase Cloud Messaging 通知未显示在 iOS 设备上(前台和后台)
Posted
技术标签:
【中文标题】Firebase Cloud Messaging 通知未显示在 iOS 设备上(前台和后台)【英文标题】:Firebase Cloud Messaging notifications not being displayed on iOS device (foreground and background) 【发布时间】:2020-04-26 14:12:40 【问题描述】:我正在使用 FCM 为我的 ios 应用创建和发送推送通知。
开发环境:
Xcode 11.3
运行 iOS 13.3 的 iPhone X
斯威夫特 5.2
Pod 版本:
Firebase 6.14.0 FirebaseMessaging 4.1.10 FirebaseInstanceID 4.2.8问题:
在遇到问题之前,我已将我的应用设置为能够在应用同时处于后台和前台时接收通知。我对自己非常满意,我提交了代码。在此之后,我无法在前台或后台接收通知。无论使用通知是从 Cloud Messaging 仪表板还是 POSTMAN 发送的,我都会收到成功的响应,但通知永远不会出现。
起初我以为我可能已经达到通知配额,但现在是事后 2 天。
我已尝试解决问题:
-
卸载并重新安装应用程序(刷新设备令牌)
将
UIApplication.shared.registerForRemoteNotifications()
移至FirebaseApp.configure()
之前
下载了新的 GoogleService-Info.plist 并替换了现有的
检查了 bundle id 等是否全部匹配
将 firebase pod 更新为最新版本(如果有帮助,FirebaseMessaging 的版本为 4.1.9)
设置Messaging.messaging().shouldEstablishDirectChannel = true
删除并重新添加了所需的功能
将FirebaseAppDelegateProxyEnabled
设置为YES 和NO
设置shouldEstablishDirectChannel = true
设置useMessagingDelegateForDirectChannel = true
将一些逻辑从 didFinishLaunchingWithOptions() 移至 applicationDidBecomeActive()
代码:
注意:这是最初为我工作的未更改代码。
AppDelegate.swift
import UIKit
import Firebase
import FBSDKCoreKit
import GoogleMaps
import SwiftLocation
import GooglePlaces
import Crashlytics
import GoogleSignIn
import Armchair
import UserNotifications
import FirebaseMessaging
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate
var window: UIWindow?
var swipeNavigationViewController: SwipeNavigationViewController!
override init()
super.init()
FirebaseApp.configure()
Database.database().isPersistenceEnabled = true
swipeNavigationViewController = SwipeNavigationViewController()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool
FirebaseConfiguration.shared.setLoggerLevel(.error)
ApplicationDelegate.shared.application(application, didFinishLaunchingWithOptions: launchOptions)
// Google Maps
GMSServices.provideAPIKey(FireBaseConstants.GoogleAPIKey)
GMSPlacesClient.provideAPIKey(FireBaseConstants.GoogleAPIKey)
GeocoderRequest.GoogleOptions(APIKey: FireBaseConstants.GoogleAPIKey)
let navigationViewController = UINavigationController(rootViewController: swipeNavigationViewController)
navigationViewController.setNavigationBarHidden(true, animated: false)
self.window?.rootViewController = navigationViewController
self.window?.makeKeyAndVisible()
showAlertIfPointedTowardProductionDatabase()
setupReviewRequest()
UIApplication.shared.registerForRemoteNotifications()
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options:[.badge, .alert, .sound]) (granted, error) in
// If granted comes true you can enabled features based on authorization.
guard granted else return
DispatchQueue.main.async
print("UserID: \(UserManager.sharedManager.currentUser?.userID)")
let pushManager = PushNotificationManager(userID: "currently_logged_in_user_id")
pushManager.registerForPushNotifications()
UNUserNotificationCenter.current().delegate = self
return true
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool
let handledByFB = ApplicationDelegate.shared.application(app, open: url, options: options)
var handledByGoogle = false
if !handledByFB
handledByGoogle = GIDSignIn.sharedInstance().handle(url)
let handled = handledByFB || handledByGoogle
return handled
private func setupReviewRequest()
//Code...
// This method will be called when app received push notifications in foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
completionHandler([.alert, .badge, .sound])
PushNotificationManager.swift
import Foundation
import Firebase
import FirebaseFirestore
import FirebaseMessaging
import UIKit
import UserNotifications
class PushNotificationManager: NSObject, MessagingDelegate, UNUserNotificationCenterDelegate
let userID: String
let gcmMessageIDKey = "gcm.message_id"
init(userID: String)
self.userID = userID
super.init()
func registerForPushNotifications()
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions) (_, error) in
guard error == nil else
print(error!.localizedDescription)
return
//get application instance ID
InstanceID.instanceID().instanceID (result, error) in
if let error = error
print("Error fetching remote instance ID: \(error)")
else if let result = result
print("Remote instance ID token: \(result.token)")
UIApplication.shared.registerForRemoteNotifications()
updateFirestorePushTokenIfNeeded()
func updateFirestorePushTokenIfNeeded()
if let token = Messaging.messaging().fcmToken
// let usersRef = Firestore.firestore().collection("users_table").document(userID)
// usersRef.setData(["fcmToken": token], merge: true)
print("Remote instance ID token: \(token)")
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String)
print("Firebase registration token: \(fcmToken)")
let dataDict:[String: String] = ["token": fcmToken]
NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
// TODO: If necessary send token to application server.
// Note: This callback is fired at each app startup and whenever a new token is generated.
func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage)
print("Received data message: \(remoteMessage.appData)")
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)
print(response)
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any])
if let messageID = userInfo[gcmMessageIDKey]
print("Message ID: \(messageID)")
print(userInfo)
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error)
print("Unable to register for remote notifications: \(error.localizedDescription)")
func application(_ application: UIApplication,didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
let tokenParts = deviceToken.map //data -> String in
return String(format: "%02.2hhx", $0)
Messaging.messaging().apnsToken = deviceToken
Messaging.messaging().setAPNSToken(deviceToken, type: .unknown)
UserDefaults.standard.synchronize()
这是使用以下所有链接设置的(我确定还有一些我忘记了):
https://code.tutsplus.com/tutorials/get-started-with-firebase-messaging-for-ios--cms-32126#comment-4345018783 Firebase - Swift 4: Message sent but not received Get push notification while App in foreground iOS https://www.iosapptemplates.com/blog/ios-development/push-notifications-firebase-swift-5回复信息:
邮递员:
"multicast_id": 2586780331808083728,
"success": 1,
"failure": 0,
"canonical_ids": 0,
"results": [
"message_id": "0:1578532253832479%2b1845e62b1845e6"
]
云消息传递:
【问题讨论】:
【参考方案1】:我可以通过移动来解决问题
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
从 PushNotificationManager 到 AppDelegate。希望这对其他人有帮助!
【讨论】:
【参考方案2】:您可以在控制器层中使用didRegisterForRemoteNotifications
。我的控制器中有一个私有方法,它也调用registerForRemoteNotifications
。我在 AppDelegate 中实例化了我的控制器,因此它可以立即使用,当我尝试在没有强引用的情况下制作控制器时确实遇到了问题,可能与弱委托有关。
这是我的全部appDelegate
:
import UIKit
import Firebase
@main
class AppDelegate: UIResponder, UIApplicationDelegate
var controller: FirebaseMessagingController!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
FirebaseApp.configure()
self.controller = FirebaseMessagingController.shared
return true
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>)
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
我的控制器的初始化:
private init()
NotificationCenter.default.addObserver(
self,
selector: #selector(receiveToken(_:)),
name: .tokenKey,
object: nil)
registerForRemoteNotifications(UIApplication.shared)
requestNotificationPermissions _ in // TODO: move to more user friendly place
Messaging.messaging().delegate = UIApplication.shared.delegate as? MessagingDelegate
UNUserNotificationCenter.current().delegate = UIApplication.shared.delegate as? AppDelegate
@objc private func receiveToken(_ notification: Notification)
guard let tokenDict = notification.userInfo as? [Notification.Name: String],
let token = tokenDict[.tokenKey] else return
self.token = token
let apiTokenDict = ["token": token]
if AuthService.shared.isLoggedIn
guard let user = AuthService.shared.user else return
FirebaseDatabaseController().updateValues(for: APIRef.userRef(userId: user.userId).endpoint, with: apiTokenDict)
private func registerForRemoteNotifications(_ application: UIApplication)
application.registerForRemoteNotifications()
private func requestNotificationPermissions(completion: @escaping (Result<Bool, Error>) -> Void)
let authOptions: UNAuthorizationOptions = [.alert, .badge]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: success, error in
if success
completion(.success(success))
else if let error = error
completion(.failure(error))
else
let error = NSError(domain: #function, code: 0)
completion(.failure(error))
)
didRegisterForRemoteNotifications
也在我的控制器中并接收令牌:
// set FirebaseMessaging service with apnsToken
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
Messaging.messaging().apnsToken = deviceToken
编辑:实际上,我在 AppDelegate 中还有一块,接收注册令牌,但将其隐藏在我的控制器的扩展中:
extension AppDelegate: MessagingDelegate
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?)
guard let fcmToken = fcmToken else return
let dataDict:[NSNotification.Name: String] = [.tokenKey: fcmToken]
NotificationCenter.default.post(name: .tokenKey, object: nil, userInfo: dataDict)
想一想,这是一个奇怪的选择,因为我可以让控制器成为消息传递委托,并在那里执行此操作而不发布通知......
【讨论】:
以上是关于Firebase Cloud Messaging 通知未显示在 iOS 设备上(前台和后台)的主要内容,如果未能解决你的问题,请参考以下文章
Flutter Firebase Cloud Messaging onMessage 被触发两次
Google Cloud Messaging与Firebase
Firebase Cloud Messaging:Firebase Admin SDK 中的设备组发送支持
如何使用 Firebase Cloud Messaging 在前台显示多个通知