如要將訊息傳送至多部裝置,請使用主題訊息。這項功能可讓您將訊息傳送至已選擇特定主題的多部裝置。
本教學課程著重於使用 FCM 的 Admin SDK 或 REST API,從應用程式伺服器傳送主題訊息,並在 Apple 應用程式中接收及處理這些訊息。本頁面列出所有步驟,從設定到驗證都有,因此如果您已設定 Apple 用戶端應用程式以使用 FCM,或已完成傳送第一則訊息的步驟,可能已完成部分步驟。
將 Firebase 新增至 Apple 專案
如果您已為應用程式啟用其他 Firebase 功能,可能已完成本節所述工作。不過,如要使用 FCM,您需要上傳 APN 驗證金鑰,並註冊遠端通知。
事前準備
安裝下列項目:
- Xcode 16.2 以上版本
請確認專案符合下列規定:
- 專案必須以這些平台版本或更新版本為目標:
- iOS 13
- macOS 10.15
- tvOS 13
- watchOS 7
- 專案必須以這些平台版本或更新版本為目標:
設定實體 Apple 裝置來執行應用程式,並完成下列工作:
- 為Apple 開發人員帳戶取得 Apple 推播通知驗證金鑰。
- 在 XCode 中依序前往「App」>「Capabilities」,啟用推播通知。
- 使用 Google 帳戶登入 Firebase。
如果您還沒有 Xcode 專案,只是想試用 Firebase 產品,可以下載我們的快速入門範例。
建立 Firebase 專案
將 Firebase 加入 Apple 應用程式前,請先建立要連結至該應用程式的 Firebase 專案。如要進一步瞭解 Firebase 專案,請參閱「瞭解 Firebase 專案」一文。
向 Firebase 註冊應用程式
如要在 Apple 應用程式中使用 Firebase,請向 Firebase 專案註冊應用程式。註冊應用程式通常稱為「將應用程式新增至專案」。
前往 Firebase 控制台。
在專案總覽頁面中間,按一下「iOS+」圖示,啟動設定工作流程。
如果已將應用程式新增至 Firebase 專案,請按一下「新增應用程式」,顯示平台選項。
在「軟體包 ID」欄位中輸入應用程式的軟體包 ID。
(選用) 輸入其他應用程式資訊: 「應用程式暱稱」和「App Store ID」。
按一下 [Register app] (註冊應用程式)。
新增 Firebase 設定檔
按一下「Download GoogleService-Info.plist」,取得應用程式的 Firebase 設定檔 (
GoogleService-Info.plist
)。將設定檔移至 Xcode 專案的根目錄。如果系統提示,請選取將設定檔新增至所有目標。
如果專案中有多個套件組合 ID,您必須將每個套件組合 ID 與 Firebase 控制台中的已註冊應用程式建立關聯,這樣每個應用程式才能擁有自己的 GoogleService-Info.plist
檔案。
在應用程式中新增 Firebase SDK
使用 Swift Package Manager 安裝及管理 Firebase 依附元件。
- 在 Xcode 中保持開啟應用程式專案,然後依序點選「File」(檔案) 和「Add Packages」(新增 Package)。
- 系統提示時,請新增 Firebase Apple 平台 SDK 存放區:
- 選擇 Firebase Cloud Messaging 程式庫。
- 將
-ObjC
標記新增至目標建構設定的「Other Linker Flags」部分。 - 為獲得最佳 Firebase Cloud Messaging 使用體驗,建議您在 Firebase 專案中啟用 Google Analytics,並在應用程式中加入 Google Analytics 專用 Firebase SDK。您可以選擇不收集 IDFA 的程式庫,也可以選擇收集 IDFA 的程式庫。請參閱常見問題,瞭解 Firebase SDK 中 Google Analytics 的最新模組架構。
- 完成後,Xcode 會自動開始在背景中解析並下載依附元件。
https://github.com/firebase/firebase-ios-sdk.git
上傳 APN 驗證金鑰
將 APNs 驗證金鑰上傳至 Firebase。 如果沒有 APNs 驗證金鑰,請務必在 Apple 開發人員會員中心建立。
-
在 Firebase 控制台的專案中,依序選取齒輪圖示、「專案設定」和「Cloud Messaging」分頁標籤。
-
在「iOS 應用程式設定」下方的「APNs 驗證金鑰」, 按一下「上傳」 按鈕。
-
瀏覽至您儲存金鑰的位置,選取金鑰,然後按一下「開啟」。新增金鑰的 ID (可在 Apple Developer Member Center 中取得),然後按一下「上傳」。
在應用程式中初始化 Firebase
您需要在應用程式中加入 Firebase 初始化程式碼。匯入 Firebase 模組,並設定共用執行個體,如下所示:
- 在
FirebaseCore
中匯入UIApplicationDelegate
模組,以及應用程式委派使用的任何其他 Firebase 模組。舉例來說,如要使用 Cloud Firestore 和 Authentication:SwiftUI
import SwiftUI import FirebaseCore import FirebaseFirestore import FirebaseAuth // ...
Swift
import FirebaseCore import FirebaseFirestore import FirebaseAuth // ...
Objective-C
@import FirebaseCore; @import FirebaseFirestore; @import FirebaseAuth; // ...
- 在應用程式委派的
application(_:didFinishLaunchingWithOptions:)
方法中,設定FirebaseApp
共用例項:SwiftUI
// Use Firebase library to configure APIs FirebaseApp.configure()
Swift
// Use Firebase library to configure APIs FirebaseApp.configure()
Objective-C
// Use Firebase library to configure APIs [FIRApp configure];
- 如果您使用 SwiftUI,就必須建立應用程式委派,並透過
UIApplicationDelegateAdaptor
或NSApplicationDelegateAdaptor
將其附加至App
結構體。您也必須停用應用程式委派項目的交換。詳情請參閱 SwiftUI 指示。SwiftUI
@main struct YourApp: App { // register app delegate for Firebase setup @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate var body: some Scene { WindowGroup { NavigationView { ContentView() } } } }
註冊遠端通知
在啟動時或應用程式流程中的所需時間點,註冊應用程式以接收遠端通知。如以下範例所示呼叫registerForRemoteNotifications
:
Swift
UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization( options: authOptions, completionHandler: { _, _ in } ) application.registerForRemoteNotifications()
Objective-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 會自動重試。 如果無法完成訂閱,訂閱項目會擲回錯誤,您可以在完成處理常式中擷取該錯誤,如下所示:
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 會在背景取消訂閱主題。
接收及處理主題訊息
FCM 傳送主題訊息的方式與其他下游訊息相同。
如以下範例所示,實作 application(_:didReceiveRemoteNotification:fetchCompletionHandler:)
:
Swift
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 }
Objective-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 的指南,瞭解重要的背景和設定資訊。
在後端的傳送邏輯中,指定所需的主題名稱,如下所示:
Node.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);
});
Java
// 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)
Go
// 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);
REST
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
如要將訊息傳送至主題組合,請指定條件,也就是指定目標主題的布林運算式。舉例來說,下列條件會將訊息傳送至已訂閱 TopicA
,且訂閱 TopicB
或 TopicC
的裝置:
"'TopicA' in topics && ('TopicB' in topics || 'TopicC' in topics)"
FCM 會先評估括號中的任何條件,然後從左到右評估運算式。在上述運算式中,訂閱任何單一主題的使用者都不會收到訊息。同樣地,如果使用者未訂閱 TopicA
,就不會收到訊息。以下組合會收到這項資訊:
TopicA
和TopicB
TopicA
和TopicC
條件式運算式最多可包含五個主題。
如要傳送至條件:
Node.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);
});
Java
// 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)
Go
// 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);
REST
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
後續步驟
- 您可以使用伺服器訂閱主題的用戶端應用程式執行個體,以及執行其他管理工作。請參閱「在伺服器上管理主題訂閱項目」。