Firebase通知通过FCM令牌发送,显示已发送但未收到

10
我试图使用Firebase通知控制台向特定设备发送一个简单的推送通知,使用FCM令牌。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 ?? "")")
      }
}

权利和启用推送通知

我已经添加了推送授权 see here,并为推送通知启用了后台模式 see here,并将GoogleService-Info.plist添加到我的项目中。

发送通知的方法

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

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

有人知道为什么Firebase通知控制台将通知标记已发送,但在设备上没有显示吗?


请检查以下内容:
  1. 您是否添加了推送权限?
  2. 您是否添加了 GoogleService-Info.plist 文件?
- jn-se
@jn-se,感谢您的评论。我已经添加了推送权限(并启用了推送通知的后台模式),并将GoogleService-Info.plist添加到了我的项目中。我已经在我的问题上方添加了截图。 - Rbar
除了权限之外:1. “但是通知需要太长时间(几个小时)才能在 Firebase 控制台中显示为已发送(即使我将优先级设置为高)。” <-- 首先解决这个问题。它不应该是这样的。2. 确保您没有收到任何注册令牌的错误。3. 然后编辑您的问题并添加负载的确切结构以及其外观。很多时候,负载的结构是错误的,因此您什么也得不到! - mfaani
  1. 我已经研究了修复这个问题的方法,只找到了将优先级设置为“高”的方法,并已经执行;如果您有任何修复建议,将不胜感激。
  2. 我确保我没有收到错误,并在xcode中将FCM令牌打印到控制台中,然后使用它定位到适当的设备。
  3. 为了消除您所描述的风险,我尚未自己创建结构,而是从Firebase通知控制台创建通知; 我已经在我的问题中添加了一张图片,您可以看到。
- Rbar
4个回答

11

在处理推送通知时,我使用的一些故障排除步骤包括:

  1. 首先独立于Firebase服务使推送工作。我使用这个工具。
  2. 确保没有捆绑标识符命名空间冲突。例如,在设备上有任何组合的应用商店构建、测试版构建和/或开发版构建。删除除应用程序之外的所有实例。捆绑标识符是您的设备如何知道将推送通知路由到哪个应用程序的方式。
  3. 当一切都失败时,我尝试通过构建一个新的示例项目并将其连接到新的Firebase项目来隔离问题,看看是否可以将焦点缩小到只能在我的应用程序中让推送工作而没有其他业务逻辑。这有助于证明我没有疯狂,并向我证明这不是某种神秘的网络条件导致了我的困扰。

希望这对您在解决问题时有所帮助。


3
感谢您提供的故障排除技巧!有趣的是,从手机中删除应用程序,然后再从XCode运行/安装解决了问题。代码本身没有任何改变,现在通知正在按预期工作。仅仅使用快速入门项目也有助于防止我感觉自己完全疯掉,因为它按预期工作,并且与我的代码相同(https://github.com/firebase/quickstart-ios/blob/master/messaging/MessagingExampleSwift/AppDelegate.swift)。再次感谢您的建议,希望这个答案/问题/评论能够帮助其他人! - Rbar
1
很高兴能帮到你!祝你在项目的其余部分好运。 - Doug
我也遇到了同样的问题,当我删除并重新安装应用程序时,它仅能工作一次,当我再次运行代码时,iPhone 将停止响应由 Firebase 控制台发送的通知。FirebaseInstanceID 版本:v3.4.0。 - Sunrise17

6

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

Messaging.messaging().apnsToken = deviceToken

所以它会看起来像这样:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

    Messaging.messaging().apnsToken = deviceToken
}

这对我来说是有效的工作。


1
这解决了问题,但我确实想知道为什么现在需要它。 - ANGOmarcello

0
有人知道为什么 Firebase 通知控制台中的通知被标记为已发送,但在设备上没有显示吗?
因为“已发送”并不意味着“已接收”。
不能保证通知能够在设备上接收到。使用基本的 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", $0) }.joined()
    }
}

我很感激所有的建议,但如果我理解正确,你是在建议我根本不使用 Firebase 通知,而这在我的情况下不是一个选项。 - Rbar
这是一个很好的调试选项,可以识别问题所在。如果最小化的APNS设置无法工作,则证明存在配置或应用程序设置错误。如果它能够正常工作,则错误可能与Firebase设置或推送负载有关,但应用程序已正确配置。 - jn-se

0

我看到你在项目的Capabilities中已经完成了所有工作。

一些要点:

  • 将您的类符合 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)
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接