FCM后台通知在iOS中无效

时间:2016-06-18 17:22:11

标签: ios swift firebase google-cloud-messaging firebase-cloud-messaging

我在iOS上遇到FCM通知问题。

当我的应用处于前台时(didReceiveRemoteNotification中的回调appdelegate被触发),我收到成功通知,但是当应用处于后台时我没有收到通知(我这样做)在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被触发)。

7 个答案:

答案 0 :(得分:23)

假设您已正确设置所有内容,则将邮件的priority设置为normalhigh会立即显示。这是由于iOS捆绑通知并处理它们的方式。您可以阅读Priority of FCM notifications here。请注意,除非有好的情况,否则你不应该在生产中使用high,因为它有电池损失。

以下是Apple's docs

的参考资料
  

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

     

10 - 立即发送推送消息。具有此优先级的通知   必须在目标设备上触发警报,声音或徽章。它是一个   将此优先级用于仅包含的推送通知时出错   内容可用密钥。

     

5 - 在考虑电源的时间发送推送消息   设备的注意事项。具有此优先级的通知可能   分组并以突发方式交付。他们受到限制,有些人受到限制   案件未送达。如果省略此标头,APNs服务器将设置   优先考虑10。

答案 1 :(得分:17)

您需要将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

答案 2 :(得分:9)

您可能需要添加推送通知权利。通过转到目标设置,然后单击“功能”并打开“推送通知”来执行此操作。

Target Capabilities

答案 3 :(得分:9)

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

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

优先级="高" => "即时登录" (明显的网络延迟)接收消息。

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

通知中的

content_available = true(无有效负载消息)

  • 前景=按预期收到的数据
  • 背景=按预期收到的数据(打开应用时)
顶层的

content_available = true(无有效载荷消息)

  • 前景=按预期收到的数据
  • 背景=按预期收到的数据(打开应用时)
通知中的

content_available = true(带有消息{title / body})

  • 前景=收到的数据TWICE
  • 背景=数据收到TWICE(打开应用时)
顶层的

content_available = true(带有效内容消息)

  • 前景=收到的数据TWICE
  • 背景=数据收到TWICE(打开应用时)

结论:

  1. 虽然优先级是不接收邮件的可能原因,但最重要的因素是您必须拥有' content_available'或有效载荷消息。
  2. content_available必须用于仅数据的有效载荷(没有它,不会发送任何消息)。
  3. content_available不应该用于包含消息的有效负载,因为它会导致从FCM发送双重消息。
  4. 在顶级或通知中使用content_available没有任何差异。
  5. 编辑:其他测试结果: - 如果你有一个msg标题你必须有一个msg正文或者你没有得到警报。

    这个奇怪的部分是你会获得振动,徽章和声音,但警报框不会出现,除非你有身体和标题。

答案 4 :(得分:1)

使用直接FCM频道消息时,您不会在后台收到通知

这是Firebase document的段落:

  

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

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

答案 5 :(得分:0)

- 当应用程序处于后台或前台且OS <10时为FCM 应用程序(_:didReceiveRemoteNotification :)方法将触发。

- 当应用程序是前台且OS =&gt; 10 userNotificationCenter:willPresentNotification:withCompletionHandler:方法将触发。

- 发送没有通知组件的数据消息时: 应用程序(_:didReceiveRemoteNotification :)方法将触发。

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

答案 6 :(得分:-2)

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