iOS 上的主题消息传递

在发布/订阅模式下,利用 FCM 主题消息传递功能,可以将消息发送至已经选择加入特定主题的多台设备。您根据需要撰写主题消息,FCM 将处理消息路由并将消息可靠地传送至正确的设备。

例如,某个本地天气预报应用的用户可选择加入“恶劣天气警报”主题,并接收威胁特定区域的风暴的通知。体育应用的用户可以订阅他们心仪球队的实时比分自动更新。

关于主题,请注意以下事项:

  • 主题消息传递不限制每个应用拥有的主题和订阅数。
  • 主题消息传递最适合传递新闻、天气或其他可通过公开途径获得的信息等内容。
  • 主题消息针对吞吐量(而非延迟)进行了优化。要将消息快速安全地传送到单台设备或小规模设备组,应将消息定位至注册令牌,而非主题。
  • 如果您需要向一位用户的多台设备发送消息,可考虑针对这些使用情形使用设备组消息传递

为客户端应用订阅主题

客户端应用可以订阅任何现有主题,也可创建新主题。当客户端应用订阅新的主题名称(您的 Firebase 项目中尚不存在的名称)时,系统会在 FCM 中创建使用这个名称的新主题,随后任何客户端都可订阅该主题。

FIRMessaging 类可处理主题消息传递功能。要订阅一个主题,请从应用的主线程中调用 subscribeToTopic:topic(FCM 并非是线程安全的):

[[FIRMessaging messaging] subscribeToTopic:@"news"];
NSLog(@"Subscribed to news topic");

这会向 FCM 后端发出一个异步请求,并为该客户端订阅指定主题。在调用 subscribeToTopic:topic 之前,请确保客户端应用实例已通过回调函数 didReceiveRegistrationToken 收到注册令牌。

如果一开始订阅请求失败,FCM 会重试,直至能够成功订阅该主题。每次应用启动时,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 云消息传递主题发送消息与向单台设备或一个用户组发送消息十分类似。应用服务器会将消息正文中的 topic 键设为类似 yourTopic 这样的值。开发者可以选择符合以下正则表达式的任何主题名称:"[a-zA-Z0-9-_.~%]+"

要向多个主题的组合发送消息,应用服务器会将 condition 键设置为指定目标主题的某个布尔型条件。例如,向已订阅 TopicATopicBTopicC 的设备发送消息:

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

FCM 首先对括号中的所有条件求值,然后从左至右对表达式求值。在上述表达式中,只订阅某个单一主题的用户将不会接收到消息。同样地,未订阅 TopicA 的用户也不会接收到消息。下列组合将会接收到消息:

  • TopicA 和 TopicB
  • TopicA 和 TopicC

您最多可以在条件表达式中添加五个主题,支持使用括号。支持的运算符包括 &&||!。请注意 ! 的用法:

!('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 '{
  "notification": {
    "title": "FCM Message",
    "body": "This is a Firebase Cloud Messaging Topic Message!",
  },
  "topic" : "foo-bar"
}' "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 参考

后续步骤

发送以下问题的反馈:

此网页
需要帮助?请访问我们的支持页面