بسته به وضعیت دستگاه، پیامهای دریافتی به گونهای متفاوت مدیریت میشوند. برای درک این سناریوها و نحوه ادغام FCM در برنامه خود، ابتدا مهم است که وضعیتهای مختلفی را که یک دستگاه میتواند در آن قرار گیرد مشخص کنید:
ایالت | توضیحات |
---|---|
پیش زمینه | وقتی برنامه باز است، در حال مشاهده و در حال استفاده است. |
پس زمینه | هنگامی که برنامه باز است، اما در پس زمینه (به حداقل می رسد). این معمولاً زمانی اتفاق میافتد که کاربر دکمه «خانه» را روی دستگاه فشار داده باشد، با استفاده از تعویضکننده برنامه به برنامه دیگری تغییر مکان داده باشد، یا برنامه در یک برگه دیگر (وب) باز شود. |
خاتمه یافت | وقتی دستگاه قفل است یا برنامه اجرا نمی شود. |
قبل از اینکه برنامه بتواند محموله های پیام را از طریق FCM دریافت کند، چند پیش شرط وجود دارد:
- برنامه باید حداقل یک بار باز شده باشد (برای ثبت نام در FCM).
- در iOS، اگر کاربر برنامه را از سوئیچر برنامه دور کند، باید به صورت دستی باز شود تا پیامهای پسزمینه دوباره شروع به کار کنند.
- در اندروید، اگر کاربر برنامه را به اجبار از تنظیمات دستگاه خارج کند، باید به صورت دستی باز شود تا پیام ها شروع به کار کنند.
- در وب، باید یک توکن (با استفاده از
getToken()
) همراه با گواهی فشار وب خود درخواست کرده باشید.
درخواست مجوز برای دریافت پیام
در iOS، macOS، وب و اندروید 13 (یا جدیدتر)، قبل از دریافت بارهای FCM در دستگاه شما، ابتدا باید از کاربر اجازه بگیرید.
بسته firebase_messaging
یک API ساده برای درخواست مجوز از طریق متد requestPermission
فراهم می کند. این API تعدادی آرگومان نامگذاری شده را میپذیرد که نوع مجوزهایی را که میخواهید درخواست کنید، تعریف میکنند، مانند اینکه آیا پیامهای حاوی بارهای اعلان میتوانند صدا را تحریک کنند یا پیامها را از طریق Siri بخوانند. به طور پیش فرض، متد مجوزهای پیش فرض معقولی را درخواست می کند. API مرجع مستندات کاملی را در مورد اینکه هر مجوز برای چه چیزی است ارائه می دهد.
برای شروع، روش را از برنامه خود فراخوانی کنید (در iOS یک مدال بومی نمایش داده می شود، در وب جریان API بومی مرورگر فعال می شود):
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 خود دسترسی داشته باشید.
پیش زمینه و پیام های اعلان
پیامهای اعلانهایی که در زمانی که برنامه در پیشزمینه است دریافت میشوند، بهطور پیشفرض اعلان قابل مشاهدهای را در اندروید و iOS نشان نمیدهند. با این حال، می توان این رفتار را نادیده گرفت:
- در اندروید، باید یک کانال اعلان با «اولویت بالا» ایجاد کنید.
- در iOS، می توانید گزینه های ارائه برنامه را به روز کنید.
پیام های پس زمینه
فرآیند مدیریت پیام های پس زمینه در پلتفرم های بومی (اندروید و اپل) و مبتنی بر وب متفاوت است.
پلتفرم های اپل و اندروید
با ثبت یک کنترل کننده onBackgroundMessage
پیام های پس زمینه را مدیریت کنید. هنگامی که پیامها دریافت میشوند، یک ایزوله ایجاد میشود (فقط اندروید، iOS/macOS نیازی به جداسازی جداگانه ندارد) که به شما امکان میدهد پیامها را حتی زمانی که برنامه شما اجرا نمیشود مدیریت کنید.
چند نکته را باید در مورد مدیریت پیام پسزمینه خود در نظر داشته باشید:
- نباید یک تابع ناشناس باشد.
- این باید یک تابع سطح بالا باشد (مثلاً یک متد کلاس که نیاز به مقداردهی اولیه دارد).
- هنگام استفاده از Flutter نسخه 3.3.0 یا بالاتر، کنترل کننده پیام باید با
@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());
}
از آنجایی که کنترل کننده در ایزوله خود خارج از زمینه برنامه های کاربردی شما اجرا می شود، امکان به روز رسانی وضعیت برنامه یا اجرای منطق تأثیرگذار UI وجود ندارد. با این حال، میتوانید منطقی مانند درخواستهای HTTP، عملیات IO (مثلاً بهروزرسانی فضای ذخیرهسازی محلی)، برقراری ارتباط با سایر افزونهها و غیره را انجام دهید.
همچنین توصیه می شود در اسرع وقت منطق خود را تکمیل کنید. انجام کارهای طولانی و فشرده بر عملکرد دستگاه تأثیر می گذارد و ممکن است باعث شود که سیستم عامل فرآیند را خاتمه دهد. اگر کارها بیش از 30 ثانیه اجرا شوند، دستگاه ممکن است به طور خودکار فرآیند را از بین ببرد.
وب
در وب، یک JavaScript Service Worker بنویسید که در پسزمینه اجرا میشود. از سرویسکار برای مدیریت پیامهای پسزمینه استفاده کنید.
برای شروع، یک فایل جدید در فهرست 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 خود را مجددا راه اندازی کنید. کارگر ثبت نام می شود و هرگونه پیام پس زمینه از طریق این فایل بررسی می شود.
مدیریت تعامل
از آنجایی که اعلانها یک نشانه قابل مشاهده هستند، معمولاً کاربران با آنها تعامل دارند (با فشار دادن). رفتار پیش فرض در اندروید و iOS باز کردن برنامه است. اگر برنامه خاتمه یابد، شروع خواهد شد. اگر در پسزمینه باشد، در پیشزمینه قرار میگیرد.
بسته به محتوای یک اعلان، ممکن است بخواهید هنگام باز شدن برنامه، تعامل کاربر را مدیریت کنید. به عنوان مثال، اگر یک پیام چت جدید از طریق یک اعلان ارسال شود و کاربر آن را فشار دهد، ممکن است بخواهید با باز شدن برنامه، مکالمه خاصی را باز کنید.
بسته firebase-messaging
دو راه برای مدیریت این تعامل ارائه میدهد:
-
getInitialMessage()
: اگر برنامه از حالت پایان یافته باز شود،Future
حاویRemoteMessage
برگردانده می شود. پس از مصرف،RemoteMessage
حذف خواهد شد. -
onMessageOpenedApp
:Stream
که وقتی برنامه از حالت پسزمینه باز میشود، یکRemoteMessage
ارسال میکند.
توصیه می شود که هر دو سناریو برای اطمینان از یک UX صاف برای کاربران خود بررسی شوند. مثال کد زیر چگونگی دستیابی به این امر را شرح می دهد:
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 نشان می دهد.
بومی سازی پیام ها
شما می توانید رشته های محلی را به دو روش مختلف ارسال کنید:
- زبان دلخواه هر یک از کاربران خود را در سرور خود ذخیره کنید و اعلان های سفارشی برای هر زبان ارسال کنید
- رشته های محلی شده را در برنامه خود جاسازی کنید و از تنظیمات محلی سیستم عامل استفاده کنید
در اینجا نحوه استفاده از روش دوم آورده شده است:
اندروید
پیام های زبان پیش فرض خود را در
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 به شما امکان میدهد دادهها را با استفاده از BigQuery SQL تجزیه و تحلیل کنید، آنها را به ارائهدهنده ابری دیگر صادر کنید یا از دادهها برای مدلهای ML سفارشی خود استفاده کنید. صادرات به BigQuery شامل تمام دادههای موجود برای پیامها، صرفنظر از نوع پیام یا ارسال پیام از طریق API یا سازنده Notifications است.
برای فعال کردن صادرات، ابتدا مراحل توضیح داده شده در اینجا را دنبال کنید، سپس این دستورالعمل ها را دنبال کنید:
اندروید
می توانید از کد زیر استفاده کنید:
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
وب
برای وب، برای استفاده از نسخه v9 SDK باید سرویس کار خود را تغییر دهید. نسخه v9 باید باندل شود، بنابراین برای مثال باید از باندلری مانند esbuild
استفاده کنید تا سرویسکار را به کار بیاورید. برای مشاهده نحوه دستیابی به این برنامه به مثال برنامه مراجعه کنید.
هنگامی که به v9 SDK مهاجرت کردید، می توانید از کد زیر استفاده کنید:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
فراموش نکنید که yarn build
اجرا کنید تا نسخه جدید سرویس کارگر خود را به پوشه web
صادر کنید.
نمایش تصاویر در اعلان ها در iOS
در دستگاههای اپل، برای اینکه اعلانهای FCM ورودی تصاویر را از محموله FCM نمایش دهند، باید یک افزونه سرویس اعلان اضافی اضافه کنید و برنامه خود را برای استفاده از آن پیکربندی کنید.
اگر از احراز هویت تلفن Firebase استفاده می کنید، باید غلاف Firebase Auth را به Podfile خود اضافه کنید.
مرحله 1 - یک برنامه افزودنی سرویس اعلان اضافه کنید
- در Xcode، روی File > New > Target کلیک کنید...
- یک مدال فهرستی از اهداف احتمالی را ارائه می دهد. به پایین پیمایش کنید یا از فیلتر برای انتخاب برنامه افزودنی سرویس اطلاع رسانی استفاده کنید. روی Next کلیک کنید.
- نام محصول را اضافه کنید (برای دنبال کردن این آموزش از "ImageNotification" استفاده کنید)، زبان را روی Objective-C تنظیم کنید و روی Finish کلیک کنید.
- با کلیک روی فعال کردن، طرح را فعال کنید.
مرحله 2 - هدف را به Podfile اضافه کنید
با افزودن آن در Podfile، اطمینان حاصل کنید که برنامه افزودنی جدید شما به غلاف Firebase/Messaging
دسترسی دارد:
از Navigator، 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 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 کیلوبایت توسط دستگاه اعمال می شود.