| Seleziona la piattaforma: | iOS+ Android Web Flutter Unity C++ |
A seconda dello stato di un dispositivo, i messaggi in arrivo vengono gestiti in modo diverso. Per comprendere questi scenari e come integrare FCM nella tua applicazione, è importante innanzitutto stabilire i vari stati in cui può trovarsi un dispositivo:
| Stato | Descrizione |
|---|---|
| Primo piano | Quando l'applicazione è aperta, in visualizzazione e in uso. |
| Sfondo | Quando l'applicazione è aperta, ma in background (ridotta a icona). In genere, questo accade quando l'utente ha premuto il pulsante "Home" sul dispositivo, ha cambiato app utilizzando il selettore di app o ha aperto l'applicazione in una scheda diversa (web). |
| Risolto in data | Quando il dispositivo è bloccato o l'applicazione non è in esecuzione. |
Prima che l'applicazione possa ricevere i payload dei messaggi utilizzando FCM, devono essere soddisfatte alcune precondizioni:
- L'applicazione deve essere stata aperta almeno una volta (per consentire la registrazione con FCM).
- Su iOS, se l'utente chiude l'applicazione dal selettore di app, deve riaprirla manualmente affinché i messaggi in background inizino a funzionare di nuovo.
- Su Android, se l'utente chiude forzatamente l'app dalle impostazioni del dispositivo, deve riaprirla manualmente affinché i messaggi inizino a funzionare.
- Sul web, devi aver richiesto un token (utilizzando
getToken()) con il certificato push web.
Richiedi l'autorizzazione a ricevere messaggi
Su iOS, macOS, web e Android 13 (o versioni successive), prima di poter ricevere i payload FCM sul dispositivo, devi prima chiedere l'autorizzazione all'utente.
Il pacchetto firebase_messaging fornisce un'API per richiedere l'autorizzazione utilizzando
il
requestPermission
metodo. Questa API accetta una serie di argomenti denominati che definiscono il tipo di autorizzazioni che vuoi richiedere, ad esempio se la messaggistica contenente payload di notifica può attivare un suono o leggere i messaggi utilizzando Siri. Per impostazione predefinita, il metodo richiede autorizzazioni predefinite sensibili. L'API di riferimento fornisce la documentazione completa sullo scopo di ogni autorizzazione.
Per iniziare, chiama il metodo dalla tua applicazione (su iOS verrà visualizzata una finestra modale integrata, sul web verrà attivato il flusso dell'API del browser):
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}');
La proprietà authorizationStatus dell'oggetto NotificationSettings restituito dalla richiesta può essere utilizzata per determinare la decisione complessiva dell'utente:
authorized: l'utente ha concesso l'autorizzazione.denied: l'utente ha negato l'autorizzazione.notDetermined: l'utente non ha ancora scelto se concedere l'autorizzazione.provisional: l'utente ha concesso l'autorizzazione provvisoria.
Le altre proprietà di NotificationSettings indicano se un'autorizzazione specifica è attivata, disattivata o non supportata sul dispositivo corrente.
Una volta concessa l'autorizzazione e compresi i diversi tipi di stato del dispositivo, l'applicazione può iniziare a gestire i payload FCM in arrivo.
Gestione dei messaggi
A seconda dello stato attuale dell'applicazione, i payload in arrivo di diversi tipi di messaggi richiedono implementazioni diverse per la gestione:
Messaggi in primo piano
Per gestire i messaggi mentre l'applicazione è in primo piano, ascolta lo stream 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}');
}
});
Lo stream contiene un RemoteMessage, che descrive varie informazioni sul payload, ad esempio la provenienza, l'ID univoco, l'ora di invio, se conteneva una notifica e altro ancora. Poiché il messaggio è stato recuperato mentre l'applicazione è in primo piano, puoi accedere direttamente allo stato e al contesto dell'applicazione Flutter.
Messaggi in primo piano e di notifica
Per impostazione predefinita, i messaggi di notifica che arrivano mentre l'applicazione è in primo piano non mostrano una notifica visibile, sia su Android che su iOS. Tuttavia, è possibile eseguire l'override di questo comportamento:
- Su Android, devi creare un canale di notifica "Priorità alta".
- Su iOS, puoi aggiornare le opzioni di presentazione per l'applicazione.
Messaggi in background
La procedura di gestione dei messaggi in background è diversa sulle piattaforme basate su Android, Apple e web.
Piattaforme Apple e Android
Gestisci i messaggi in background registrando un gestore onBackgroundMessage. Quando vengono ricevuti i messaggi, viene generato un isolato (solo Android, iOS/macOS non richiede un isolato separato) che ti consente di gestire i messaggi anche quando l'applicazione non è in esecuzione.
Ecco alcuni aspetti da tenere presente sul gestore dei messaggi in background:
- Non deve essere una funzione anonima.
- Deve essere una funzione di primo livello (ad es. non un metodo di classe che richiede l'inizializzazione).
- Quando utilizzi Flutter versione 3.3.0 o successive, il gestore dei messaggi deve essere
annotato con
@pragma('vm:entry-point')subito sopra la dichiarazione della funzione (altrimenti potrebbe essere rimosso durante l'eliminazione del codice inutilizzato per la modalità di rilascio).
@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());
}
Poiché il gestore viene eseguito nel proprio isolato al di fuori del contesto delle applicazioni, non è possibile aggiornare lo stato dell'applicazione o eseguire qualsiasi logica che influisce sull'UI. Tuttavia, puoi eseguire logiche come richieste HTTP, operazioni di I/O (ad es. aggiornamento dell'archiviazione locale), comunicare con altri plug-in e così via.
Ti consigliamo inoltre di completare la logica il prima possibile. L'esecuzione di attività lunghe e intensive influisce sulle prestazioni del dispositivo e potrebbe causare la chiusura del processo da parte del sistema operativo. Se le attività vengono eseguite per più di 30 secondi, il dispositivo potrebbe chiudere automaticamente il processo.
Web
Sul web, scrivi un service worker JavaScript Service Worker che viene eseguito in background. Utilizza il service worker per gestire i messaggi in background.
Per iniziare, crea un nuovo file nella directory web e chiamalo firebase-messaging-sw.js:
// See this file for the latest firebase-js-sdk version:
// https://github.com/firebase/flutterfire/blob/main/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);
});
Il file deve importare gli SDK dell'app e della messaggistica, inizializzare Firebase ed esporre la variabile messaging.
Poi, il worker deve essere registrato. Nel file index.html, registra il
worker modificando il tag <script> che esegue il bootstrap di 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>
Se utilizzi ancora il vecchio sistema di modelli, puoi registrare il worker
modificando il tag <script> che esegue il bootstrap di Flutter come segue:
<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>
Riavvia l'applicazione Flutter. Il worker verrà registrato e tutti i messaggi in background verranno gestiti utilizzando questo file.
Gestione dell'interazione
Poiché le notifiche sono un segnale visibile, è normale che gli utenti interagiscano con loro (premendo). Il comportamento predefinito sia su Android che su iOS è l'apertura dell'applicazione. Se l'applicazione viene chiusa, verrà avviata; se è in background, verrà portata in primo piano.
A seconda del contenuto di una notifica, potresti voler gestire l'interazione dell'utente all'apertura dell'applicazione. Ad esempio, se viene inviato un nuovo messaggio di chat utilizzando una notifica e l'utente la preme, potresti voler aprire la conversazione specifica all'apertura dell'applicazione.
Il pacchetto firebase-messaging offre due modi per gestire questa interazione:
getInitialMessage(): se l'applicazione viene aperta da uno stato di chiusura, verrà restituito unFuturecontenente unRemoteMessage. Una volta utilizzato, ilRemoteMessageverrà rimosso.onMessageOpenedApp: unoStreamche pubblica unRemoteMessagequando l'applicazione viene aperta da uno stato in background.
Ti consigliamo di gestire entrambi gli scenari per garantire un'esperienza utente fluida per i tuoi utenti. Il seguente esempio di codice illustra come è possibile ottenere questo risultato:
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 using 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("...");
}
}
La modalità di gestione dell'interazione dipende dalla configurazione dell'applicazione. L'esempio precedente mostra un'illustrazione di base che utilizza un StatefulWidget.
Localizzare i messaggi
Puoi inviare stringhe localizzate in due modi diversi:
- Memorizza la lingua preferita di ogni utente sul server e invia notifiche personalizzate per ogni lingua.
- Incorpora le stringhe localizzate nell'app e utilizza le impostazioni internazionali integrate del sistema operativo.
Ecco come utilizzare il secondo metodo:
Android
Specifica i messaggi nella lingua predefinita in
resources/values/strings.xml:<string name="notification_title">Hello world</string> <string name="notification_message">This is a message</string>Specifica i messaggi tradotti nella directory
values-language. Ad esempio, specifica i messaggi in francese inresources/values-fr/strings.xml:<string name="notification_title">Bonjour le monde</string> <string name="notification_message">C'est un message</string>Nel payload del server, anziché utilizzare le chiavi
title,message, ebody, utilizzatitle_loc_keyebody_loc_keyper il messaggio localizzato e impostale sull'attributonamedel messaggio che vuoi visualizzare.Il payload del messaggio avrà il seguente aspetto:
{ "android": { "notification": { "title_loc_key": "notification_title", "body_loc_key": "notification_message" } } }
iOS
Specifica i messaggi nella lingua predefinita in
Base.lproj/Localizable.strings:"NOTIFICATION_TITLE" = "Hello World"; "NOTIFICATION_MESSAGE" = "This is a message";Specifica i messaggi tradotti nella directory
language.lproj. Ad esempio, specifica i messaggi in francese infr.lproj/Localizable.strings:"NOTIFICATION_TITLE" = "Bonjour le monde"; "NOTIFICATION_MESSAGE" = "C'est un message";Il payload del messaggio avrà il seguente aspetto:
{ "apns": { "payload": { "alert": { "title-loc-key": "NOTIFICATION_TITLE", "loc-key": "NOTIFICATION_MESSAGE" } } } }
Attiva l'esportazione dei dati di recapito dei messaggi
Puoi esportare i dati dei messaggi in BigQuery per ulteriori analisi. BigQuery ti consente di analizzare i dati utilizzando BigQuery SQL, esportarli in un altro provider di servizi cloud o utilizzarli per i tuoi modelli di ML personalizzati. Un'esportazione in BigQuery include tutti i dati disponibili per i messaggi, indipendentemente dal tipo di messaggio o dal fatto che il messaggio venga inviato utilizzando l'API o il compositore di notifiche.
Per attivare l'esportazione, segui prima i passaggi descritti nel documento Esportazione dei dati di BigQuery. L'attivazione programmatica a livello di istanza dell'app ti consente di chiedere agli utenti finali l'autorizzazione ad analizzare i dati di recapito dei messaggi (opzione consigliata). Segui queste istruzioni per attivare l'esportazione a livello di programmazione:
Android
Puoi utilizzare il seguente codice:
await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);
iOS
Per iOS, devi modificare AppDelegate.m con i seguenti contenuti.
#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
Web
Per il web, devi modificare il service worker per utilizzare la versione 9 dell'SDK. La versione 9 deve essere raggruppata, quindi devi utilizzare un bundler come esbuild per far funzionare il service worker. Consulta l'app
di esempio
per scoprire come fare.
Una volta eseguita la migrazione all'SDK v9, puoi utilizzare il seguente codice:
import {
experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
getMessaging,
} from 'firebase/messaging/sw';
...
const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);
Non dimenticare di eseguire yarn build per esportare la nuova versione del service worker nella cartella web.
Visualizzare le immagini nelle notifiche su iOS
Sui dispositivi Apple, affinché le notifiche FCM in arrivo visualizzino le immagini dal payload FCM, devi aggiungere un'estensione del servizio di notifica aggiuntiva e configurare l'app in modo che la utilizzi.
Se utilizzi l'autenticazione telefonica Firebase, devi aggiungere il pod Firebase Auth al podfile.
Passaggio 1: aggiungi un'estensione del servizio di notifica
- In Xcode, fai clic su File > New > Target... (File > Nuovo > Destinazione…)
- Verrà visualizzata una finestra modale con un elenco di possibili destinazioni; scorri o utilizza il filtro per selezionare Notification Service Extension (Estensione del servizio di notifica). Fai clic su Avanti.
- Aggiungi un nome del prodotto (utilizza "ImageNotification" per seguire questo tutorial), seleziona
SwiftoObjective-Ce fai clic su Fine. - Attiva lo schema facendo clic su Attiva.
Passaggio 2: aggiungi la destinazione al podfile
Swift
Assicurati che la nuova estensione abbia accesso al pacchetto Swift FirebaseMessaging aggiungendolo alla destinazione Runner:
Dal navigatore, aggiungi l'SDK delle piattaforme Apple Firebase: File > Aggiungi dipendenze pacchetto…
Cerca o inserisci l'URL del pacchetto:
none https://github.com/firebase/firebase-ios-sdkAggiungi a Project
Runner: Add Package (Aggiungi pacchetto)Scegli FirebaseMessaging e aggiungilo alla destinazione ImageNotification: Add Package (Aggiungi pacchetto)
Objective-C
Assicurati che la nuova estensione abbia accesso al pod Firebase/Messaging aggiungendolo al podfile:
Dal navigatore, apri il podfile: Pods > Podfile
Vai in fondo al file e aggiungi:
target 'ImageNotification' do use_frameworks! pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication pod 'Firebase/Messaging' endInstalla o aggiorna i pod utilizzando
pod installdalla directoryiosomacos.
Passaggio 3: utilizza l'helper dell'estensione
A questo punto, tutto dovrebbe funzionare normalmente. Il passaggio finale consiste nell'invocare l'helper dell'estensione.
Swift
Dal navigatore, seleziona l'estensione ImageNotification
Apri il file
NotificationService.swift.Sostituisci i contenuti di
NotificationService.swiftcon:import UserNotifications import FirebaseMessaging class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) Messaging.serviceExtension().populateNotificationContent(bestAttemptContent!, withContentHandler: contentHandler) } override func serviceExtensionTimeWillExpire() { if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } } }
Objective-C
Dal navigatore, seleziona l'estensione ImageNotification
Apri il file
NotificationService.m.All'inizio del file, importa
FirebaseMessaging.hsubito dopoNotificationService.h.Sostituisci i contenuti di
NotificationService.mcon:#import "NotificationService.h" #import "FirebaseMessaging.h" #import <FirebaseAuth/FirebaseAuth-Swift.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 () <NSURLSessionDelegate> @property(nonatomic) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property(nonatomic) 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
Passaggio 4: aggiungi l'immagine al payload
Nel payload della notifica, ora puoi aggiungere un'immagine. Per saperne di più, scopri come creare una richiesta di invio.