带有 FCM 令牌的设备的 Firebase 通知显示已发送但未收到

Firebase Notification To Device with FCM Token Says Sent but not received

我正在尝试使用 FCM 令牌从 firebase 通知控制台向特定设备发送简单的推送通知。 firebase 通知控制台显示通知已发送,但设备未收到。我尝试发送通知,然后等待查看控制台是否从 didReceiveRemoteNotification 登录,但通知花费的时间太长(数小时),无法在 firebase 控制台中显示为已发送(即使我将优先级设置为 high).

应用代理

import UIKit
import Firebase
import FirebaseStorage
import FirebaseDatabase
import FirebaseMessaging
import CoreData
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        // Use Firebase library to configure APIs
        FirebaseApp.configure()

        /////
        // For Firebase Cloud Messaging (FCM)
        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: {_, _ in })
        } else {
            let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
            application.registerUserNotificationSettings(settings)
        }
        application.registerForRemoteNotifications()
        // End of [for Firebase Cloud Messaging (FCM)]
        /////

        return true
    }

    ///////////////////////
    // FCM Setup

    // Monitor token generation for FCM: Be notified whenever the FCM token is updated
    func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
        print("Firebase registration token: \(fcmToken)")
    }

    // Monitor token generation for FCM:
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        Messaging.messaging().apnsToken = deviceToken
    }    // Handle messages received through the FCM APNs interface
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
        print("didReceiveRemoteNotification")
        // 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

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

        // Print message ID.
        // gcm_message_id
        if let messageID = userInfo["gcmMessageIDKey"] {
            print("Message ID: \(messageID)")
        }

^我猜这个问题可能与 "gcm_message_id"/"gcmMessageId"/"gcm.message_id" 有关,因为这三种方法各不相同我试过下面

        // Print full message.
        print(userInfo)
    }

    // Handle messages received through the FCM APNs interface
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        print("didReceiveRemoteNotification (withCompletionHandeler)")
        // 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

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

        // Print message ID.
        if let messageID = userInfo["gcmMessageIDKey"] {
            print("Message ID: \(messageID)")
        }

^我猜这个问题可能与 "gcm_message_id"/"gcmMessageId"/"gcm.message_id" 有关,因为这三种方法各不相同我试过下面

        // Print full message.
        print(userInfo)

        completionHandler(UIBackgroundFetchResult.newData)
    }

    // End of [FCM Setup]
    ///////////////////////
}

视图控制器

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Retrieve the current registration token for Firebase Cloud Messaging (FCM)
        let token = Messaging.messaging().fcmToken
        print("FCM token: \(token ?? "")")
      }
}

权利和启用推送通知

我已添加推送权利 and enabled background modes for push notifications 并将 GoogleService-Info.plist 添加到我的项目中。

发送通知的方法

我正在从 Firebase 通知控制台创建通知(如下所示),因此通知本身的结构应该没有问题。

我尝试了以下方法来解决这个问题,但都产生了相同的结果:

有谁知道为什么通知在 firebase 通知控制台中被标记为已发送但没有显示在设备上?

Does anyone know why the notification is being marked as sent in the firebase notification console but not showing up on the device?

因为"sent"不代表"received"。

无法保证在设备上收到通知。使用基本的 APNS 基础设施,如果在设备上收到或处理了通知,您甚至无法获取信息。

如果您在设备上没有收到成功发送的消息,可能有多种原因。此外,即使您收到了 Firebase 令牌,也不意味着您的设备在任何情况下都可以收到通知。

为了隔离问题,我建议构建最小设置并在没有 Firebase 的情况下使用 APNS。您可以使用终端或 NWPusher (https://github.com/noodlewerk/NWPusher) 从本地 macOS 系统发送通知,并使用 iOS 本机远程推送通知框架接收通知。

注意将 APNS 设备令牌转换为提交通知所需的正确格式:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

    let token = deviceToken.hexEncodedString()
    print("Token: \(token)")
}

数据扩展名:

extension Data {
    func hexEncodedString() -> String {
        return map { String(format: "%02hhx", [=11=]) }.joined()
    }
}

我看到你已经完成了项目 Capabilities 中的所有内容。

几点:

  • 让你的 class 符合 messaging delegate 就像这样:

    Messaging.messaging().delegate = self
    
  • 确保您已正确设置证书并在 Firebase 推送通知配置上上传所有内容(不是确切的术语)。

  • 我已经完成了几个使用 Firebase 推送通知服务的应用程序,从头开始制作一个示例应用程序可能会帮助您找出自己做错了什么。

还有...这是一个更好的代码块,用于注册您的应用程序以获取推送通知。

    // Setup Push Notifications

    if #available(iOS 10.0, *) {
        let center  = UNUserNotificationCenter.current()
        center.delegate = self
        center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in
            if error == nil{
                DispatchQueue.main.async(execute: {
                    application.registerForRemoteNotifications()
                })
            }
        }
    }
    else {
        let notificationTypes: UIUserNotificationType = [.sound, .alert, .badge]
        let notificationSettings = UIUserNotificationSettings(types: notificationTypes, categories: nil)
        application.registerForRemoteNotifications()
        application.registerUserNotificationSettings(notificationSettings)
    }

我在处理推送通知时使用的几个故障排除步骤是:

  1. 首先让推送独立于 firebase 服务工作。我用这个 tool.
  2. 确保您没有任何包标识符名称空间冲突。因此,例如,在设备上拥有应用商店构建、testflight 构建和/或开发构建的任意组合。删除应用程序的一个实例以外的所有实例。捆绑包标识符是您的设备如何知道将推送通知路由到哪个应用程序的方式。
  3. 当所有其他方法都失败时 - 我尝试通过构建一个新的示例项目并将其连接到一个新的 firebase 项目来隔离问题,看看我是否可以将我的注意力缩小到能够在没有任何东西的情况下进行推送工作我的应用程序中的其他业务逻辑。这帮助我向自己证明我没有发疯,并向我证明这不是导致我陷入困境的某种神秘的网络状况。

我希望这能帮助你解决所有问题。

尝试将以下代码添加到 didRegisterForRemoteNotificationsWithDeviceToken 函数中:

Messaging.messaging().apnsToken = deviceToken

所以它看起来像这样:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

    Messaging.messaging().apnsToken = deviceToken
}

它适合我。