إرسال رسائل إلى أجهزة متعددة على أنظمة Apple الأساسية

لتوجيه رسالة إلى أجهزة متعدّدة، استخدِم مراسلة الموضوع. وتتيح لك هذه الميزة إرسال رسالة إلى أجهزة متعددة وافقت على مشاركة موضوع معيّن.

يركّز هذا البرنامج التعليمي على إرسال رسائل بشأن مواضيع من خادم تطبيقك باستخدام SDK للمشرف أو REST API لخدمة "المراسلة عبر السحابة الإلكترونية من Firebase"، واستلامها ومعالجتها في أحد تطبيقات Apple. وتسرد هذه الصفحة كل خطوات تحقيق ذلك، بدءًا من الإعداد ووصولاً إلى إثبات الملكية، لذلك قد يتناول الخطوات التي أكملتها إذا كنت قد أعددت تطبيق عميل Apple لخدمة "المراسلة عبر السحابة الإلكترونية من Firebase" أو تم تنفيذ خطوات إرسال رسالة

إضافة Firebase إلى مشروع Apple

يتناول هذا القسم المهام التي ربما أكملتها إذا سبق لك تفعيل ميزات Firebase الأخرى في تطبيقك. وبالنسبة إلى ميزة "المراسلة عبر السحابة الإلكترونية من Firebase" تحديدًا، عليك تحميل مفتاح مصادقة أسماء نقاط الوصول (APN) والتسجيل لتلقّي الإشعارات عن بُعد.

المتطلّبات الأساسية

  • ثبِّت ما يلي:

    • Xcode 14.1 أو إصدار أحدث
  • تأكد من أن مشروعك يلبي هذه المتطلبات:

    • يجب أن يستهدف مشروعك إصدارات النظام الأساسي التالية أو الإصدارات الأحدث:
      • الإصدار 11 من نظام التشغيل iOS
      • الإصدار 10.13 من نظام التشغيل macOS
      • نظام التشغيل tvOS 12
      • ساعة تعمل بنظام التشغيل WatchOS 6
  • يمكنك إعداد جهاز Apple مادي لتشغيل تطبيقك، وإكمال المهام التالية:

    • احصل على مفتاح مصادقة الإشعارات الفورية من Apple لحساب مطوِّر برامج Apple.
    • فعِّل الإشعارات الفورية في XCode ضمن التطبيق > الإمكانات.

إذا لم يكن لديك مشروع Xcode من قبل وتريد فقط تجربة أحد منتجات Firebase، يمكنك تنزيل أحد نماذج البدء السريع.

إنشاء مشروع على Firebase

قبل أن تتمكّن من إضافة Firebase إلى تطبيق Apple، يجب إنشاء مشروع في Firebase لربطه بتطبيقك. انتقِل إلى فهم مشاريع Firebase للاطّلاع على مزيد من المعلومات حول مشاريع Firebase.

تسجيل تطبيقك في Firebase

لاستخدام Firebase في تطبيق Apple، يجب تسجيل تطبيقك في مشروع Firebase. غالبًا ما يطلق على تسجيل تطبيقك "إضافة" التطبيق إلى مشروعك.

  1. انتقِل إلى وحدة تحكُّم Firebase.

  2. في منتصف صفحة "نظرة عامة على المشروع"، انقر على الرمز +iOS لبدء عملية الإعداد.

    إذا سبق وأضفت تطبيقًا إلى مشروع Firebase، انقر على إضافة تطبيق لعرض خيارات النظام الأساسي.

  3. أدخِل معرّف حزمة التطبيق في الحقل معرّف الحزمة.

  4. (اختياري) أدخِل معلومات التطبيق الأخرى: اسم التطبيق ورقم تعريف متجر التطبيقات.

  5. انقر على تسجيل التطبيق.

إضافة ملف إعداد Firebase

  1. انقر على تنزيل GoogleService-Info.plist للحصول على ملف إعداد Firebase Apple (GoogleService-Info.plist).

  2. انقل ملف التكوين إلى جذر مشروع Xcode. اختَر إضافة ملف الإعداد إلى جميع الاستهدافات إذا طُلب منك ذلك

إذا كان لديك عدّة معرّفات حِزم في مشروعك، عليك ربط كل رقم تعريف بتطبيق مسجَّل في وحدة تحكُّم Firebase كي يكون لكل تطبيق ملف GoogleService-Info.plist خاص به.

إضافة حِزم تطوير البرامج (SDK) لمنصة Firebase إلى تطبيقك

يمكنك استخدام "مدير حزم Swift" لتثبيت اعتماديات Firebase وإدارتها.

  1. في Xcode، بعد فتح مشروع تطبيقك، انتقِل إلى File > Add Packages (ملف > إضافة حِزم).
  2. أضِف مستودع حزمة تطوير البرامج (SDK) لمنصّات Firebase Apple عندما يُطلب منك ذلك:
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. اختَر مكتبة "المراسلة عبر السحابة الإلكترونية من Firebase".
  5. أضِف العلامة -ObjC إلى القسم علامات الروابط الأخرى في إعدادات إصدار هدفك.
  6. للحصول على أفضل تجربة في "المراسلة عبر السحابة الإلكترونية من Firebase"، نقترح عليك تفعيل "إحصاءات Google" في مشروعك على Firebase وإضافة حزمة تطوير البرامج (SDK) لمنصّة Firebase الخاصّة بخدمة "إحصاءات Google" إلى تطبيقك. يمكنك اختيار المكتبة التي لا تتطلّب جمع معرّف المعلِنين (IDFA) أو من خلال جمع معرّف المعلِنين (IDFA).
  7. عند الانتهاء، ستبدأ خدمة Xcode تلقائيًا في حلّ المشاكل المتعلّقة بالعناصر التي تعتمد عليها وتنزيلها في الخلفية.

تحميل مفتاح مصادقة أسماء نقاط الوصول (APN)

حمِّل مفتاح مصادقة أسماء نقاط الوصول إلى Firebase. إذا لم يكن لديك مفتاح مصادقة لأسماء نقاط الوصول (APN)، احرص على إنشاء مفتاح في مركز أعضاء المطوّرين في Apple.

  1. من داخل مشروعك في وحدة تحكُّم Firebase، اختَر رمز الترس، ثمّ اختَر إعدادات المشروع (Project Settings)، ثم اختَر علامة التبويب المراسلة عبر السحابة الإلكترونية.

  2. في مفتاح مصادقة أسماء نقاط الوصول (APN) ضمن ضبط تطبيق iOS، انقر على الزر تحميل.

  3. انتقِل إلى الموقع الجغرافي الذي حفظت فيه المفتاح واختَره وانقر على فتح. أضِف معرّف المفتاح (المتوفّر في Apple Developer Member Center) وانقر على Upload (تحميل).

إعداد Firebase في تطبيقك

سيتعيّن عليك إضافة رمز إعداد Firebase إلى تطبيقك. استورد وحدة Firebase وقم بتهيئة مثيل مشترك على النحو الموضح:

  1. يمكنك استيراد وحدة FirebaseCore في UIApplicationDelegate، بالإضافة إلى أي وحدات Firebase أخرى يستخدمها المستخدم المفوَّض في تطبيقك. على سبيل المثال، لاستخدام Cloud Firestore والمصادقة:

    واجهة المستخدم

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

    Swift

    import FirebaseCore
    import FirebaseFirestore
    import FirebaseAuth
    // ...
          

    Objective-C

    @import FirebaseCore;
    @import FirebaseFirestore;
    @import FirebaseAuth;
    // ...
          
  2. يمكنك ضبط المثيل المشترك ضمن FirebaseApp في طريقة application(_:didFinishLaunchingWithOptions:) الخاصة بمفوَّض تطبيقك:

    واجهة المستخدم

    // 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 من خلال UIApplicationDelegateAdaptor أو NSApplicationDelegateAdaptor. ويجب أيضًا إيقاف إيماءة تفويض التطبيق. لمزيد من المعلومات، راجِع تعليمات 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)، يتم إنشاء موضوع جديد بهذا الاسم في خدمة "المراسلة عبر السحابة الإلكترونية من Firebase"، ويمكن لأي عميل الاشتراك فيه في وقت لاحق.

للاشتراك في موضوع، يمكنك طلب طريقة الاشتراك من سلسلة المحادثات الرئيسية لتطبيقك ("المراسلة عبر السحابة الإلكترونية من Firebase" غير ملائمة لسلاسل المحادثات). وإذا تعذّر طلب الاشتراك في البداية، ستحاول خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" تلقائيًا المحاولة. بالنسبة إلى الحالات التي يتعذّر فيها إكمال الاشتراك، يعرض الاشتراك خطأً يمكنك رصده في معالج الإكمال كما هو موضَّح:

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

تُجري هذه الاستدعاء طلبًا غير متزامن إلى الواجهة الخلفية للمراسلة عبر السحابة الإلكترونية من Firebase ويشترك العميل في الموضوع المحدد. قبل طلب subscribeToTopic:topic، تأكّد من أنّ مثيل تطبيق العميل قد تلقّى رمزًا مميّزًا للتسجيل من خلال معاودة الاتصال didReceiveRegistrationToken.

في كل مرة يبدأ فيها التطبيق، تتأكد خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" من أن جميع المواضيع المطلوبة قد تم الاشتراك فيها. لإلغاء الاشتراك، يمكنك الاتصال بالرقم unsubscribeFromTopic:topic وإلغاء الاشتراك من خلال خدمة "المراسلة عبر السحابة الإلكترونية من Firebase" في الخلفية.

تلقّي رسائل المواضيع والتعامل معها

تسلِّم ميزة "المراسلة عبر السحابة الإلكترونية من Firebase" رسائل المواضيع بالطريقة نفسها التي ترسِل بها الرسائل الأخرى في مرحلة متأخرة من الوقت.

نفِّذ 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);
}

إنشاء طلبات الإرسال

بعد إنشاء موضوع، يمكنك إرسال رسائل إلى الموضوع من خلال اشتراك مثيلات تطبيق العميل في الموضوع من جهة العميل أو عبر واجهة برمجة تطبيقات الخادم. إذا كانت هذه هي المرة الأولى التي تنشئ فيها طلبات لإرسال طلبات إلى "المراسلة عبر السحابة الإلكترونية من Firebase"، يمكنك الاطّلاع على دليل بيئة الخادم والمراسلة عبر السحابة الإلكترونية من Firebase للحصول على معلومات مهمة حول الخلفية والإعداد.

في منطق الإرسال في الخلفية، حدد اسم الموضوع المطلوب كما هو موضح:

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

لإرسال رسالة إلى مجموعة من المواضيع، حدِّد condition، وهو تعبير منطقي يحدد المواضيع المستهدفة. على سبيل المثال، سيؤدي الشرط التالي إلى إرسال رسائل إلى الأجهزة المشتركة في 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

الخطوات اللاحقة