Bonnes pratiques pour la gestion des enregistrements FCM

Si vous utilisez les API FCM pour créer des requêtes d'envoi de manière programmatique, vous constaterez peut-être qu'au fil du temps, vous gaspillez des ressources en envoyant des messages à des appareils inactifs avec des enregistrements obsolètes. Cette situation peut affecter les données de distribution des messages indiquées dans la console Firebase ou les données exportées vers BigQuery, en affichant une baisse spectaculaire (mais en réalité non valide) des taux de distribution. Ce guide présente certaines mesures que vous pouvez prendre pour vous assurer que vos messages sont ciblés efficacement et que les rapports de diffusion sont valides.

Enregistrements obsolètes et expirés

Les enregistrements obsolètes sont associés à des appareils inactifs qui ne se sont pas connectés à FCM depuis plus d'un mois. Au fil du temps, il devient de moins en moins probable que l'appareil se connecte à nouveau à FCM. Il est peu probable que les messages et les fan-outs de thèmes pour ces enregistrements obsolètes soient un jour distribués.

Plusieurs raisons peuvent expliquer qu'un enregistrement devienne obsolète. Par exemple, l'appareil auquel l'enregistrement est associé peut être perdu, détruit ou rangé et oublié.

Pour Android, lorsqu'un enregistrement est inactif pendant 270 jours, FCM le considère comme expiré et le supprime. Une fois l'enregistrement expiré, FCM le marque comme non valide et refuse les envois. Notez que les ID d'installation Firebase (FIDs) sont gérés par le service Firebase Installations (FIS), et non par FCM. Dans le cas rare où un appareil se reconnecte et où l'application est ouverte après que son enregistrement a été collecté par le garbage collector, l'application cliente s'enregistre à nouveau avec FCM à l'aide du FID récupéré à partir de FIS. Notez que le FID peut changer. Pour savoir quand les FID sont réémis, consultez Gérer les installations Firebase.

Pour les autres plates-formes comme iOS, FCM s'appuie sur le service push sous-jacent (par exemple, APNs), qui n'a pas la même expiration basée sur l'inactivité de 270 jours. Nous vous recommandons de maintenir à jour les enregistrements et de supprimer ceux qui sont obsolètes.

Bonnes pratiques de base

Il existe des pratiques fondamentales que vous devez suivre dans toute application qui utilise les API FCM pour créer des requêtes d'envoi de manière programmatique. Voici les principales bonnes pratiques :

  • Récupérez les ID d'installation Firebase (FIDs) à partir de FCM et stockez-les sur le serveur de votre application. Un rôle important du serveur est de suivre le FID enregistré de chaque client et de tenir à jour une liste des FID actifs. Nous vous recommandons vivement d'implémenter un code temporel d'enregistrement dans votre base de données et de le mettre à jour chaque fois qu'un enregistrement est importé.
  • Maintenez la fraîcheur des enregistrements et supprimez ceux qui sont obsolètes. En plus de supprimer les enregistrements que FCM ne considère plus comme valides, vous pouvez surveiller d'autres signes indiquant que les enregistrements sont devenus obsolètes et les supprimer de manière proactive. Ce guide décrit certaines des options qui s'offrent à vous.

Récupérer et stocker les ID d'installation Firebase

Au démarrage initial de votre application, le SDK FCM enregistre l'instance de l'application auprès de FCM et renvoie un ID d'installation Firebase (FID). Il s'agit de l'identifiant que vous devez inclure dans les requêtes d'envoi ciblé depuis l'API ou utiliser pour les abonnements aux thèmes.

Nous vous recommandons vivement d'enregistrer le FID sur le serveur de votre application avec un code temporel chaque fois qu'il est importé. En mettant à jour le code temporel à chaque requête d'importation, votre serveur sait quand l'instance d'application a été ouverte et synchronisée avec succès avec le backend FCM.

Selon que l'initialisation automatique est activée ou désactivée (y compris non prise en charge), vous devez gérer l'enregistrement et les mises à jour comme suit :

  • (Recommandé) Lorsque l'initialisation automatique est activée : le SDK maintient automatiquement l'enregistrement à jour et surveille les modifications. Le rappel onRegistered() est invoqué régulièrement lors des synchronisations de routine au démarrage de l'application, ainsi que lorsque des modifications du FID se produisent. Il vous suffit d'implémenter cette fonction de rappel pour importer le FID sur votre serveur et enregistrer l'horodatage actuel.
  • Lorsque l'initialisation automatique est désactivée : le rappel onRegistered() n'est pas automatiquement appelé au démarrage. Pour suivre les enregistrements et les actualiser, appelez register() au démarrage de l'application. Par exemple, sur Android, dans la méthode onCreate() de l'activité principale. Un appel réussi déclenche le processus d'enregistrement FCM à l'aide du FID et le transmet à votre rappel onRegistered(), ce qui permet à votre application d'importer le FID et de mettre à jour le code temporel sur votre serveur.

Exemple : stocker les FIDs et les codes temporels des magasins dans Cloud Firestore

Par exemple, vous pouvez utiliser Cloud Firestore pour stocker des FIDs dans une collection appelée fcmRegistrations. Chaque ID de document de la collection correspond à un ID utilisateur. Le document stocke le FID actuel et son code temporel de dernière mise à jour. Utilisez la fonction set comme indiqué dans cet exemple Kotlin :

private fun sendRegistrationToServer(installationId: String?) {
    // If you're running your own server, call API to send registration details and today's date for the user

    // Example shown uses Firestore
    // Add FID and timestamp to Firestore for this user
    val deviceFid = hashMapOf(
        "installationId" to installationId,
        "timestamp" to FieldValue.serverTimestamp(),
    )
    // Get user ID from Firebase Auth or your own server
    Firebase.firestore.collection("fcmRegistrations").document("myuserid")
        .set(deviceFid)
}

Chaque fois qu'un ID d'installation Firebase est enregistré ou mis à jour, le rappel onRegistered() est appelé. Vous devez implémenter ce rappel pour importer le FID et mettre à jour le code temporel :

override fun onRegistered(installationId: String) {
    Log.d(TAG, "Registered installation ID: $installationId")

    // Send the Firebase Installation ID (FID) to your app server. Your app
    // server should save the FID and update the timestamp upon receipt.
    sendRegistrationToServer(installationId)
}

Pour les instances où l'initialisation automatique est désactivée, appelez register() au démarrage de l'application (par exemple, dans onCreate()) pour déclencher le flux d'enregistrement et la diffusion du FID via onRegistered() :

// Trigger manual registration if auto-initialization is turned off.
FirebaseMessaging.getInstance().register()
    .addOnCompleteListener(this) { task ->
        if (task.isSuccessful) {
            // The registration callback onRegistered() will be invoked with the current FID.
        } else {
            Log.w(TAG, "Failed to register with Firebase Cloud Messaging", task.exception)
        }
    }

Maintenir la fraîcheur des enregistrements et supprimer les enregistrements obsolètes

Il n'est pas toujours facile de déterminer si un enregistrement est récent ou obsolète. Pour couvrir tous les cas, vous devez adopter un seuil pour déterminer quand les enregistrements sont considérés comme obsolètes. Par défaut, FCM considère qu'un enregistrement est obsolète si son instance d'application ne s'est pas connectée depuis un mois. Tout enregistrement datant de plus d'un mois correspond probablement à un appareil inactif. En effet, un appareil actif aurait actualisé son enregistrement.

Selon votre cas d'utilisation, un mois peut être trop court ou trop long. C'est donc à vous de déterminer les critères qui vous conviennent.

Détecter les réponses non valides du backend FCM

Assurez-vous de détecter les réponses non valides de FCM et de répondre en supprimant de votre système tous les enregistrements connus comme non valides ou expirés. Avec l'API HTTP v1, ces messages d'erreur peuvent indiquer que votre demande d'envoi ciblait des enregistrements non valides ou expirés :

  • UNREGISTERED (HTTP 404)
  • INVALID_ARGUMENT (HTTP 400)

Si vous êtes certain que la charge utile du message est valide et que vous recevez l'une de ces réponses pour un enregistrement ciblé, vous pouvez supprimer l'enregistrement, car il ne sera plus jamais valide. Par exemple, pour supprimer les enregistrements non valides de Cloud Firestore, vous pouvez déployer et exécuter une fonction comme celle-ci :

        // Firebase Installation ID comes from the client FCM SDKs
        const firebaseInstallationId = 'YOUR_FIREBASE_INSTALLATION_ID';

        const message = {
            data: {
                // Information you want to send inside of notification
            },
            fid: firebaseInstallationId
        };

        // Send message to device with provided Firebase Installation ID
        getMessaging().send(message)
        .then((response) => {
            // Response is a message ID string.
        })
        .catch((error) => {
            // Delete registration for user if error code is UNREGISTERED or INVALID_ARGUMENT.
            if (error.errorCode == "messaging/registration-token-not-registered") {
                // If you're running your own server, call API to delete the registration for the user
                // Example shown uses Firestore
                // Get user ID from Firebase Auth or your own server
                Firebase.firestore.collection("fcmRegistrations").document(user.uid).delete()
            }
        });

FCM renvoie une réponse non valide si l'enregistrement d'un appareil Android a expiré après 270 jours d'inactivité ou si un client s'est désinscrit de manière explicite. Si vous avez besoin de suivre plus précisément l'obsolescence selon vos propres définitions, vous pouvez supprimer de manière proactive les enregistrements obsolètes.

Mettre à jour régulièrement les inscriptions

Que vos enregistrements soient basés sur des FID ou des anciens jetons d'enregistrement, votre serveur doit toujours mettre à jour l'horodatage de l'enregistrement dans votre base de données à chaque requête d'importation. Ce code temporel sert de signal pour l'installation de l'application, indiquant que le client a bien ouvert l'application et s'est synchronisé avec le backend FCM. En fonction des API que vous utilisez, implémentez la stratégie appropriée :

Pour les applications clientes qui utilisent les API FID, vous n'avez pas besoin de planifier des tâches d'arrière-plan périodiques dans votre application cliente pour récupérer ou actualiser les enregistrements. Le SDK gère automatiquement les actualisations en cas d'initialisation automatique, en fournissant régulièrement le FID actuel correct à votre rappel onRegistered() lors des synchronisations de routine au démarrage de l'application.

Pour que votre serveur reste à jour, implémentez les stratégies d'importation au démarrage décrites dans Récupérer et stocker les ID d'installation Firebase :

  • L'initialisation automatique est activée : le SDK s'assure automatiquement que le dernier FID est envoyé à votre serveur lors des synchronisations de routine au démarrage de l'application.
  • L'initialisation automatique est désactivée ou non prise en charge : appelez register() au démarrage de l'application (par exemple, sur Android, dans onCreate() de l'activité principale) pour forcer la séquence d'enregistrement et déclencher la remise du FID à votre rappel onRegistered().

Ces stratégies garantissent que votre serveur dispose toujours du dernier FID actif et peut récupérer automatiquement les échecs d'importation, ce qui rend l'application très résiliente.

API de jeton d'enregistrement obsolètes

Si vous utilisez d'anciens jetons d'enregistrement, le SDK client ne gère pas automatiquement les actualisations lors des synchronisations de routine. Par conséquent, nous vous recommandons de récupérer et de mettre à jour régulièrement tous les jetons d'enregistrement sur votre serveur. Pour ce faire :

  • Ajoutez la logique d'application dans votre application cliente pour récupérer le jeton actuel à l'aide de l'appel d'API approprié (par exemple, token(completion): pour les plates-formes Apple ou getToken() pour Android), puis envoyez le jeton actuel à votre serveur d'application pour le stockage (avec un code temporel). Il peut s'agir d'un job mensuel configuré pour couvrir tous les clients ou jetons.
  • Ajoutez une logique de serveur pour mettre à jour le code temporel du jeton à intervalles réguliers, que le jeton ait changé ou non.

Pour obtenir un exemple de logique Android permettant de mettre à jour les anciens jetons à l'aide de WorkManager, consultez Gestion des jetons Cloud Messaging sur le blog Firebase.

Quel que soit le calendrier que vous suivez, veillez à mettre à jour les jetons régulièrement. Une fréquence de mise à jour d'une fois par mois constitue un bon équilibre entre l'impact sur la batterie et la détection des jetons d'enregistrement inactifs. En effectuant cette actualisation, vous vous assurez également que tout appareil qui devient inactif actualisera son enregistrement lorsqu'il redeviendra actif. Il n'est pas utile d'actualiser les données plus d'une fois par semaine.

Supprimer les enregistrements obsolètes

Avant d'envoyer des messages à un appareil, assurez-vous que le code temporel de l'enregistrement de l'appareil se trouve dans la période de validité. Par exemple, vous pouvez implémenter Cloud Functions for Firebase pour exécuter une vérification quotidienne afin de vous assurer que le code temporel se trouve dans une période de fraîcheur définie, telle que const EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30;, puis supprimer les enregistrements obsolètes :

exports.pruneRegistrations = functions.pubsub.schedule('every 24 hours').onRun(async (context) => {
  // Get all documents where the timestamp exceeds is not within the past month
  const staleRegistrationsResult = await admin.firestore().collection('fcmRegistrations')
      .where("timestamp", "<", Date.now() - EXPIRATION_TIME)
      .get();
  // Delete devices with stale registrations
  staleRegistrationsResult.forEach(function(doc) { doc.ref.delete(); });
});
exports.pruneTokens = functions.pubsub.schedule('every 24 hours').onRun(async (context) => { // Get all documents where the timestamp exceeds is not within the past month const staleTokensResult = await admin.firestore().collection('fcmTokens') .where("timestamp", "<", Date.now() - EXPIRATION_TIME) .get(); // Delete devices with stale tokens staleTokensResult.forEach(function(doc) { doc.ref.delete(); }); });

Se désabonner des enregistrements obsolètes des thèmes

Si vous utilisez des thèmes, vous pouvez également vous désabonner des enregistrements obsolètes des thèmes auxquels ils sont abonnés. Ce processus implique deux étapes :

  1. Votre application doit se réabonner aux thèmes chaque fois que l'ID d'installation Firebase (FID) change. Cela permet aux abonnements de réapparaître automatiquement lorsqu'une application redevient active.
  2. Si une instance d'application est inactive pendant un mois (ou votre propre période d'obsolescence), vous devez la désabonner des thèmes à l'aide du SDK Firebase Admin pour supprimer le mappage entre l'ID d'installation Firebase et le thème du backend FCM.

L'avantage de ces deux étapes est que vos fan-outs se produiront plus rapidement, car il y aura moins d'enregistrements obsolètes à distribuer. De plus, vos instances d'application obsolètes se réabonneront automatiquement une fois qu'elles seront à nouveau actives.

Mesurer la réussite des livraisons

Pour obtenir une vue plus précise de la distribution des messages, il est préférable de n'envoyer des messages qu'aux instances d'application activement utilisées. C'est particulièrement important si vous envoyez régulièrement des messages à des thèmes comptant un grand nombre d'abonnés. Si une partie de ces abonnés sont en fait inactifs, l'impact sur vos statistiques de distribution peut être important au fil du temps.

Avant de cibler des messages sur une instance d'application, tenez compte des points suivants :

  • Les données Google Analytics, les données capturées dans BigQuery ou d'autres signaux de suivi indiquent-ils que l'enregistrement est actif ?
  • Les précédentes tentatives de livraison ont-elles échoué de manière répétée sur une période donnée ?
  • L'ID d'installation Firebase a-t-il été mis à jour sur vos serveurs au cours du mois dernier ?
  • Pour les appareils Android, l'API FCM Data signale-t-elle un pourcentage élevé d'échecs de distribution des messages en raison de droppedDeviceInactive ?

Pour en savoir plus sur la distribution, consultez Comprendre la distribution des messages.