В зависимости от состояния устройства входящие сообщения обрабатываются по-разному. Чтобы понять эти сценарии и способы интеграции FCM в ваше собственное приложение, сначала важно установить различные состояния, в которых может находиться устройство:
Состояние | Описание |
---|---|
передний план | Когда приложение открыто, отображается и используется. |
Фон | Когда приложение открыто, но в фоновом режиме (свернуто). Обычно это происходит, когда пользователь нажал кнопку «Домой» на устройстве, переключился на другое приложение с помощью переключателя приложений или открыл приложение на другой вкладке (в Интернете). |
Прекращено | Когда устройство заблокировано или приложение не запущено. |
Существует несколько предварительных условий, которые должны быть выполнены, прежде чем приложение сможет получать полезные данные сообщений через FCM:
- Приложение должно быть открыто хотя бы один раз (чтобы можно было зарегистрироваться в FCM).
- В iOS, если пользователь убирает приложение из переключателя приложений, его необходимо открыть вручную, чтобы фоновые сообщения снова начали работать.
- На Android, если пользователь принудительно закрывает приложение в настройках устройства, его необходимо открыть вручную, чтобы сообщения начали работать.
- В Интернете вы должны запросить токен (с помощью
getToken()
) с вашим сертификатом веб-push.
Запросить разрешение на получение сообщений
В iOS, macOS, веб-версии и Android 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.
Сообщения переднего плана и уведомлений
Уведомительные сообщения, которые приходят, когда приложение находится на переднем плане, по умолчанию не отображают видимое уведомление ни на Android, ни на iOS. Однако это поведение можно переопределить:
- На Android необходимо создать канал уведомлений «Высокий приоритет».
- В iOS вы можете обновить параметры презентации приложения.
Фоновые сообщения
Процесс обработки фоновых сообщений различается на собственных (Android и Apple) и веб-платформах.
Платформы Apple и Android
Обрабатывайте фоновые сообщения, регистрируя обработчик onBackgroundMessage
. При получении сообщений создается изолят (только для Android, 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());
}
Поскольку обработчик выполняется изолированно вне контекста вашего приложения, невозможно обновить состояние приложения или выполнить какую-либо логику, влияющую на пользовательский интерфейс. Однако вы можете выполнять логику, такую как HTTP-запросы, выполнять операции ввода-вывода (например, обновление локального хранилища), взаимодействовать с другими плагинами и т. д.
Также рекомендуется как можно скорее завершить логику. Выполнение длительных и интенсивных задач влияет на производительность устройства и может привести к завершению процесса ОС. Если задачи выполняются более 30 секунд, устройство может автоматически завершить процесс.
Интернет
В Интернете напишите Service Worker 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.
Локализация сообщений
Вы можете отправлять локализованные строки двумя разными способами:
- Сохраняйте предпочтительный язык каждого из ваших пользователей на своем сервере и отправляйте индивидуальные уведомления для каждого языка.
- Встраивайте локализованные строки в свое приложение и используйте собственные настройки локали операционной системы.
Вот как использовать второй метод:
Андроид
Укажите сообщения на языке по умолчанию в
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, экспортировать их другому облачному провайдеру или использовать данные для собственных моделей машинного обучения. Экспорт в BigQuery включает в себя все доступные данные для сообщений, независимо от типа сообщения или от того, отправлено ли сообщение через API или через композитор уведомлений.
Чтобы включить экспорт, сначала выполните действия, описанные здесь , затем следуйте этим инструкциям:
Андроид
Вы можете использовать следующий код:
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
Интернет
В Интернете вам необходимо сменить своего сервис-воркера, чтобы использовать версию SDK v9. Версия v9 должна быть в комплекте, поэтому вам нужно использовать упаковщик, например, esbuild
чтобы заставить работать сервис-воркера. Посмотрите пример приложения, чтобы узнать, как этого добиться.
После перехода на SDK v9 вы можете использовать следующий код:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
Не забудьте запустить yarn build
, чтобы экспортировать новую версию вашего сервис-воркера в web
папку.
Отображение изображений в уведомлениях на iOS
На устройствах Apple, чтобы входящие уведомления FCM отображали изображения из полезных данных FCM, необходимо добавить дополнительное расширение службы уведомлений и настроить приложение для его использования.
Если вы используете аутентификацию по телефону Firebase, вам необходимо добавить модуль Firebase Auth в свой подфайл.
Шаг 1. Добавьте расширение службы уведомлений
- В Xcode нажмите Файл > Создать > Цель...
- Модальное окно представит список возможных целей; прокрутите вниз или воспользуйтесь фильтром, чтобы выбрать «Расширение службы уведомлений» . Нажмите Далее .
- Добавьте название продукта (используйте «ImageNotification», чтобы следовать этому руководству), установите язык Objective-C и нажмите «Готово» .
- Включите схему, нажав «Активировать» .
Шаг 2. Добавьте цель в подфайл.
Убедитесь, что ваше новое расширение имеет доступ к модулю Firebase/Messaging
, добавив его в подфайл:
В навигаторе откройте подфайл: «Модули» > «Подфайл».
Прокрутите файл до конца и добавьте:
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 КБ установлен устройством.
,В зависимости от состояния устройства входящие сообщения обрабатываются по-разному. Чтобы понять эти сценарии и способы интеграции FCM в ваше собственное приложение, сначала важно установить различные состояния, в которых может находиться устройство:
Состояние | Описание |
---|---|
передний план | Когда приложение открыто, отображается и используется. |
Фон | Когда приложение открыто, но в фоновом режиме (свернуто). Обычно это происходит, когда пользователь нажал кнопку «Домой» на устройстве, переключился на другое приложение с помощью переключателя приложений или открыл приложение на другой вкладке (в Интернете). |
Прекращено | Когда устройство заблокировано или приложение не запущено. |
Существует несколько предварительных условий, которые должны быть выполнены, прежде чем приложение сможет получать полезные данные сообщения через FCM:
- Приложение должно быть открыто хотя бы один раз (чтобы можно было зарегистрироваться в FCM).
- В iOS, если пользователь убирает приложение из переключателя приложений, его необходимо открыть вручную, чтобы фоновые сообщения снова начали работать.
- На Android, если пользователь принудительно закрывает приложение в настройках устройства, его необходимо открыть вручную, чтобы сообщения начали работать.
- В Интернете вы должны запросить токен (с помощью
getToken()
) с вашим сертификатом веб-push.
Запросить разрешение на получение сообщений
В iOS, macOS, веб-версии и Android 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.
Сообщения переднего плана и уведомлений
Уведомительные сообщения, которые приходят, когда приложение находится на переднем плане, по умолчанию не отображают видимое уведомление ни на Android, ни на iOS. Однако это поведение можно переопределить:
- На Android необходимо создать канал уведомлений «Высокий приоритет».
- В iOS вы можете обновить параметры презентации приложения.
Фоновые сообщения
Процесс обработки фоновых сообщений различается на собственных (Android и Apple) и веб-платформах.
Платформы Apple и Android
Обрабатывайте фоновые сообщения, регистрируя обработчик onBackgroundMessage
. При получении сообщений создается изолят (только для Android, 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());
}
Поскольку обработчик работает изолированно вне контекста вашего приложения, невозможно обновить состояние приложения или выполнить какую-либо логику, влияющую на пользовательский интерфейс. Однако вы можете выполнять логику, такую как HTTP-запросы, выполнять операции ввода-вывода (например, обновление локального хранилища), взаимодействовать с другими плагинами и т. д.
Также рекомендуется как можно скорее завершить логику. Выполнение длительных и интенсивных задач влияет на производительность устройства и может привести к завершению процесса ОС. Если задачи выполняются более 30 секунд, устройство может автоматически завершить процесс.
Интернет
В Интернете напишите Service Worker 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.
Локализация сообщений
Вы можете отправлять локализованные строки двумя разными способами:
- Сохраняйте предпочтительный язык каждого из ваших пользователей на своем сервере и отправляйте индивидуальные уведомления для каждого языка.
- Встраивайте локализованные строки в свое приложение и используйте собственные настройки локали операционной системы.
Вот как использовать второй метод:
Андроид
Укажите сообщения на языке по умолчанию в
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, экспортировать их другому облачному провайдеру или использовать данные для собственных моделей машинного обучения. Экспорт в BigQuery включает в себя все доступные данные для сообщений, независимо от типа сообщения или от того, отправлено ли сообщение через API или через композитор уведомлений.
Чтобы включить экспорт, сначала выполните действия, описанные здесь , затем следуйте этим инструкциям:
Андроид
Вы можете использовать следующий код:
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
Интернет
В Интернете вам необходимо сменить своего сервис-воркера, чтобы использовать версию SDK v9. Версия v9 должна быть в комплекте, поэтому вам нужно использовать упаковщик, например, esbuild
чтобы заставить работать сервис-воркера. Посмотрите пример приложения, чтобы узнать, как этого добиться.
После перехода на SDK v9 вы можете использовать следующий код:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
Не забудьте запустить yarn build
, чтобы экспортировать новую версию вашего сервис-воркера в web
папку.
Отображение изображений в уведомлениях на iOS
На устройствах Apple, чтобы входящие уведомления FCM отображали изображения из полезных данных FCM, необходимо добавить дополнительное расширение службы уведомлений и настроить приложение для его использования.
Если вы используете аутентификацию по телефону Firebase, вам необходимо добавить модуль Firebase Auth в свой подфайл.
Шаг 1. Добавьте расширение службы уведомлений
- В Xcode нажмите Файл > Создать > Цель...
- Модальное окно представит список возможных целей; прокрутите вниз или воспользуйтесь фильтром, чтобы выбрать «Расширение службы уведомлений» . Нажмите Далее .
- Добавьте название продукта (используйте «ImageNotification», чтобы следовать этому руководству), установите язык Objective-C и нажмите «Готово» .
- Включите схему, нажав «Активировать» .
Шаг 2. Добавьте цель в подфайл.
Убедитесь, что ваше новое расширение имеет доступ к модулю Firebase/Messaging
, добавив его в подфайл:
В навигаторе откройте подфайл: «Модули» > «Подфайл».
Прокрутите файл до конца и добавьте:
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 КБ установлен устройством.
,В зависимости от состояния устройства входящие сообщения обрабатываются по-разному. Чтобы понять эти сценарии и способы интеграции FCM в ваше собственное приложение, сначала важно установить различные состояния, в которых может находиться устройство:
Состояние | Описание |
---|---|
передний план | Когда приложение открыто, отображается и используется. |
Фон | Когда приложение открыто, но в фоновом режиме (свернуто). Обычно это происходит, когда пользователь нажал кнопку «Домой» на устройстве, переключился на другое приложение с помощью переключателя приложений или открыл приложение на другой вкладке (в Интернете). |
Прекращено | Когда устройство заблокировано или приложение не запущено. |
Существует несколько предварительных условий, которые должны быть выполнены, прежде чем приложение сможет получать полезные данные сообщения через FCM:
- Приложение должно быть открыто хотя бы один раз (чтобы можно было зарегистрироваться в FCM).
- В iOS, если пользователь убирает приложение из переключателя приложений, его необходимо открыть вручную, чтобы фоновые сообщения снова начали работать.
- На Android, если пользователь принудительно закрывает приложение в настройках устройства, его необходимо открыть вручную, чтобы сообщения начали работать.
- В Интернете вы должны запросить токен (с помощью
getToken()
) с вашим сертификатом веб-push.
Запросить разрешение на получение сообщений
В iOS, macOS, веб-версии и Android 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.
Сообщения переднего плана и уведомлений
Уведомительные сообщения, которые приходят, когда приложение находится на переднем плане, по умолчанию не отображают видимое уведомление ни на Android, ни на iOS. Однако это поведение можно переопределить:
- На Android необходимо создать канал уведомлений «Высокий приоритет».
- В iOS вы можете обновить параметры презентации приложения.
Фоновые сообщения
Процесс обработки фоновых сообщений различается на собственных (Android и Apple) и веб-платформах.
Платформы Apple и Android
Обрабатывайте фоновые сообщения, регистрируя обработчик onBackgroundMessage
. При получении сообщений создается изолят (только для Android, 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());
}
Поскольку обработчик выполняется изолированно вне контекста вашего приложения, невозможно обновить состояние приложения или выполнить какую-либо логику, влияющую на пользовательский интерфейс. Однако вы можете выполнять логику, такую как HTTP-запросы, выполнять операции ввода-вывода (например, обновление локального хранилища), взаимодействовать с другими плагинами и т. д.
Также рекомендуется как можно скорее завершить логику. Выполнение длительных и интенсивных задач влияет на производительность устройства и может привести к завершению процесса ОС. Если задачи выполняются более 30 секунд, устройство может автоматически завершить процесс.
Интернет
В Интернете напишите Service Worker 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.
Локализация сообщений
Вы можете отправлять локализованные строки двумя разными способами:
- Сохраняйте предпочтительный язык каждого из ваших пользователей на своем сервере и отправляйте индивидуальные уведомления для каждого языка.
- Встраивайте локализованные строки в свое приложение и используйте собственные настройки локали операционной системы.
Вот как использовать второй метод:
Андроид
Укажите сообщения на языке по умолчанию в
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 или композитор уведомлений.
Чтобы включить экспорт, сначала следуйте описанным здесь шагам, затем следуйте этим инструкциям:
Андроид
Вы можете использовать следующий код:
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
На устройствах Apple, чтобы входящие уведомления FCM отображали изображения из полезной нагрузки FCM, необходимо добавить дополнительное расширение службы уведомлений и настроить ваше приложение для его использования.
Если вы используете аутентификацию телефона Firebase, вы должны добавить капсул Firebase Auth в свой Podfile.
Шаг 1 - добавьте расширение уведомления
- В xcode нажмите «Файл»> «Новая»> «Target ...
- Модал представит список возможных целей; Прокрутите вниз или используйте фильтр, чтобы выбрать расширение службы уведомления . Нажмите Далее .
- Добавьте название продукта (используйте «ImageNotification», чтобы следовать этим уроку), установите язык на Objective-C и нажмите Finish .
- Включите схему, нажав Activate .
Шаг 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 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 КБ применяется устройством.
,В зависимости от состояния устройства входящие сообщения обрабатываются по -разному. Чтобы понять эти сценарии и как интегрировать FCM в ваше собственное приложение, сначала важно установить различные состояния, в которых может быть устройство:
Состояние | Описание |
---|---|
передний план | Когда приложение открыто, с учетом и использование. |
Фон | Когда приложение открыто, но на заднем плане (минимизировано). Обычно это происходит, когда пользователь нажимал кнопку «Домашний» на устройстве, переключилось на другое приложение с помощью коммутатора приложения или открывает приложение на другой вкладке (веб -сайт). |
Прекращен | Когда устройство заблокировано или приложение не работает. |
Есть несколько предварительных условий, которые должны быть выполнены до того, как приложение сможет получить полезные нагрузки сообщений через FCM:
- Заявка должна была открыться хотя бы один раз (чтобы разрешить регистрацию с FCM).
- На iOS, если пользователь откидывает приложение от переключателя приложений, его необходимо вручную вручную открыть для фоновых сообщений, чтобы начать работу снова.
- На Android, если пользователь придает приложение из настройки устройства, его необходимо вручную повторно открыть для того, чтобы сообщения начали работать.
- В Интернете вы должны запросить токен (с помощью
getToken()
) с вашим сертификатом веб -push.
Запросить разрешение на получение сообщений
На iOS, MacOS, Web и Android 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
, детализирующую различную информацию о полезной нагрузке, например, откуда он был, уникальный идентификатор, отправлено время, независимо от того, содержат ли он уведомление и многое другое. Поскольку сообщение было извлечено, пока ваше приложение находится на переднем плане, вы можете напрямую получить доступ к состоянию и контексту вашего приложения.
Сообщения на переднем плане и уведомлениях
Сообщения уведомлений, которые поступают, когда приложение находится на переднем плане, не будут отображать видимое уведомление по умолчанию, как на Android, так и на iOS. Однако возможно переопределить это поведение:
- На Android вы должны создать канал уведомления «высокий приоритет».
- На iOS вы можете обновить параметры презентации для приложения.
Фоновые сообщения
Процесс обработки фоновых сообщений отличается на собственных (Android и Apple) и веб -платформах.
Apple Platforms и Android
Обработайте фоновые сообщения, зарегистрировав обработчик onBackgroundMessage
. При получении сообщений, изолят появляется (только для Android, 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());
}
Поскольку обработчик запускается в своем собственном изоляте за пределами вашего контекста приложений, невозможно обновить состояние приложения или выполнить любую логику, влияющего на пользовательский интерфейс. Вы можете, однако, выполнить логику, такую как HTTP -запросы, выполнить операции ввода -вывода (например, обновление локального хранилища), общение с другими плагинами и т. Д.
Также рекомендуется завершить вашу логику как можно скорее. Запуск длинных, интенсивных задач влияет на производительность устройства и может привести к прекращению процесса ОС. Если задачи работают дольше 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);
});
Файл должен импортировать как приложение, так и сообщения об обмене сообщениями, инициализировать Firebase и выявить переменную messaging
.
Далее работник должен быть зарегистрирован. В файле index.html
зарегистрируйте работника, изменяя тег <script>
, который Bootstraps 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>
, который засыпается, что развевает следующим образом:
<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
, когда приложение открыто из фонового состояния.
Рекомендуется обрабатывать оба сценария, чтобы обеспечить плавный 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 или композитор уведомлений.
Чтобы включить экспорт, сначала следуйте описанным здесь шагам, затем следуйте этим инструкциям:
Андроид
Вы можете использовать следующий код:
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
На устройствах Apple, чтобы входящие уведомления FCM отображали изображения из полезной нагрузки FCM, необходимо добавить дополнительное расширение службы уведомлений и настроить ваше приложение для его использования.
Если вы используете аутентификацию телефона Firebase, вы должны добавить капсул Firebase Auth в свой Podfile.
Шаг 1 - добавьте расширение уведомления
- В xcode нажмите «Файл»> «Новая»> «Target ...
- Модал представит список возможных целей; Прокрутите вниз или используйте фильтр, чтобы выбрать расширение службы уведомления . Нажмите Далее .
- Добавьте название продукта (используйте «ImageNotification», чтобы следовать этим уроку), установите язык на Objective-C и нажмите Finish .
- Включите схему, нажав Activate .
Шаг 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 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 КБ применяется устройством.