يتم التعامل مع الرسائل الواردة بشكل مختلف حسب حالة الجهاز. لفهم هذه السيناريوهات وكيفية دمج FCM في تطبيقك، من المهم أولاً تحديد الحالات المختلفة التي يمكن أن يكون فيها الجهاز:
الولاية | الوصف |
---|---|
العناصر الأمامية | عندما يكون التطبيق مفتوحًا ومرئيًا وقيد الاستخدام |
الخلفية | عندما يكون التطبيق مفتوحًا ولكن في الخلفية (مُصغّرًا) يحدث ذلك عادةً عندما يضغط المستخدم على زر "الشاشة الرئيسية" على الجهاز، أو عندما ينتقل إلى تطبيق آخر باستخدام أداة تبديل التطبيقات، أو عندما يكون التطبيق مفتوحًا في علامة تبويب مختلفة (على الويب). |
تم إنهاء | عندما يكون الجهاز مقفلًا أو التطبيق غير مفعَّل |
هناك بعض الشروط المسبقة التي يجب استيفاؤها قبل أن يتمكّن التطبيق من تلقّي حِزم بيانات الرسائل من خلال إطار عمل إرسال الرسائل (FCM):
- يجب أن يكون قد تم فتح التطبيق مرة واحدة على الأقل (للسماح بالتسجيل باستخدام "نظام إرسال الرسائل من Google").
- على نظام التشغيل iOS، إذا مرّر المستخدم التطبيق سريعًا من مبدِّل التطبيقات، يجب إعادة فتحه يدويًا لبدء عمل الرسائل في الخلفية مرة أخرى.
- على أجهزة Android، إذا أوقف المستخدم التطبيق عن العمل من إعدادات الجهاز، يجب إعادة فتحه يدويًا لبدء عمل الرسائل.
- على الويب، يجب أن تكون قد طلبت رمزًا مميّزًا (باستخدام
getToken()
) باستخدام شهادة الإشعارات الفورية على الويب.
طلب إذن لتلقّي الرسائل
على نظام التشغيل iOS وmacOS والويب وAndroid 13 (أو الإصدارات الأحدث)، قبل أن تتمكّن من تلقّي حِزم بيانات FCM على جهازك، عليك أولاً طلب إذن المستخدم.
توفّر حزمة firebase_messaging
واجهة برمجة تطبيقات بسيطة لطلب الإذن من خلال طريقة requestPermission
.
تقبل هذه الواجهة عددًا من الوسيطات المُسمّاة التي تحدِّد نوع الأذونات التي تريد طلبها، مثل ما إذا كان
يمكن للرسائل التي تحتوي على بيانات أساسية للإشعارات تشغيل صوت أو قراءة الرسائل من خلال Siri. بشكلٍ تلقائي،تطلب الوسيطة أذونات تلقائية معقولة. توفّر واجهة برمجة التطبيقات المرجعية مستندات كاملة حول الغرض من كل إذن.
للبدء، يمكنك استدعاء الطريقة من تطبيقك (سيتم عرض نافذة مشروطة أصلية على نظام التشغيل iOS، وعلى الويب، سيتم بدء مسار واجهة برمجة التطبيقات الأصلية للمتصفّح):
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
print('User granted permission: ${settings.authorizationStatus}');
يمكن استخدام السمة authorizationStatus
لعنصر NotificationSettings
الذي تم إرجاعه من
الطلب لتحديد القرار العام للمستخدِم:
authorized
: منح المستخدم الإذن.-
denied
: رفض المستخدم الإذن. -
notDetermined
: لم يختار المستخدم بعد ما إذا كان سيمنح الإذن. -
provisional
: منح المستخدم إذنًا مؤقتًا
تعرض السمات الأخرى في NotificationSettings
ما إذا كان إذن معيّن مفعّلاً أو غير مفعّل أو غير متوافق على
الجهاز الحالي.
بعد منح الإذن وفهم الأنواع المختلفة لحالة الجهاز، يمكن لتطبيقك الآن بدء معالجة حمولات FCM الواردة.
معالجة الرسائل
استنادًا إلى الحالة الحالية لتطبيقك، تتطلّب الحمولات الواردة من مختلف أنواع الرسائل عمليات تنفيذ مختلفة للتعامل معها:
الرسائل التي تعمل في المقدّمة
لمعالجة الرسائل عندما يكون تطبيقك في المقدّمة، استمع إلى بث onMessage
.
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message whilst in the foreground!');
print('Message data: ${message.data}');
if (message.notification != null) {
print('Message also contained a notification: ${message.notification}');
}
});
يحتوي البث على RemoteMessage
يوضّح
معلومات مختلفة عن الحمولة، مثل مصدرها والمعرّف الفريد والوقت الذي تم إرسالها فيه وما إذا كانت تحتوي على
إشعار وغير ذلك. بما أنّه تم استرداد الرسالة عندما كان تطبيقك في المقدّمة، يمكنك الوصول مباشرةً إلى حالة تطبيق Flutter
وسياقه.
رسائل المقدّمة والإشعارات
رسائل الإشعارات التي تصل عندما يكون التطبيق في المقدّمة لن تعرِض إشعارًا مرئيًا تلقائيًا على كلٍّ من Android وiOS. ومع ذلك، من الممكن إلغاء هذا السلوك:
- على جهاز Android، عليك إنشاء قناة إشعارات "ذات الأولوية القصوى".
- على أجهزة iOS، يمكنك تعديل خيارات العرض التقديمي للتطبيق.
الرسائل التي تعمل في الخلفية
تختلف عملية معالجة الرسائل في الخلفية على المنصات المتوافقة مع الأجهزة الجوّالة (Android و Apple) والمنصّات المستندة إلى الويب.
أنظمة التشغيل Apple وAndroid
معالجة الرسائل التي تعمل في الخلفية من خلال تسجيل معالِج onBackgroundMessage
عند استلام الرسائل، يتم إنشاء مثيل
معزول (لنظام التشغيل Android فقط، ولا يتطلب iOS/macOS مثيلًا معزولًا منفصلاً)، ما يتيح لك معالجة الرسائل حتى عندما لا يكون تطبيقك قيد التشغيل.
في ما يلي بعض النقاط التي يجب أخذها في الاعتبار بشأن معالِج الرسائل في الخلفية:
- يجب ألا تكون دالة مجهولة الهوية.
- يجب أن تكون دالة من المستوى الأعلى (على سبيل المثال، لا تكون طريقة فئة تتطلّب الإعداد).
- عند استخدام الإصدار 3.3.0 من Flutter أو إصدار أحدث، يجب إضافة تعليق توضيحي إلى معالِج الرسائل باستخدام الرمز
@pragma('vm:entry-point')
فوق بيان الدالة مباشرةً (وإلا قد تتم إزالته أثناء عملية إزالة الرمز غير الضروري في وضع الإصدار).
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
وبما أنّ معالِج الأحداث يتم تشغيله في مساحة معزولة خاصة به خارج سياق تطبيقاتك، لا يمكن تعديل حالة التطبيق أو تنفيذ أي منطق يؤثر في واجهة المستخدم. ومع ذلك، يمكنك تنفيذ عمليات منطقية، مثل طلبات HTTP، وتنفيذ عمليات I/O (مثل تعديل مساحة التخزين المحلية)، والتواصل مع الإضافات الأخرى، وما إلى ذلك.
ننصحك أيضًا بإكمال منطقك في أقرب وقت ممكن. يؤثّر تنفيذ المهام الطويلة والمكثفة في أداء الجهاز وقد يؤدي إلى إنهاء عملية النظام. إذا استمر تنفيذ المهام لأكثر من 30 ثانية، قد يوقف الجهاز العملية تلقائيًا.
الويب
على الويب، اكتب مشغّل خدمات JavaScript يتم تشغيله في الخلفية. استخدِم الخدمة العاملة لمعالجة الرسائل في الخلفية.
للبدء، أنشئ ملفًا جديدًا في دليل web
، واسمه firebase-messaging-sw.js
:
// Please see this file for the latest firebase-js-sdk version:
// https://github.com/firebase/flutterfire/blob/master/packages/firebase_core/firebase_core_web/lib/src/firebase_sdk_version.dart
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-messaging-compat.js");
firebase.initializeApp({
apiKey: "...",
authDomain: "...",
databaseURL: "...",
projectId: "...",
storageBucket: "...",
messagingSenderId: "...",
appId: "...",
});
const messaging = firebase.messaging();
// Optional:
messaging.onBackgroundMessage((message) => {
console.log("onBackgroundMessage", message);
});
يجب أن يستورد الملف كلّ من حِزم تطوير البرامج (SDK) للتطبيق والمراسلة، وأن يُنشئ Firebase ويُعرِض المتغيّر messaging
.
بعد ذلك، يجب تسجيل العامل. في ملف index.html
، سجِّل عامل التشغيل من خلال تعديل علامة <script>
التي تنشئ Flutter:
<script src="flutter_bootstrap.js" async>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('firebase-messaging-sw.js', {
scope: '/firebase-cloud-messaging-push-scope',
});
});
}
</script>
إذا كنت لا تزال تستخدم نظام النماذج القديم، يمكنك تسجيل العامل من خلال تعديل علامة <script>
التي تنشئ Flutter على النحو التالي:
<html>
<body>
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Register Firebase Messaging service worker.
navigator.serviceWorker.register('firebase-messaging-sw.js', {
scope: '/firebase-cloud-messaging-push-scope',
});
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl =
'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl).then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing ?? reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.'
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
</body>
بعد ذلك، أعِد تشغيل تطبيق Flutter. سيتم تسجيل العامل وسيتم التعامل مع أي رسائل في الخلفية من خلال هذا الملف.
التعامل مع التفاعل
وبما أنّ الإشعارات هي إشارات مرئية، من الشائع أن يتفاعل المستخدمون معها (من خلال الضغط عليها). السلوك التلقائي على كلٍّ من Android وiOS هو فتح التطبيق. إذا تم إنهاء التطبيق، سيتم تشغيله، وإذا كان في الخلفية، سيتم إظهاره في المقدّمة.
استنادًا إلى محتوى الإشعار، قد تحتاج إلى معالجة تفاعل المستخدم عند فتح التطبيق. على سبيل المثال، إذا تم إرسال رسالة محادثة جديدة عبر إشعار وضغط المستخدم عليه، قد تحتاج إلى فتح المحادثة المحدّدة عند فتح التطبيق.
توفّر حزمة firebase-messaging
طريقتَين للتعامل مع هذا التفاعل:
getInitialMessage()
: في حال فتح التطبيق من حالة إنهاء، سيتم عرضFuture
يحتوي علىRemoteMessage
. بعد استخدام القيمة، ستتم إزالتها.RemoteMessage
onMessageOpenedApp
:Stream
ينشرRemoteMessage
عند فتح التطبيق من حالة "التشغيل في الخلفية"
ننصحك بمعالجة كلا السيناريوهَين لضمان تجربة مستخدم سلسة. يوضّح مثال الرمز البرمجي أدناه كيفية تحقيق ذلك:
class Application extends StatefulWidget {
@override
State<StatefulWidget> createState() => _Application();
}
class _Application extends State<Application> {
// It is assumed that all messages contain a data field with the key 'type'
Future<void> setupInteractedMessage() async {
// Get any messages which caused the application to open from
// a terminated state.
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
// If the message also contains a data property with a "type" of "chat",
// navigate to a chat screen
if (initialMessage != null) {
_handleMessage(initialMessage);
}
// Also handle any interaction when the app is in the background via a
// Stream listener
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);
}
void _handleMessage(RemoteMessage message) {
if (message.data['type'] == 'chat') {
Navigator.pushNamed(context, '/chat',
arguments: ChatArguments(message),
);
}
}
@override
void initState() {
super.initState();
// Run code required to handle interacted messages in an async function
// as initState() must not be async
setupInteractedMessage();
}
@override
Widget build(BuildContext context) {
return Text("...");
}
}
تعتمد طريقة معالجة التفاعل على إعدادات تطبيقك. يعرض المثال أعلاه رسمًا توضيحيًا أساسيًا باستخدام عنصر StatefulWidget.
ترجمة تطبيق "الرسائل"
يمكنك إرسال سلاسل النصوص المترجَمة بطريقتَين مختلفتَين:
- تخزين اللغة المفضّلة لكل مستخدم في خادمك وإرسال إشعارات مخصّصة لكل لغة
- تضمين سلاسل نصية مترجَمة في تطبيقك والاستفادة من إعدادات اللغة الأصلية لنظام التشغيل
إليك كيفية استخدام الطريقة الثانية:
Android
حدِّد رسائل اللغة التلقائية في
resources/values/strings.xml
:<string name="notification_title">Hello world</string> <string name="notification_message">This is a message</string>
حدِّد الرسائل المترجمة في دليل
values-language
. على سبيل المثال، حدِّد الرسائل الفرنسية فيresources/values-fr/strings.xml
:<string name="notification_title">Bonjour le monde</string> <string name="notification_message">C'est un message</string>
في الحمولة على الخادم، بدلاً من استخدام المفاتيح
title
وmessage
وbody
، استخدِمtitle_loc_key
وbody_loc_key
لرسالتك المترجَمة، واضبطهما على سمةname
للرسالة التي تريد عرضها.سيبدو حمولة الرسالة على النحو التالي:
{ "data": { "title_loc_key": "notification_title", "body_loc_key": "notification_message" } }
iOS
حدِّد رسائل اللغة التلقائية في
Base.lproj/Localizable.strings
:"NOTIFICATION_TITLE" = "Hello World"; "NOTIFICATION_MESSAGE" = "This is a message";
حدِّد الرسائل المترجمة في دليل
language.lproj
. على سبيل المثال، حدِّد الرسائل الفرنسية فيfr.lproj/Localizable.strings
:"NOTIFICATION_TITLE" = "Bonjour le monde"; "NOTIFICATION_MESSAGE" = "C'est un message";
سيبدو حمولة الرسالة على النحو التالي:
{ "data": { "title_loc_key": "NOTIFICATION_TITLE", "body_loc_key": "NOTIFICATION_MESSAGE" } }
تفعيل تصدير بيانات تسليم الرسائل
يمكنك تصدير بيانات الرسائل إلى BigQuery لإجراء مزيد من التحليلات. تتيح لك أداة BigQuery تحليل البيانات باستخدام لغة الاستعلامات البنيوية (SQL) في BigQuery، أو تصديرها إلى مقدّم خدمة آخر من خدمات السحابة الإلكترونية، أو استخدام البيانات في نماذج الذكاء الاصطناعي المخصّصة. يتضمّن التصدير إلى BigQuery جميع البيانات المتاحة للرسائل، بغض النظر عن نوع الرسالة أو ما إذا تم إرسال الرسالة من خلال واجهة برمجة التطبيقات أو أداة إنشاء الإشعارات.
لتفعيل التصدير، اتّبِع أولاً الخطوات الموضّحة هنا، ثم اتّبِع التعليمات التالية:
Android
يمكنك استخدام الرمز البرمجي التالي:
await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
iOS
بالنسبة إلى أجهزة iOS، عليك تغيير AppDelegate.m
بالمحتوى التالي.
#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
#import <Firebase/Firebase.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)application:(UIApplication *)application
didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:userInfo];
}
@end
الويب
بالنسبة إلى الويب، عليك تغيير عامل الخدمة لاستخدام الإصدار 9 من حزمة تطوير البرامج (SDK).
يجب تجميع الإصدار 9، لذا عليك استخدام أداة تجميع مثل esbuild
على سبيل المثال
لتشغيل مشغّل الخدمة.
اطّلِع على مثال التطبيق لمعرفة كيفية تحقيق ذلك.
بعد نقل البيانات إلى الإصدار 9 من حزمة تطوير البرامج (SDK)، يمكنك استخدام الرمز التالي:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
لا تنسَ تشغيل yarn build
لتصدير الإصدار الجديد من worker الخدمة إلى مجلد web
.
عرض الصور في الإشعارات على أجهزة iOS
على أجهزة Apple، لكي يتمكّن تطبيقك من عرض الصور الواردة من حمولة إطار عمل Firebase للرسائل في إشعارات FCM، عليك إضافة إضافة إضافية لخدمة الإشعارات وضبط تطبيقك لاستخدامها.
إذا كنت تستخدم مصادقة الهاتف في Firebase، عليك إضافة مجموعة Firebase Auth pod إلى Podfile.
الخطوة 1: إضافة إضافة خدمة إشعارات
- في Xcode، انقر على ملف > جديد > هدف...
- ستعرض نافذة منبثقة قائمة بالأهداف المحتملة، وانتقِل للأسفل أو استخدِم الفلتر لاختيار إضافة خدمة الإشعارات. انقر على التالي.
- أضِف اسم منتج (استخدِم "ImageNotification" لمتابعة هذا الدليل التعليمي)، واضبط اللغة على Objective-C، ثم انقر على إنهاء.
- فعِّل المخطّط بالنقر على تفعيل.
الخطوة 2: إضافة هدف إلى Podfile
تأكَّد من أنّ الإضافة الجديدة يمكنها الوصول إلى مجموعة Firebase/Messaging
من خلال إضافتها إلى Podfile:
من "المستكشف"، افتح Podfile: Pods > Podfile
انتقِل إلى أسفل الملف وأضِف ما يلي:
target 'ImageNotification' do use_frameworks! pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication pod 'Firebase/Messaging' end
ثبِّت وحدات pod أو عدِّلها باستخدام
pod install
من دليلios
أوmacos
.
الخطوة 3: استخدام أداة مساعدة الإضافات
في هذه المرحلة، من المفترض أن يظلّ كل شيء يعمل على النحو المعتاد. الخطوة الأخيرة هي استدعاء مساعد الإضافة.
من المستكشف، اختَر إضافة ImageNotification.
افتح ملف
NotificationService.m
.في أعلى الملف، استورِد
FirebaseMessaging.h
مباشرةً بعدNotificationService.h
كما هو موضّح أدناه.استبدِل محتوى
NotificationService.m
بما يلي:#import "NotificationService.h" #import "FirebaseMessaging.h" #import "FirebaseAuth.h" // Add this line if you are using FirebaseAuth phone authentication #import <UIKit/UIKit.h> // Add this line if you are using FirebaseAuth phone authentication @interface NotificationService () @property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; @end @implementation NotificationService /* Uncomment this if you are using Firebase Auth - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options { if ([[FIRAuth auth] canHandleURL:url]) { return YES; } return NO; } - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts { for (UIOpenURLContext *urlContext in URLContexts) { [FIRAuth.auth canHandleURL:urlContext.URL]; } } */ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; // Modify the notification content here... [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler]; } - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. self.contentHandler(self.bestAttemptContent); } @end
الخطوة 4: إضافة الصورة إلى الحمولة
يمكنك الآن إضافة صورة في الحمولة البرمجية للإشعار. اطّلِع على مستندات iOS حول كيفية إنشاء طلب إرسال. يُرجى العِلم أنّ الجهاز يفرض الحد الأقصى لحجم الصورة وهو 300 كيلوبايت.