ส่งข้อความไปยังอุปกรณ์หลายเครื่องบนแพลตฟอร์ม Apple

หากต้องการกำหนดเป้าหมายข้อความไปยังอุปกรณ์หลายเครื่อง ให้ใช้การรับส่งข้อความตามหัวข้อ ฟีเจอร์นี้ช่วยให้คุณส่งข้อความไปยังอุปกรณ์หลายเครื่องที่เลือกใช้หัวข้อหนึ่งๆ ได้

บทแนะนำนี้มุ่งเน้นที่การส่งข้อความหัวข้อจากเซิร์ฟเวอร์แอปโดยใช้ Admin SDK หรือ REST API สำหรับ FCM รวมถึงการรับและจัดการข้อความเหล่านั้นในแอป Apple หน้านี้จะแสดงขั้นตอนทั้งหมดตั้งแต่การตั้งค่าไปจนถึงการยืนยัน ดังนั้นจึงอาจครอบคลุมขั้นตอนที่คุณได้ทำไปแล้วหากคุณได้ตั้งค่าแอปไคลเอ็นต์ Apple สำหรับ FCM หรือทำตามขั้นตอนเพื่อส่งข้อความแรกแล้ว

เพิ่ม Firebase ไปยังโปรเจ็กต์ Apple

ส่วนนี้จะกล่าวถึงงานที่คุณอาจทําเสร็จแล้วหากเปิดใช้ฟีเจอร์อื่นๆ ของ Firebase สําหรับแอปแล้ว สําหรับ FCM โดยเฉพาะ คุณจะต้องอัปโหลดคีย์การตรวจสอบสิทธิ์ APNs และลงทะเบียนเพื่อรับการแจ้งเตือนจากระยะไกล

ข้อกำหนดเบื้องต้น

  • ติดตั้งรายการต่อไปนี้

    • Xcode 15.2 ขึ้นไป
  • ตรวจสอบว่าโปรเจ็กต์เป็นไปตามข้อกำหนดต่อไปนี้

    • โปรเจ็กต์ต้องกำหนดเป้าหมายเป็นแพลตฟอร์มเวอร์ชันต่อไปนี้หรือเวอร์ชันที่ใหม่กว่า
      • iOS 13
      • macOS 10.15
      • tvOS 13
      • watchOS 7
  • ตั้งค่าอุปกรณ์ Apple จริงเพื่อเรียกใช้แอป และทํางานต่อไปนี้

    • รับคีย์การตรวจสอบสิทธิ์ข้อความ Push ของ Apple สำหรับบัญชีนักพัฒนาแอป Apple
    • เปิดใช้ข้อความ Push ใน XCode ในส่วนแอป > ความสามารถ

หากยังไม่มีโปรเจ็กต์ Xcode และต้องการลองใช้ผลิตภัณฑ์ Firebase เพียงอย่างเดียว คุณสามารถดาวน์โหลดตัวอย่างการเริ่มต้นใช้งานอย่างรวดเร็วของเรา

สร้างโปรเจ็กต์ Firebase

คุณต้องสร้างโปรเจ็กต์ Firebase เพื่อเชื่อมต่อกับแอปก่อนจึงจะเพิ่ม Firebase ในแอป Apple ได้ โปรดไปที่ทําความเข้าใจโปรเจ็กต์ Firebase เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับโปรเจ็กต์ Firebase

ลงทะเบียนแอปกับ Firebase

หากต้องการใช้ Firebase ในแอป Apple คุณต้องลงทะเบียนแอปกับโปรเจ็กต์ Firebase การลงทะเบียนแอปมักเรียกว่า "การเพิ่ม" แอปลงในโปรเจ็กต์

  1. ไปที่คอนโซล Firebase

  2. คลิกไอคอน iOS+ ตรงกลางหน้าภาพรวมโปรเจ็กต์เพื่อเปิดเวิร์กโฟลว์การตั้งค่า

    หากเพิ่มแอปลงในโปรเจ็กต์ Firebase อยู่แล้ว ให้คลิกเพิ่มแอปเพื่อแสดงตัวเลือกแพลตฟอร์ม

  3. ป้อนรหัสกลุ่มของแอปในช่องรหัสกลุ่ม

  4. (ไม่บังคับ) ป้อนข้อมูลอื่นๆ ของแอป ได้แก่ ชื่อเล่นของแอปและ App Store ID

  5. คลิกลงทะเบียนแอป

เพิ่มไฟล์การกําหนดค่า Firebase

  1. คลิกดาวน์โหลด GoogleService-Info.plist เพื่อรับไฟล์การกําหนดค่าแพลตฟอร์ม Apple ของ Firebase (GoogleService-Info.plist)

  2. ย้ายไฟล์การกําหนดค่าไปยังรูทของโปรเจ็กต์ Xcode หากได้รับข้อความแจ้ง ให้เลือกเพิ่มไฟล์กำหนดค่าลงในเป้าหมายทั้งหมด

หากมีรหัสกลุ่มหลายรายการในโปรเจ็กต์ คุณต้องเชื่อมโยงรหัสกลุ่มแต่ละรายการกับแอปที่ลงทะเบียนในคอนโซล Firebase เพื่อให้แต่ละแอปมีไฟล์ GoogleService-Info.plist เป็นของตัวเอง

เพิ่ม Firebase SDK ลงในแอป

ใช้ Swift Package Manager เพื่อติดตั้งและจัดการทรัพยากร Dependency ของ Firebase

  1. เปิดโปรเจ็กต์แอปใน Xcode แล้วไปที่ไฟล์ > เพิ่มแพ็กเกจ
  2. เมื่อได้รับข้อความแจ้ง ให้เพิ่มที่เก็บ Firebase SDK สําหรับแพลตฟอร์ม Apple ดังนี้
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. เลือกคลัง Firebase Cloud Messaging
  5. เพิ่ม Flag -ObjC ลงในส่วน Other Linker Flags ของการตั้งค่าบิลด์เป้าหมาย
  6. เราขอแนะนําให้เปิดใช้ Google Analytics ในโปรเจ็กต์ Firebase และเพิ่ม Firebase SDK สําหรับ Google Analytics ลงในแอปเพื่อให้ได้รับประสบการณ์การใช้งาน Firebase Cloud Messaging ที่ดีที่สุด คุณสามารถเลือกคลังแบบไม่เก็บรวบรวม IDFA หรือแบบเก็บรวบรวม IDFA ก็ได้
  7. เมื่อเสร็จแล้ว Xcode จะเริ่มจับคู่ข้อมูลและดาวน์โหลดทรัพยากร Dependency ในเบื้องหลังโดยอัตโนมัติ

อัปโหลดคีย์การตรวจสอบสิทธิ์ APNs

อัปโหลดคีย์การตรวจสอบสิทธิ์ APNs ไปยัง Firebase หากยังไม่มีคีย์การตรวจสอบสิทธิ์ APNs โปรดสร้างคีย์ในศูนย์สมาชิกนักพัฒนาแอปของ Apple

  1. ในโปรเจ็กต์ในคอนโซล Firebase ให้เลือกไอคอนรูปเฟือง เลือกการตั้งค่าโปรเจ็กต์ แล้วเลือกแท็บการรับส่งข้อความระบบคลาวด์

  2. ในส่วนคีย์การตรวจสอบสิทธิ์ APNs ใต้การกำหนดค่าแอป iOS ให้คลิกปุ่มอัปโหลด

  3. เรียกดูตำแหน่งที่คุณบันทึกคีย์ไว้ เลือกคีย์ แล้วคลิกเปิด เพิ่มรหัสคีย์สำหรับคีย์ (ดูได้ใน ศูนย์สมาชิกนักพัฒนาแอปของ Apple) แล้วคลิกอัปโหลด

เริ่มต้นใช้งาน Firebase ในแอป

คุณจะต้องเพิ่มโค้ดการเริ่มต้น Firebase ลงในแอปพลิเคชัน นําเข้ามอดุล Firebase และกำหนดค่าอินสแตนซ์ที่แชร์ดังที่แสดง

  1. นําเข้าโมดูล 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;
    // ...
          
  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 คุณต้องสร้างผู้รับมอบสิทธิ์แอปพลิเคชันและแนบไปกับ App struct ผ่าน UIApplicationDelegateAdaptor หรือ NSApplicationDelegateAdaptor นอกจากนี้ คุณยังต้องปิดใช้การสลับผู้รับมอบสิทธิ์แอปด้วย ดูข้อมูลเพิ่มเติมได้ที่วิธีการ 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 ให้ตรวจสอบว่าอินสแตนซ์แอปไคลเอ็นต์ได้รับโทเค็นการลงทะเบียนผ่าน Callback 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);
}

สร้างคำขอส่ง

หลังจากสร้างหัวข้อแล้ว คุณสามารถส่งข้อความไปยังหัวข้อได้ด้วยการสมัครใช้บริการอินสแตนซ์แอปไคลเอ็นต์เพื่อรับหัวข้อที่ฝั่งไคลเอ็นต์หรือผ่าน server 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

คุณใส่หัวข้อในนิพจน์เงื่อนไขได้สูงสุด 5 หัวข้อ

หากต้องการส่งไปยังเงื่อนไข

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

ขั้นตอนถัดไป