如要將訊息指定至多部裝置,請使用主題訊息功能。這項功能可讓你向已選擇加入特定主題的多部裝置傳送訊息。
本教學課程著重於使用 Admin SDK 或 FCM 的 REST API,從應用程式伺服器傳送主題訊息,以及在 Apple 應用程式中接收及處理這些訊息。本頁面列出從設定到驗證的所有步驟,因此如果您已為 FCM 設定 Apple 用戶端應用程式,或已完成傳送第一則訊息的步驟,這裡可能會涵蓋您已完成的步驟。
將 Firebase 新增至您的 Apple 專案
本節將說明如果您已為應用程式啟用其他 Firebase 功能,可能已完成的作業。具體來說,您需要上傳 APNs 驗證金鑰,並註冊遠端通知。
事前準備
安裝下列項目:
- Xcode 15.2 以上版本
請確認您的專案符合下列規定:
- 您的專案必須指定以下平台版本或更新版本:
- iOS 13
- macOS 10.15
- tvOS 13
- watchOS 7
- 您的專案必須指定以下平台版本或更新版本:
設定實體 Apple 裝置來執行應用程式,並完成下列工作:
- 取得 Apple Developer 帳戶的 Apple 推播通知驗證金鑰。
- 在 XCode 中依序前往「App」>「Capabilities」,啟用推播通知。
- 使用 Google 帳戶登入 Firebase。
如果您還沒有 Xcode 專案,但想試用 Firebase 產品,可以下載我們的快速入門範例。
建立 Firebase 專案
如要將 Firebase 新增至 Apple 應用程式,您必須先建立 Firebase 專案,才能連結至應用程式。請參閱「瞭解 Firebase 專案」,進一步瞭解 Firebase 專案。
透過 Firebase 註冊應用程式
如要在 Apple 應用程式中使用 Firebase,您必須使用 Firebase 專案註冊應用程式。註冊應用程式通常稱為「新增」應用程式至專案中。
前往 Firebase 控制台。
在專案總覽頁面的中間,按一下 iOS+ 圖示,啟動設定工作流程。
如果您已將應用程式新增至 Firebase 專案,請按一下「Add app」,顯示平台選項。
在「bundle ID」欄位中輸入應用程式的軟體包 ID。
(選用) 輸入其他應用程式資訊:應用程式暱稱和 App Store ID。
按一下 [Register app] (註冊應用程式)。
新增 Firebase 設定檔
點選「Download GoogleService-Info.plist」,取得 Firebase Apple 平台設定檔 (
GoogleService-Info.plist
)。將設定檔移至 Xcode 專案的根目錄。如果系統提示,請選取將設定檔新增至所有目標。
如果專案中有多個軟體包 ID,您必須將每個軟體包 ID 與 Firebase 控制台中的註冊應用程式建立關聯,讓每個應用程式擁有自己的 GoogleService-Info.plist
檔案。
在應用程式中新增 Firebase SDK
使用 Swift Package Manager 安裝及管理 Firebase 依附元件。
- 在 Xcode 中保持開啟應用程式專案,然後依序點選「File」>「Add Packages」。
- 系統顯示提示訊息時,請新增 Firebase Apple 平台 SDK 存放區:
- 選擇 Firebase Cloud Messaging 程式庫。
- 將
-ObjC
標記新增至目標的建構設定「Other Linker Flags」部分。 - 為提供最佳的 Firebase Cloud Messaging 體驗,建議您在 Firebase 專案中啟用 Google Analytics,並將 Google Analytics 專用 Firebase SDK 新增至應用程式。您可以選擇不收集 IDFA 的程式庫,也可以選擇收集 IDFA 的程式庫。
- 完成後,Xcode 就會自動開始在背景中解析並下載依附元件。
https://github.com/firebase/firebase-ios-sdk.git
上傳 APN 驗證金鑰
將 APNs 驗證金鑰上傳至 Firebase。如果您尚未取得 APNs 驗證金鑰,請務必前往 Apple Developer Member Center 建立金鑰。
-
在 Firebase 控制台的專案中,依序選取齒輪圖示、「Project Settings」(專案設定),然後選取「Cloud Messaging」分頁。
-
在「iOS 應用程式設定」下方的「APNs 驗證金鑰」中,按一下「上傳」按鈕。
-
瀏覽至您儲存金鑰的位置,選取金鑰,然後按一下 [Open] (開啟)。新增金鑰的金鑰 ID (可在 Apple Developer Member Center 中找到),然後按一下「上傳」。
在應用程式中初始化 Firebase
您必須在應用程式中加入 Firebase 初始化程式碼。匯入 Firebase 模組,並設定共用例項,如下所示:
- 在
UIApplicationDelegate
中匯入FirebaseCore
模組,以及應用程式委派程式使用的任何其他 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
後續步驟
- 您可以使用伺服器讓用戶端應用程式執行緒訂閱主題,並執行其他管理工作。請參閱管理伺服器上的主題訂閱。