web-dev-qa-db-ja.com

iOSでFCMバックグラウンド通知が機能しない

IOSでのFCM通知に問題があります。

アプリがフォアグラウンド(didReceiveRemoteNotificationのコールバックappdelegateが起動される)で成功した通知を受け取りますが、アプリがバックグラウンドにあるとき通知を受け取りません(何も表示されません) iOSの通知トレイ)。

したがって、問題はFCMによって送信されるメッセージの形式にあると思います。私のサーバーからFCMに送信されるjsonは、次の形式です。

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

ご覧のとおり、jsonには2つのブロックがあります。1つの通知ブロック(バックグラウンドで通知を受信する)と1つのデータブロック(フォアグラウンドで通知を受信する)です。

バックグラウンドでの通知が受信されない理由を理解できません。私の疑問は、ブロックの順序に関するものです(「通知」ブロックの前に「データ」ブロックを置くと問題になりますか?)。

編集:問題に関する詳細情報。

これは私の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に次の2つのメソッドを実装する必要があると書かれています。

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

しかし、これらの機能を実装すると、アプリがフォアグラウンドにある場合でもメッセージの到着が停止します。

これは非常に奇妙です。

編集2:

アプリがバックグラウンドにある場合、通知は受信されませんが、アプリを開くと、同じ通知がすぐに受信されます(didReceiveRemoteNotificationメソッドが起動されます)。

32
Mark O' Brian

すべてを正しく設定したと仮定すると、メッセージのprioritynormalからhighに設定すると、すぐに表示されます。これは、iOSが通知をバンドルして処理する方法によるものです。 FCM通知の優先順位 について読むことができます。バッテリーのペナルティがあるため、適切なケースがない限り、本番環境でhighを実際に使用しないでください。

Appleのドキュメント からの参照です

通知の優先度。次の値のいずれかを指定します。

10–プッシュメッセージをすぐに送信します。この優先度の通知は、ターゲットデバイスでアラート、サウンド、またはバッジをトリガーする必要があります。コンテンツで利用可能なキーのみを含むプッシュ通知にこの優先度を使用するとエラーになります。

5—デバイスの電力に関する考慮事項を考慮したプッシュメッセージを一度に送信します。この優先度の通知はグループ化され、バーストで配信される場合があります。それらは抑制され、場合によっては配信されません。このヘッダーを省略すると、APNsサーバーは優先順位を10に設定します。

23
Chris

以下のように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

17
Keith Holliday

優先度とcontent_available(他の回答で述べたように)は、通知を確実に受け取るための重要な要素です。テストは興味深い結果を示したので、ここでそれらを共有したいと思いました。

テスト結果:Swift 3、Xcode 8、iOS 10

優先度= "高" => "即時"(明らかなネットワーク遅延内)メッセージの受信。

優先度=「通常」=>さまざまな結果(一般に高速ですが、「高」より明らかに遅い)

content_available =通知でtrue(ペイロードメッセージなし)

  • 前景=期待どおりに受信したデータ
  • バックグラウンド=期待どおりに受信したデータ(アプリを開くとき)

content_available =トップレベルでtrue(ペイロードメッセージなし)

  • 前景=期待どおりに受信したデータ
  • バックグラウンド=期待どおりに受信したデータ(アプリを開くとき)

通知ではcontent_available = true(メッセージ{title/body}付き)

  • 前景=受信したデータは2回
  • バックグラウンド= TWICEで受信したデータ(アプリを開くとき)

content_available =トップレベルでtrue(ペイロードメッセージ付き)

  • 前景=受信したデータは2回
  • バックグラウンド= TWICEで受信したデータ(アプリを開くとき)

結論:

  1. 優先度はメッセージを受信しない原因として考えられますが、最も重要な要因は、「content_available」またはペイロードメッセージのいずれかが必要であることです。
  2. content_availableは、データのみのペイロードで使用する必要があります(それなしでは、メッセージは送信されません)。
  3. content_availableは、FCMから二重メッセージが送信されるため、メッセージを含むペイロードには使用しないでください(SHOULD NOT)。
  4. トップレベルまたは通知でcontent_availableを使用しても違いは見つかりませんでした。

編集:追加のテスト結果:-あなたは、MSGのタイトルを持っている場合は、MSGの本文を持っている必要がありますか、アラートを取得しません。

これの奇妙な部分は、バイブレーション、バッジ、サウンドが得られるということですが、タイトルと同様にボディがなければアラートボックスは表示されません。

10
CFP Support

プッシュ通知の資格を追加する必要がある場合があります。これを行うには、ターゲット設定に移動し、[機能]をクリックして、[プッシュ通知]をオンにします。

Target Capabilities

9
astromme

直接FCMチャネルメッセージを使用すると、バックグラウンドで通知を受信できません

これは Firebaseドキュメント からの段落です:

ダイレクトチャネルを有効にすると、FCMバックエンドは信頼できるメッセージキューを使用して、アプリがバックグラウンドにあるか閉じているときに保留中のメッセージを追跡します。アプリがフォアグラウンドになり、接続が再確立されると、チャネルはクライアントから確認応答を受信するまで、保留中のメッセージをクライアントに自動的に送信します。

fCM APNsインターフェイスを使用して、フォアグラウンドとバックグラウンドの両方で通知を受信できます

1
hamed moosaei

-アプリケーションがバックグラウンドまたはフォアグラウンドにあり、OS <10 application(_:didReceiveRemoteNotification :)メソッドが起動するFCMの場合。

-アプリケーションがフォアグラウンドでOS => 10の場合userNotificationCenter:willPresentNotification:withCompletionHandler:メソッドが起動します。

-通知コンポーネントなしでデータメッセージを送信する場合:application(_:didReceiveRemoteNotification :)メソッドが起動します。

-通知コンポーネントでデータメッセージを送信する場合:userNotificationCenter:willPresentNotification:withCompletionHandler:メソッドが起動します。

0