iOS でトピックにメッセージを送信する

パブリッシュ / サブスクライブ モデルに基づく FCM トピック メッセージングでは、特定のトピックにオプトインした複数の端末にメッセージを送信できます。必要に応じて作成したトピック メッセージを FCM がルーティング処理し、確実に正しい端末にメッセージを配信します。

たとえば、地域の潮汐予測アプリのユーザーは、「潮流警報」トピックにオプトインし、特定の地域の海釣りに最適な状況の通知を受信できます。スポーツアプリのユーザーは、お気に入りのチームの実況ゲームスコアの自動更新に登録できます。

トピックに関する留意点を以下に示します。

  • トピック メッセージングは、天気、その他の広く入手可能な情報コンテンツに最適です。
  • トピック メッセージは、レイテンシではなくスループットに対して最適化されます。単一のデバイスまたはデバイスの小グループに高速で安全に配信する場合は、トピックではなく登録トークンをメッセージのターゲットとしてください
  • 各ユーザーの複数のデバイスにメッセージを送信する必要がある場合は、そのようなユースケース向けのデバイス グループ メッセージングを検討してください。
  • トピック メッセージングでは、トピックごとの登録数に制限はありませんが、FCM により次の制限が課されます。
    • 1 つのアプリ インスタンスを登録できるのは、2,000 トピックまでです。
    • 一括インポートを使用してアプリ インスタンスを登録する場合、各リクエストのアプリ インスタンスの数が 1,000 に制限されます。
    • 新規登録の頻度がプロジェクトごとに制限されます。短期間に多数の登録リクエストを送信すると、FCM サーバーから 429 RESOURCE_EXHAUSTED(「割り当てを超過した」)というレスポンスが返されます。この場合は、指数バックオフを使用して再試行します。

クライアント アプリをトピックに登録する

クライアント アプリを既存のトピックに登録することも、新しいトピックを作成することもできます。クライアント アプリを新しいトピック名(Firebase プロジェクトにまだ存在していないトピック名)に登録すると、その名前の新しいトピックが FCM に作成され、その後すべてのクライアントはそのトピック名に登録できるようになります。

トピックに登録するには、アプリケーションのメインスレッドから登録メソッドを呼び出します(FCM はスレッドセーフではありません)。最初の登録リクエストが失敗すると、FCM は自動的に再試行します。登録を完了できなければ、その登録はエラーをスローします。その場合、次のように完了ハンドラでエラーをキャッチできます。

Swift

Messaging.messaging().subscribe(toTopic: "weather") { error in
  print("Subscribed to weather topic")
}

Objective-C

[[FIRMessaging messaging] subscribeToTopic:@"weather"
                                completion:^(NSError * _Nullable error) {
  NSLog(@"Subscribed to weather topic");
}];

これにより、FCM バックエンドへの非同期リクエストが作成され、所定のトピックにクライアントが登録されます。subscribeToTopic:topic を呼び出す前には、コールバック didReceiveRegistrationToken を介してクライアント アプリ インスタンスがすでに登録トークンを受信していることを確認します。

アプリが開始するごとに、FCM は、リクエストされたすべてのトピックが登録されたことを確認します。登録解除するには、unsubscribeFromTopic:topic を呼び出します。これにより、FCM がバックグラウンドでトピックからの登録解除を行います。

サーバーでトピック登録を管理する

Instance ID API を利用して、サーバー側から基本的なトピック管理タスクを行うことができます。クライアント アプリ インスタンスの登録トークンを取得すると、次の操作を行えます。

メッセージを受信して処理する

FCM は、他のダウンストリーム メッセージと同じようにトピック メッセージを配信します。

AppDelegate application:didReceiveRemoteNotification: を次のように実装します。

Swift

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
  // 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)")
  }

  // Print full message.
  print(userInfo)
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (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

  // 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)")
  }

  // Print full message.
  print(userInfo)

  completionHandler(UIBackgroundFetchResult.newData)
}

Objective-C

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
  // 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
  // [[FIRMessaging messaging] appDidReceiveMessage:userInfo];

  // Print message ID.
  if (userInfo[kGCMMessageIDKey]) {
    NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]);
  }

  // Print full message.
  NSLog(@"%@", userInfo);
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  // 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
  // [[FIRMessaging messaging] appDidReceiveMessage:userInfo];

  // Print message ID.
  if (userInfo[kGCMMessageIDKey]) {
    NSLog(@"Message ID: %@", userInfo[kGCMMessageIDKey]);
  }

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

  completionHandler(UIBackgroundFetchResultNewData);
}

ビルド送信リクエスト

Firebase Cloud Messaging トピックへのメッセージを送信する方法は、個々の端末やユーザー グループ宛てのメッセージを送信する場合とよく似ています。アプリサーバーはメッセージ本文の topic キーに yourTopic などの値を設定します。デベロッパーは、正規表現 "[a-zA-Z0-9-_.~%]+" に一致するすべてのトピック名を選択できます。

複数のトピックの組み合わせに送信する場合、アプリサーバーは condition キーに、対象のトピックを指定するブール条件を設定します。たとえば、TopicA と、TopicB または TopicC のいずれかに登録されたデバイスにメッセージを送信する場合には、次のようになります。

'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)

FCM はまず、かっこ内の条件を評価し、次に左から右に式を評価していきます。上記の式では、いずれか 1 つのトピックだけに登録したユーザーはメッセージを受信しません。同様に、TopicA に登録していないユーザーはメッセージを受信しません。メッセージを受信するのは、次の組み合わせの場合のみです。

  • TopicA と TopicB
  • TopicA と TopicC

条件式には最大 5 つのトピックを含めることができ、かっこを使用できます。使用できる演算子は &&||! です。! の使用方法に注意してください。

!('TopicA' in topics)

この式では、どのトピックにも登録されていないアプリ インスタンスを含め、TopicA に登録されていないアプリ インスタンスすべてがメッセージを受信します。

アプリサーバーのキーの詳細については、リファレンス情報をご覧ください。

トピック HTTP POST リクエスト

単一のトピックに送信します。

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA
{
  "message":{
    "topic" : "foo-bar",
    "notification" : {
      "body" : "This is a Firebase Cloud Messaging Topic Message!",
      "title" : "FCM Message"
      }
   }
}

次の cURL で送信します。

curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
  "message": {
    "topic" : "foo-bar",
    "notification": {
      "body": "This is a Firebase Cloud Messaging Topic Message!",
      "title": "FCM Message"
    }
  }
}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

「犬」('dogs')または「猫」('cats')のトピックに登録されたデバイスに送信します。

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA
{
   "message":{
    "condition": "'dogs' in topics || 'cats' in topics",
    "notification" : {
      "body" : "This is a Firebase Cloud Messaging Topic Message!",
      "title" : "FCM Message",
    }
  }
}

次の cURL で送信します。

curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
  "notification": {
    "title": "FCM Message",
    "body": "This is a Firebase Cloud Messaging Topic Message!",
  },
  "condition": "'dogs' in topics || 'cats' in topics"
}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1

トピック HTTP レスポンス

{
    "name": "projects/myproject-b5ae1/messages/5735743068807585451"
}

メッセージ オプションの完全なリストについては、HTTP v1 API リファレンスをご覧ください。

次のステップ

フィードバックを送信...

ご不明な点がありましたら、Google のサポートページをご覧ください。