Gửi tin nhắn đến nhiều thiết bị trên các nền tảng của Apple

Để nhắm đến nhiều thiết bị bằng một thông báo, hãy sử dụng tính năng Nhắn tin theo chủ đề. Tính năng này cho phép bạn gửi một thông báo đến nhiều thiết bị đã chọn nhận thông báo về một chủ đề cụ thể.

Hướng dẫn này tập trung vào việc gửi thông báo theo chủ đề từ máy chủ ứng dụng bằng cách sử dụng Admin SDK hoặc REST API cho FCM, cũng như nhận và xử lý các thông báo đó trong một ứng dụng Apple. Trang này liệt kê tất cả các bước để đạt được mục tiêu này, từ bước thiết lập đến bước xác minh. Vì vậy, có thể trang này sẽ đề cập đến những bước mà bạn đã hoàn tất nếu bạn đã thiết lập một ứng dụng khách Apple cho FCM hoặc đã thực hiện các bước để Gửi thông báo đầu tiên.

Thêm Firebase vào dự án Apple

Phần này đề cập đến những việc bạn có thể đã hoàn thành nếu đã bật các tính năng khác của Firebase cho ứng dụng. Cụ thể đối với FCM, bạn cần tải khoá xác thực APNs lênđăng ký nhận thông báo từ xa.

Điều kiện tiên quyết

  • Cài đặt các mục sau:

    • Xcode 16.2 trở lên
  • Đảm bảo rằng dự án của bạn đáp ứng các yêu cầu sau:

    • Dự án của bạn phải nhắm đến các phiên bản nền tảng này trở lên:
      • iOS 13
      • macOS 10.15
      • tvOS 13
      • watchOS 7
  • Thiết lập một thiết bị thực của Apple để chạy ứng dụng và hoàn tất các việc sau:

    • Lấy Khoá xác thực thông báo đẩy của Apple cho tài khoản Apple Developer của bạn.
    • Bật thông báo đẩy trong XCode trong phần App > Capabilities (Ứng dụng > Chức năng).

Nếu chưa có dự án Xcode và chỉ muốn dùng thử một sản phẩm của Firebase, bạn có thể tải một trong các mẫu khởi động nhanh của chúng tôi xuống.

Tạo một dự án Firebase

Trước khi có thể thêm Firebase vào ứng dụng Apple, bạn cần tạo một dự án Firebase để kết nối với ứng dụng của mình. Hãy truy cập vào trang Tìm hiểu về các dự án Firebase để tìm hiểu thêm về các dự án Firebase.

Đăng ký ứng dụng của bạn với Firebase

Để sử dụng Firebase trong ứng dụng Apple, bạn cần đăng ký ứng dụng với dự án Firebase. Việc đăng ký ứng dụng thường được gọi là "thêm" ứng dụng vào dự án.

  1. Chuyển đến bảng điều khiển Firebase.

  2. Ở giữa trang tổng quan của dự án, hãy nhấp vào biểu tượng iOS+ để chạy quy trình thiết lập.

    Nếu bạn đã thêm một ứng dụng vào dự án Firebase, hãy nhấp vào Thêm ứng dụng để hiển thị các lựa chọn về nền tảng.

  3. Nhập mã nhận dạng gói của ứng dụng vào trường mã nhận dạng gói.

  4. (Không bắt buộc) Nhập thông tin khác về ứng dụng: Biệt hiệu của ứng dụngMã App Store.

  5. Nhấp vào Đăng ký ứng dụng.

Thêm tệp cấu hình Firebase

  1. Nhấp vào Download GoogleService-Info.plist (Tải GoogleService-Info.plist xuống) để lấy tệp cấu hình Firebase của ứng dụng (GoogleService-Info.plist).

  2. Di chuyển tệp cấu hình vào thư mục gốc của dự án Xcode. Nếu được nhắc, hãy chọn thêm tệp cấu hình vào tất cả các mục tiêu.

Nếu có nhiều mã nhận dạng gói trong dự án, bạn phải liên kết từng mã nhận dạng gói với một ứng dụng đã đăng ký trong bảng điều khiển Firebase để mỗi ứng dụng có thể có tệp GoogleService-Info.plist riêng.

Thêm SDK Firebase vào ứng dụng của bạn

Sử dụng Swift Package Manager để cài đặt và quản lý các phần phụ thuộc của Firebase.

  1. Trong Xcode, khi dự án ứng dụng của bạn đang mở, hãy chuyển đến File > Add Packages (Tệp > Thêm gói).
  2. Khi được nhắc, hãy thêm kho lưu trữ SDK Firebase cho các nền tảng của Apple:
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. Chọn thư viện Firebase Cloud Messaging.
  5. Thêm cờ -ObjC vào mục Cờ trình liên kết khác trong chế độ cài đặt bản dựng của mục tiêu.
  6. Để có trải nghiệm tối ưu với Firebase Cloud Messaging, bạn nên bật Google Analytics trong dự án Firebase và thêm Firebase SDK cho Google Analytics vào ứng dụng của mình. Bạn có thể chọn thư viện không thu thập IDFA hoặc có thu thập IDFA. Hãy xem phần Câu hỏi thường gặp của chúng tôi về cách sắp xếp mới nhất của các mô-đun trong Google Analytics cho SDK Firebase.
  7. Khi hoàn tất, Xcode sẽ tự động bắt đầu phân giải và tải các phần phụ thuộc của bạn xuống ở chế độ nền.

Tải khoá xác thực APNs lên

Tải khoá xác thực APNs lên Firebase. Nếu bạn chưa có khoá xác thực APNs, hãy nhớ tạo một khoá trong Trung tâm thành viên của nhà phát triển Apple.

  1. Trong dự án của bạn trên bảng điều khiển Firebase, hãy chọn biểu tượng bánh răng, chọn Project Settings (Cài đặt dự án), rồi chọn thẻ Cloud Messaging (Nhắn tin qua đám mây).

  2. Trong phần Khoá xác thực APNs trong Cấu hình ứng dụng iOS, hãy nhấp vào nút Tải lên.

  3. Duyệt đến vị trí mà bạn đã lưu khoá, chọn khoá đó rồi nhấp vào Mở. Thêm mã khoá cho khoá (có trong Trung tâm thành viên của nhà phát triển Apple) rồi nhấp vào Tải lên.

Khởi động Firebase trong ứng dụng của bạn

Bạn cần thêm mã khởi chạy Firebase vào ứng dụng của mình. Nhập mô-đun Firebase và định cấu hình một thực thể dùng chung như minh hoạ:

  1. Nhập mô-đun FirebaseCore trong UIApplicationDelegate, cũng như mọi mô-đun Firebase khác mà uỷ quyền ứng dụng của bạn sử dụng. Ví dụ: để sử dụng Cloud FirestoreAuthentication:

    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. Định cấu hình một phiên bản dùng chung FirebaseApp trong phương thức application(_:didFinishLaunchingWithOptions:) của uỷ quyền ứng dụng:

    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. Nếu đang sử dụng SwiftUI, bạn phải tạo một uỷ quyền ứng dụng và đính kèm uỷ quyền đó vào cấu trúc App thông qua UIApplicationDelegateAdaptor hoặc NSApplicationDelegateAdaptor. Bạn cũng phải tắt tính năng thay thế phương thức uỷ quyền của ứng dụng. Để biết thêm thông tin, hãy xem hướng dẫn về SwiftUI.

    SwiftUI

    @main
    struct YourApp: App {
      // register app delegate for Firebase setup
      @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    
      var body: some Scene {
        WindowGroup {
          NavigationView {
            ContentView()
          }
        }
      }
    }
          

Đăng ký nhận thông báo từ xa

Khi khởi động hoặc tại điểm mong muốn trong quy trình ứng dụng, hãy đăng ký ứng dụng của bạn để nhận thông báo từ xa. Gọi registerForRemoteNotifications như minh hoạ:

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

Đăng ký ứng dụng khách vào một chủ đề

Ứng dụng khách có thể đăng ký theo dõi bất kỳ chủ đề nào hiện có hoặc có thể tạo một chủ đề mới. Khi một ứng dụng khách đăng ký một tên chủ đề mới (tên chưa tồn tại cho dự án Firebase của bạn), một chủ đề mới có tên đó sẽ được tạo trong FCM và mọi ứng dụng khách đều có thể đăng ký chủ đề đó sau này.

Để đăng ký theo dõi một chủ đề, hãy gọi phương thức đăng ký từ luồng chính của ứng dụng (FCM không an toàn cho luồng). Nếu yêu cầu đăng ký ban đầu không thành công, FCM sẽ tự động thử lại. Đối với những trường hợp không thể hoàn tất quy trình đăng ký, quy trình đăng ký sẽ gửi một lỗi mà bạn có thể bắt trong trình xử lý hoàn tất như minh hoạ:

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");
}];

Lệnh gọi này đưa ra một yêu cầu không đồng bộ đến phần phụ trợ FCM và đăng ký ứng dụng khách vào chủ đề đã cho. Trước khi gọi subscribeToTopic:topic, hãy đảm bảo rằng phiên bản ứng dụng khách đã nhận được mã đăng ký thông qua lệnh gọi lại didReceiveRegistrationToken.

Mỗi khi ứng dụng khởi động, FCM sẽ đảm bảo rằng bạn đã đăng ký tất cả các chủ đề được yêu cầu. Để huỷ đăng ký, hãy gọi unsubscribeFromTopic:topicFCM sẽ huỷ đăng ký theo dõi chủ đề trong nền.

Nhận và xử lý thông báo theo chủ đề

FCM gửi thông báo theo chủ đề theo cách tương tự như các thông báo khác ở hạ lưu.

Triển khai application(_:didReceiveRemoteNotification:fetchCompletionHandler:) như sau:

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);
}

Tạo yêu cầu gửi

Sau khi tạo một chủ đề, bằng cách đăng ký các phiên bản ứng dụng khách vào chủ đề ở phía ứng dụng khách hoặc thông qua API máy chủ, bạn có thể gửi thông báo đến chủ đề đó. Nếu đây là lần đầu tiên bạn tạo yêu cầu gửi cho FCM, hãy xem hướng dẫn về môi trường máy chủ và FCM để biết thông tin quan trọng về bối cảnh và cách thiết lập.

Trong logic gửi ở phần phụ trợ, hãy chỉ định tên chủ đề mong muốn như minh hoạ:

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)

Tìm

// 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);

Kiến trúc chuyển trạng thái đại diện (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"
      }
   }
}

Lệnh 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

Để gửi thông báo đến một tổ hợp các chủ đề, hãy chỉ định một điều kiện. Đây là một biểu thức boolean chỉ định các chủ đề mục tiêu. Ví dụ: điều kiện sau đây sẽ gửi thông báo đến những thiết bị đã đăng ký TopicATopicB hoặc TopicC:

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

FCM trước tiên sẽ đánh giá mọi điều kiện trong dấu ngoặc đơn, sau đó đánh giá biểu thức từ trái sang phải. Trong biểu thức trên, người dùng đăng ký một chủ đề bất kỳ sẽ không nhận được thông báo. Tương tự, người dùng không đăng ký TopicA sẽ không nhận được thông báo. Các tổ hợp này sẽ nhận được:

  • TopicATopicB
  • TopicATopicC

Bạn có thể thêm tối đa 5 chủ đề vào biểu thức có điều kiện.

Để gửi đến một điều kiện:

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)

Tìm

// 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);

Kiến trúc chuyển trạng thái đại diện (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",
    }
  }
}

Lệnh 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

Các bước tiếp theo