FCM 后台通知在 iOS 中不起作用

Posted

技术标签:

【中文标题】FCM 后台通知在 iOS 中不起作用【英文标题】:FCM background notifications not working in iOS 【发布时间】:2016-10-20 08:42:19 【问题描述】:

我在 ios 上遇到了 FCM 通知问题。

当我的应用程序在前台时,我收到成功通知(appdelegate 中的回调 didReceiveRemoteNotification 被触发),但是当应用程序在后台时我没有收到通知(我在通知中看不到任何内容iOS 托盘)。

所以,我认为问题在于 FCM 发送的消息格式。 我的服务器发给FCM的json格式如下:

  
   "data":  
      "title":"mytitle",
      "body":"mybody",
      "url":"myurl"
   ,
   "notification":  
      "title":"mytitle",
      "body":"mybody"
   ,
   "to":"/topics/topic"

如您所见,我的 json 中有两个块:一个通知块(在后台接收通知),一个数据块(在前台接收通知)。

我不明白为什么没有收到后台通知。 我怀疑块的顺序(如果我把“数据”块放在“通知”块之前有问题吗?)。

编辑: 有关该问题的更多信息。

这是我的 appdelegate.swift:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate

    var window: UIWindow?


    // Application started
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool
    
        let pushNotificationSettings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
        application.registerUserNotificationSettings(pushNotificationSettings)
        application.registerForRemoteNotifications()

        FIRApp.configure()

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "tokenRefreshNotification:", name: kFIRInstanceIDTokenRefreshNotification, object: nil)

        return true
    




    // Handle refresh notification token
    func tokenRefreshNotification(notification: NSNotification) 
        let refreshedToken = FIRInstanceID.instanceID().token()
        print("InstanceID token: \(refreshedToken)")

        // Connect to FCM since connection may have failed when attempted before having a token.
        if (refreshedToken != nil)
        
            connectToFcm()

            FIRMessaging.messaging().subscribeToTopic("/topics/topic")
        

    


    // Connect to FCM
    func connectToFcm() 
        FIRMessaging.messaging().connectWithCompletion  (error) in
            if (error != nil) 
                print("Unable to connect with FCM. \(error)")
             else 
                print("Connected to FCM.")
            
        
    


    // Handle notification when the application is in foreground
    func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (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.
            print("Message ID: \(userInfo["gcm.message_id"])")

            // Print full message.
            print("%@", userInfo)
    


    // Application will enter in background
    func applicationWillResignActive(application: UIApplication)
    
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    



    // Application entered in background
    func applicationDidEnterBackground(application: UIApplication)
    
        FIRMessaging.messaging().disconnect()
        print("Disconnected from FCM.")
    



    // Application will enter in foreground
    func applicationWillEnterForeground(application: UIApplication)
    
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    



    // Application entered in foreground
    func applicationDidBecomeActive(application: UIApplication)
    
        connectToFcm()

        application.applicationIconBadgeNumber = 0;
    



    // Application will terminate
    func applicationWillTerminate(application: UIApplication)
    
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    



我可以在前台接收消息的唯一方法是禁用方法调配,在我的 info.plist 中将 FirebaseAppDelegateProxyEnabled 设置为 NO。

在这种情况下,FCM 文档说我必须在我的 appdelegate.swift 中实现两种方法:

 - FIRMessaging.messaging().appDidReceiveMessage(userInfo)  in didReceiveRemoteNotification callback
 - FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.Sandbox) in didRegisterForRemoteNotificationsWithDeviceToken callback

但是如果我实现了这些功能,即使应用程序在前台,消息也会停止到达。

我知道这很奇怪。

编辑 2:

当应用程序在后台时,没有收到通知,但是当我打开我的应用程序时,会立即收到相同的通知(触发方法 didReceiveRemoteNotification)。

【问题讨论】:

只是出于兴趣,您是否尝试将优先级设置为高? 是的,但没有任何改变。后台仍然没有通知。 尝试将content_available 添加为真值。如果它不起作用,请尝试在使用 content_available 时删除 priority。我对 APNS 的工作原理不是很熟悉,但是如果 prioritycontent_available 都在一个有效负载中,则该消息可能会被忽略。让我知道它是否有效。干杯! 你试过了吗@MarkO'Brian?成功了吗? 我遇到了同样的问题,但我已经将优先级设置为High,但这并没有帮助 【参考方案1】:

当您使用直接 FCM 频道消息时,您无法在后台收到通知

这是Firebase document的一段话:

启用直接通道后,当应用处于后台或关闭时,FCM 后端会使用可靠的消息队列来跟踪待处理的消息。当应用程序进入前台并重新建立连接时,通道将自动向客户端发送待处理的消息,直到它得到客户端的确认。

您可以使用 FCM APNs 接口在前台和后台接收通知

【讨论】:

【参考方案2】:

-当应用程序处于后台或前台且 OS

-当应用程序在前台并且 OS => 10 userNotificationCenter:willPresentNotification:withCompletionHandler: 方法将触发。

-发送不带通知组件的数据消息时: application(_:didReceiveRemoteNotification:) 方法将触发。

-发送带有通知组件的数据消息时: userNotificationCenter:willPresentNotification:withCompletionHandler: 方法将触发。

【讨论】:

来自 Firebase 文档,是不是必须调用 func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) 而不是直接消息的 application(_:didReceiveRemoteNotification:)【参考方案3】:

Priority 和 content_available(如其他答案中所述)是确保您收到通知的关键要素。测试显示了有趣的结果,所以我想在这里分享它们。

测试结果:Swift 3、Xcode 8、iOS 10

优先级 = “高” => “立即”(在明显的网络延迟内)接收消息。

优先级 = “正常” => 各种结果(通常很快,但明显比“高”慢)

content_available = true 在通知中(无负载消息)

前台 = 按预期接收数据 背景 = 按预期接收数据(打开应用时)

content_available = true 在顶层(无负载消息)

前台 = 按预期接收数据 背景 = 按预期接收数据(打开应用时)

content_available = true 在通知中(带有消息 title/body)

前台 = 两次收到数据 背景 = 两次收到数据(打开应用时)

content_available = true 在顶层(带有有效负载消息)

前台 = 两次收到数据 背景 = 两次收到数据(打开应用时)

结论:

    虽然优先级可能导致无法接收消息,但最重要的因素是您必须拥有“content_available”或有效负载消息。 content_available 必须用于纯数据负载(没有它,永远不会发送任何消息)。 content_available 不应用于包含消息的有效负载,因为它会导致从 FCM 发送双重消息。 在顶层或通知中使用 content_available 没有发现任何区别。

编辑:附加测试结果: - 如果你有一个味精标题,你必须有一个味精正文,否则你不会收到警报。

奇怪的是你会得到振动、徽章和声音,但除非你有身体和标题,否则不会显示警告框。

【讨论】:

【参考方案4】:

我遇到了这个问题,设置了content_available 属性。解决方案是从 iOS 设备中删除并重新安装该应用程序。

【讨论】:

这是什么,在哪里??【参考方案5】:

您可能需要添加推送通知权利。为此,请转到您的目标设置,然后单击“功能”并打开“推送通知”。

【讨论】:

【参考方案6】:

您需要将content_available 属性设置为true,如下所示:

  
   "data":  
      "title":"mytitle",
      "body":"mybody",
      "url":"myurl"
   ,
   "notification":  
      "title":"mytitle",
      "body":"mybody",
      "content_available": true
   ,
   "to":"/topics/topic"

本节中有一个蓝色注释框,说明:https://firebase.google.com/docs/cloud-messaging/concept-options#notifications

【讨论】:

这个答案对于像我一样忘记放置“通知”对象的人来说很重要。 来自firebase.google.com/docs/cloud-messaging/http-server-ref,content_available 键应该与数据、通知、to 处于同一级别,而不是在通知内。 @Peacemoon 当我将 content_available : 1 放在与数据相同的级别时,我收到错误: FIRMessaging 在无效状态下接收通知 2,当我使用 content-aavailable 时:1,无论哪种方式,我都不再收到警告,我在托盘中收到通知并且它们不是静默的。有什么想法吗? @user2363025 content_available 应该是 true 而不是 1,因为 Firebase Messaging 如何处理对象。 content_available 用于 GCM,content-available 用于 APNS。 谢谢,这种格式对我制作适用于 android 和 iOS 的通知有很大帮助。【参考方案7】:

假设您已正确设置所有内容,则将消息的 prioritynormal 设置为 high 应该会立即显示。这是由于 iOS 捆绑通知和处理它们的方式。您可以阅读有关Priority of FCM notifications here 的信息。请注意,除非有充分的理由,否则您不应该在生产环境中真正使用 high,因为它会消耗电池电量。

这是来自Apple's docs的参考

通知的优先级。指定以下值之一:

10——立即发送推送消息。具有此优先级的通知 必须在目标设备上触发警报、声音或标记。它是一个 将此优先级用于仅包含的推送通知的错误 内容可用键。

5 - 在考虑功率的情况下发送推送消息 设备的注意事项。具有此优先级的通知可能 分组并分批交付。它们受到限制,在某些情况下 案件未交付。如果省略此标头,APNs 服务器将设置 优先级为 10。

【讨论】:

您好,谢谢您的回答!假设这是问题所在,我应该怎么做才能在生产模式下接收后台通知而不将优先级设置为高? 您只需等待它出现即可。 iOS 决定何时显示它。查看我的更新答案 好的,这似乎是合法的.. 但是我 2 小时前发送但尚未到达的消息呢?我的意思是,这些是通过开发 APNS 证书发送的消息,而不是生产。开发消息和生产消息的转发时间有区别吗? 假设您已正确设置所有内容,那么您肯定应该比那更早看到它们。但是考虑一下,当您在后台向您的应用程序发送通知并且您没有看到它显示时,然后打开应用程序,那么该通知会立即触发并且不再在队列中。说得通?正如我所说,这一切都假设您已遵循 Firebase 的指南,了解如何正确设置 bg 通知并且它可以正常工作。您没有提供太多代码来验证这一点,所以我在回答中做了一些强有力的假设 我可以确认问题是最重要的。将优先级设置为高后,我的问题就解决了。谢谢你们!

以上是关于FCM 后台通知在 iOS 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

FCM 链接在桌面通知中不起作用

FCM 通知在 OREO 中不起作用

颤动的 FCM 通知声音在发布中不起作用

Firebase 云消息传递前台通知在 Vue 中不起作用

FCM 推送通知在 android 中不起作用(使用 cordova-plugin-fcm 2.1.1 的 Ionic 项目)

适用于 iOS 的 Google Firebase 推送通知在生产环境中不起作用