透過 Apple 平台將訊息傳送至多部裝置

如要將訊息指定至多部裝置,請使用主題訊息功能。您可以透過這項功能,將訊息傳送給已選擇加入特定主題的多部裝置。

本教學課程主要說明使用 FCM 的 Admin SDKREST API 從應用程式伺服器傳送主題訊息,以及在 Apple 應用程式中接收和處理這些訊息。本頁列出設定到驗證的所有步驟,當中可能涵蓋了您已針對 FCM 設定 Apple 用戶端應用程式時已完成的步驟,或逐步完成傳送第一則訊息的步驟。

將 Firebase 新增至您的 Apple 專案

本節說明如果您已為應用程式啟用其他 Firebase 功能,則可能需要完成的任務。具體來說,您必須上傳 APN 驗證金鑰註冊遠端通知

先備知識

  • 安裝下列項目:

    • Xcode 14.1 以上版本
  • 確認專案符合下列規定:

    • 您的專案必須指定下列平台版本或更新版本:
      • iOS 11 作業系統
      • macOS 10.13
      • tvOS 12
      • WatchOS 6
  • 設定實體 Apple 裝置執行應用程式,然後完成以下工作:

    • 為您的 Apple 開發人員帳戶取得 Apple 推播通知驗證金鑰。
    • 依序前往「App」>「Capabilities」,在 XCode 中啟用推播通知。

如果您還沒有 Xcode 專案,只是想試用 Firebase 產品,可以下載其中一個快速入門導覽課程範例

建立 Firebase 專案

您需要先建立 Firebase 專案並連結至應用程式,才能將 Firebase 新增到 Apple 應用程式。如要進一步瞭解 Firebase 專案,請參閱「瞭解 Firebase 專案」。

透過 Firebase 註冊應用程式

如要在 Apple 應用程式中使用 Firebase,您必須使用 Firebase 專案註冊應用程式。註冊應用程式通常稱為「新增」應用程式至專案中。

  1. 前往 Firebase 主控台

  2. 在專案總覽頁面中間,按一下「iOS+」圖示來啟動設定工作流程。

    如果您已經在 Firebase 專案中加入應用程式,請按一下「Add app」顯示平台選項。

  3. 在「bundle ID」欄位中輸入應用程式的軟體包 ID。

  4. (選用) 輸入其他應用程式資訊:「應用程式暱稱」和「App Store ID」

  5. 按一下 [Register app] (註冊應用程式)

新增 Firebase 設定檔

  1. 點選「Download GoogleService-Info.plist」,取得 Firebase Apple 平台設定檔 (GoogleService-Info.plist)。

  2. 將設定檔移至 Xcode 專案的根目錄。如果出現提示,請選擇將設定檔新增至所有目標。

如果專案中有多個軟體包 ID,您必須在 Firebase 控制台中,將每個軟體包 ID 與已註冊的應用程式建立關聯,讓每個應用程式擁有自己的 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.git
  4. 選擇 Firebase 雲端通訊程式庫。
  5. 在目標建構設定的「Other Linker Flags」部分中新增 -ObjC 標記。
  6. 為了讓 Firebase 雲端通訊獲得最佳體驗,建議您在 Firebase 專案中啟用 Google Analytics (分析),並將 Google Analytics (分析) 專用 Firebase SDK 加到應用程式。您可以選取不使用 IDFA 收集功能的程式庫,或使用 IDFA 收集功能。
  7. 完成後,Xcode 會自動開始在背景解析並下載依附元件。

上傳 APN 驗證金鑰

將 APN 驗證金鑰上傳至 Firebase。如果您還沒有 APN 驗證金鑰,請務必前往 Apple Developer Member Center 建立 APN 驗證金鑰。

  1. 在 Firebase 控制台的專案中,依序選取齒輪圖示 >「專案設定」,然後選取「雲端通訊」分頁標籤。

  2. 在「iOS app configuration」下方的「APNs 驗證金鑰」中,按一下「上傳」按鈕。

  3. 瀏覽至您儲存金鑰的位置,選取金鑰,然後按一下 [Open] (開啟)。加入金鑰金鑰 ID (可於 Apple Developer Member Center 中找到),然後按一下「上傳」

在應用程式中初始化 Firebase

您必須將 Firebase 初始化程式碼加進應用程式。匯入 Firebase 模組並設定共用執行個體,如下所示:

  1. FirebaseCore 模組匯入 UIApplicationDelegate,以及應用程式委派使用的任何其他 Firebase 模組。例如,如要使用 Cloud Firestore 和驗證:

    SwiftUI

    import SwiftUI
    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    Swift

    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    Objective-C

    @import FirebaseCore;
    @import FirebaseFirestore;
    @import FirebaseAuth;
    // ...
          
  2. 在應用程式委派的 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];
  3. 如果您使用 SwiftUI,則必須建立應用程式委派,並透過 UIApplicationDelegateAdaptorNSApplicationDelegateAdaptor 將其附加至 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 以及 TopicBTopicC 的裝置:

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

FCM 會先評估括號中的任何條件,然後由左至右評估運算式。在上述運算式中,訂閱任何單一主題的使用者不會收到訊息。同樣地,未訂閱 TopicA 的使用者也不會收到訊息。這些組合可以接收:

  • TopicATopicB
  • TopicATopicC

條件運算式最多可加入五個主題。

如要傳送至條件:

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

後續步驟