获取我们在 Firebase 峰会上发布的所有信息,了解 Firebase 可如何帮助您加快应用开发速度并满怀信心地运行应用。了解详情

在 Apple 平台上向多台设备发送消息

Firebase Cloud Messaging 提供了两种将消息发送到多个设备的方法:

  • 主题消息,它允许您向已选择加入特定主题的多个设备发送消息。
  • Device group messaging ,它允许您将一条消息发送到在属于一个组的设备上运行的应用程序的多个实例。

本教程重点介绍使用适用于 FCM 的Admin SDKREST API从您的应用服务器发送主题消息,并在 Apple 应用中接收和处理它们。此页面列出了实现此目的的所有步骤,从设置到验证 — 因此,如果您已经为 FCM设置了 Apple 客户端应用程序或完成了发送您的第一条消息的步骤,那么它可能涵盖您已经完成的步骤。

将 Firebase 添加到您的 Apple 项目

如果您已经为您的应用启用了其他 Firebase 功能,本部分涵盖您可能已经完成的任务。具体对于 FCM,您需要上传您的 APNs 身份验证密钥注册远程通知

先决条件

  • 安装以下内容:

    • Xcode 13.3.1 或更高版本
  • 确保您的项目满足以下要求:

    • 您的项目必须针对这些平台版本或更高版本:
      • iOS 11
      • macOS 10.13
      • 电视操作系统 12
      • watchOS 6
  • 设置物理 Apple 设备来运行您的应用程序,并完成以下任务:

    • 为您的Apple Developer 帐户获取 Apple Push Notification Authentication Key。
    • App > Capabilities下的 XCode 中启用推送通知。

如果您还没有 Xcode 项目并且只想试用 Firebase 产品,您可以下载我们的快速入门示例之一。

创建一个 Firebase 项目

在将 Firebase 添加到您的 Apple 应用程序之前,您需要创建一个 Firebase 项目以连接到您的应用程序。访问了解 Firebase 项目以了解有关 Firebase 项目的更多信息。

向 Firebase 注册您的应用

要在您的 Apple 应用程序中使用 Firebase,您需要在 Firebase 项目中注册您的应用程序。注册您的应用程序通常称为将您的应用程序“添加”到您的项目中。

  1. 转到Firebase 控制台

  2. 在项目概览页面的中央,单击iOS+图标以启动设置工作流程。

    如果您已将应用程序添加到 Firebase 项目,请单击添加应用程序以显示平台选项。

  3. bundle ID字段中输入您应用程序的 bundle ID。

  4. (可选)输入其他应用信息:应用昵称App Store ID

  5. 单击注册应用程序

添加 Firebase 配置文件

  1. 单击下载 GoogleService-Info.plist以获取您的 Firebase Apple 平台配置文件 ( GoogleService-Info.plist )。

  2. 将配置文件移动到 Xcode 项目的根目录中。如果出现提示,请选择将配置文件添加到所有目标。

如果您的项目中有多个 bundle ID,则必须将每个 bundle ID 与 Firebase 控制台中注册的应用相关联,以便每个应用都可以拥有自己的GoogleService-Info.plist文件。

将 Firebase SDK 添加到您的应用

使用 Swift Package Manager 安装和管理 Firebase 依赖项。

  1. 在 Xcode 中,打开您的应用程序项目,导航至File > Add Packages
  2. 出现提示时,添加 Firebase Apple 平台 SDK 存储库:
  3.   https://github.com/firebase/firebase-ios-sdk
  4. 选择 Firebase 云消息传递库。
  5. 为了获得 Firebase 云消息传递的最佳体验,我们建议在您的 Firebase 项目中启用 Google Analytics ,并将适用于 Google Analytics 的 Firebase SDK 添加到您的应用程序中。您可以选择没有 IDFA 集合或有 IDFA 集合的库。
  6. 完成后,Xcode 将自动开始在后台解析和下载您的依赖项。

上传您的 APNs 身份验证密钥

将您的 APNs 身份验证密钥上传到 Firebase。如果您还没有 APNs 身份验证密钥,请确保在Apple Developer Member Center中创建一个。

  1. 在 Firebase 控制台的项目内,选择齿轮图标,选择Project Settings ,然后选择Cloud Messaging选项卡。

  2. iOS app configuration下的APNs authentication key中,点击Upload按钮。

  3. 浏览到您保存密钥的位置,选择它,然后单击打开。添加密钥的密钥 ID(可在Apple Developer Member Center中获得)并单击Upload

在您的应用中初始化 Firebase

您需要将 Firebase 初始化代码添加到您的应用程序中。导入 Firebase 模块并配置共享实例,如下所示:

  1. 在您的UIApplicationDelegate中导入FirebaseCore模块,以及您的应用委托使用的任何其他Firebase 模块。例如,要使用 Cloud Firestore 和身份验证:

    迅速

    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    目标-C

    @import FirebaseCore;
    @import FirebaseFirestore;
    @import FirebaseAuth;
    // ...
          
  2. 在您的应用委托的application(_:didFinishLaunchingWithOptions:)方法中配置一个FirebaseApp共享实例:

    迅速

    // Use Firebase library to configure APIs
    FirebaseApp.configure()

    目标-C

    // Use Firebase library to configure APIs
    [FIRApp configure];

注册远程通知

在启动时或应用程序流程中的所需时间点,注册您的应用程序以获取远程通知。如图所示调用registerForRemoteNotifications

迅速


UNUserNotificationCenter.current().delegate = self

let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
  options: authOptions,
  completionHandler: { _, _ in }
)

application.registerForRemoteNotifications()

目标-C


[UNUserNotificationCenter currentNotificationCenter].delegate = self;
UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert |
    UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
[[UNUserNotificationCenter currentNotificationCenter]
    requestAuthorizationWithOptions:authOptions
    completionHandler:^(BOOL granted, NSError * _Nullable error) {
      // ...
    }];

[application registerForRemoteNotifications];

为客户端应用程序订阅主题

客户端应用程序可以订阅任何现有主题,也可以创建新主题。当客户端应用程序订阅一个新主题名称(您的 Firebase 项目尚不存在的主题名称)时,将在 FCM 中创建一个具有该名称的新主题,随后任何客户端都可以订阅它。

要订阅主题,请从应用程序的主线程调用订阅方法(FCM 不是线程安全的)。如果订阅请求最初失败,FCM 会自动重试。对于无法完成订阅的情况,订阅会抛出一个错误,您可以在完成处理程序中捕获该错误,如下所示:

迅速

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

目标-C

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

此调用向 FCM 后端发出异步请求,并为客户端订阅给定主题。在调用subscribeToTopic:topic之前,确保客户端应用程序实例已经通过回调didReceiveRegistrationToken接收到注册令牌。

每次应用程序启动时,FCM 都会确保所有请求的主题都已订阅。要取消订阅,请调用unsubscribeFromTopic:topic ,FCM 会在后台取消订阅该主题。

接收和处理主题消息

FCM 以与其他下游消息相同的方式传递主题消息。

实现application(_:didReceiveRemoteNotification:fetchCompletionHandler:)如下所示:

迅速

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

  return UIBackgroundFetchResult.newData
}

目标-C

- (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 full message.
  NSLog(@"%@", userInfo);

  completionHandler(UIBackgroundFetchResultNewData);
}

构建发送请求

创建主题后,通过在客户端订阅客户端应用程序实例或通过服务器 API订阅主题,您可以向主题发送消息。如果这是您第一次为 FCM 构建发送请求,请参阅您的服务器环境和 FCM指南,了解重要的背景和设置信息。

在后端的发送逻辑中,指定所需的主题名称,如下所示:

节点.js

// The topic name can be optionally prefixed with "/topics/".
const topic = 'highScores';

const message = {
  data: {
    score: '850',
    time: '2:45'
  },
  topic: topic
};

// Send a message to devices subscribed to the provided topic.
getMessaging().send(message)
  .then((response) => {
    // Response is a message ID string.
    console.log('Successfully sent message:', response);
  })
  .catch((error) => {
    console.log('Error sending message:', error);
  });

爪哇

// The topic name can be optionally prefixed with "/topics/".
String topic = "highScores";

// See documentation on defining a message payload.
Message message = Message.builder()
    .putData("score", "850")
    .putData("time", "2:45")
    .setTopic(topic)
    .build();

// Send a message to the devices subscribed to the provided topic.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

Python

# The topic name can be optionally prefixed with "/topics/".
topic = 'highScores'

# See documentation on defining a message payload.
message = messaging.Message(
    data={
        'score': '850',
        'time': '2:45',
    },
    topic=topic,
)

# Send a message to the devices subscribed to the provided topic.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)

// The topic name can be optionally prefixed with "/topics/".
topic := "highScores"

// See documentation on defining a message payload.
message := &messaging.Message{
	Data: map[string]string{
		"score": "850",
		"time":  "2:45",
	},
	Topic: topic,
}

// Send a message to the devices subscribed to the provided topic.
response, err := client.Send(ctx, message)
if err != nil {
	log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)

C#

// The topic name can be optionally prefixed with "/topics/".
var topic = "highScores";

// See documentation on defining a message payload.
var message = new Message()
{
    Data = new Dictionary<string, string>()
    {
        { "score", "850" },
        { "time", "2:45" },
    },
    Topic = topic,
};

// Send a message to the devices subscribed to the provided topic.
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);

休息

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 -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

要向主题组合发送消息,请指定condition ,它是指定目标主题的布尔表达式。例如,以下条件会将消息发送到订阅了TopicATopicBTopicC的设备:

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

FCM 首先计算括号中的任何条件,然后从左到右计算表达式。在上面的表达式中,订阅任何单个主题的用户不会收到消息。同样,未订阅TopicA的用户也不会收到消息。这些组合确实收到了它:

  • TopicATopicB
  • TopicATopicC

您最多可以在条件表达式中包含五个主题。

发送到一个条件:

节点.js

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
const condition = '\'stock-GOOG\' in topics || \'industry-tech\' in topics';

// See documentation on defining a message payload.
const message = {
  notification: {
    title: '$FooCorp up 1.43% on the day',
    body: '$FooCorp gained 11.80 points to close at 835.67, up 1.43% on the day.'
  },
  condition: condition
};

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
getMessaging().send(message)
  .then((response) => {
    // Response is a message ID string.
    console.log('Successfully sent message:', response);
  })
  .catch((error) => {
    console.log('Error sending message:', error);
  });

爪哇

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
String condition = "'stock-GOOG' in topics || 'industry-tech' in topics";

// See documentation on defining a message payload.
Message message = Message.builder()
    .setNotification(Notification.builder()
        .setTitle("$GOOG up 1.43% on the day")
        .setBody("$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.")
        .build())
    .setCondition(condition)
    .build();

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
String response = FirebaseMessaging.getInstance().send(message);
// Response is a message ID string.
System.out.println("Successfully sent message: " + response);

Python

# Define a condition which will send to devices which are subscribed
# to either the Google stock or the tech industry topics.
condition = "'stock-GOOG' in topics || 'industry-tech' in topics"

# See documentation on defining a message payload.
message = messaging.Message(
    notification=messaging.Notification(
        title='$GOOG up 1.43% on the day',
        body='$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.',
    ),
    condition=condition,
)

# Send a message to devices subscribed to the combination of topics
# specified by the provided condition.
response = messaging.send(message)
# Response is a message ID string.
print('Successfully sent message:', response)

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
condition := "'stock-GOOG' in topics || 'industry-tech' in topics"

// See documentation on defining a message payload.
message := &messaging.Message{
	Data: map[string]string{
		"score": "850",
		"time":  "2:45",
	},
	Condition: condition,
}

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
response, err := client.Send(ctx, message)
if err != nil {
	log.Fatalln(err)
}
// Response is a message ID string.
fmt.Println("Successfully sent message:", response)

C#

// Define a condition which will send to devices which are subscribed
// to either the Google stock or the tech industry topics.
var condition = "'stock-GOOG' in topics || 'industry-tech' in topics";

// See documentation on defining a message payload.
var message = new Message()
{
    Notification = new Notification()
    {
        Title = "$GOOG up 1.43% on the day",
        Body = "$GOOG gained 11.80 points to close at 835.67, up 1.43% on the day.",
    },
    Condition = condition,
};

// Send a message to devices subscribed to the combination of topics
// specified by the provided condition.
string response = await FirebaseMessaging.DefaultInstance.SendAsync(message);
// Response is a message ID string.
Console.WriteLine("Successfully sent message: " + response);

休息

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 -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

下一步