Bonnes pratiques pour la gestion des enregistrements FCM

Si vous utilisez FCM API 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 signalées dans la console Firebase ou les données exportées vers BigQuery, ce qui se traduit par une baisse spectaculaire (mais en réalité non valide) des taux de distribution. Ce guide présente certaines mesures que vous pouvez prendre pour garantir un ciblage efficace des messages et des rapports de distribution 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 est de moins en moins probable que l'appareil se connecte à nouveau à FCM Les envois de messages et les diffusions de sujets pour ces enregistrements obsolètes ne seront probablement jamais distribués.

Un enregistrement peut devenir obsolète pour plusieurs raisons. 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 qu'un enregistrement a expiré, FCM le marque comme non valide et refuse les envois. Notez que les ID d'installation Firebase (FID) sont gérés par le service d'installations Firebase (FIS), et non par FCM. Dans le cas rare où un appareil se reconnecte et où l'application est ouverte après la suppression de son enregistrement, l'application cliente s'enregistre à nouveau auprès de FCM à l'aide du FID récupéré à partir du 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 de manière proactive la fraîcheur des enregistrements et de supprimer les enregistrements obsolètes.

Bonnes pratiques de base

Vous devez suivre certaines pratiques fondamentales dans toute application qui utilise FCM API 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 (FID) à partir de FCM et stockez-les sur le serveur de votre application. Le serveur a pour rôle important 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 horodatage 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 les enregistrements 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 présente certaines options pour y parvenir.

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

Au démarrage initial de votre application, le FCM SDK enregistre l'instance d'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ées de l'API ou utiliser pour les abonnements à des sujets.

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

Selon que l'initialisation automatique est activée ou désactivée (y compris si elle n'est pas compatible), 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 onRegistered() rappel est régulièrement appelé lors des synchronisations de routine au démarrage de l'application, ainsi que lorsque des modifications de FID se produisent. Il vous suffit d'implémenter ce rappel pour importer le FID sur votre serveur et enregistrer l'horodatage actuel.
  • Lorsque l'initialisation automatique est désactivée : le onRegistered() rappel n'est pas automatiquement appelé au démarrage. Pour suivre les enregistrements et les maintenir à jour, appelez register() au démarrage de l'application. Par exemple, sur Android, dans onCreate() de l'activité principale. Un appel réussi déclenche le FCM processus d'enregistrement à l'aide du FID et le transmet à votre onRegistered() rappel, ce qui permet à votre application d'importer le FID et de mettre à jour l'horodatage sur votre serveur.

Exemple : stocker les FID et les horodatages dans Cloud Firestore

Par exemple, vous pouvez utiliser Cloud Firestore pour stocker les FID dans une collection appelée fcmRegistrations. Chaque ID de document de la collection correspond à un ID utilisateur, et le document stocke le FID actuel et son horodatage 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 onRegistered() rappel est appelé. Vous devez implémenter ce rappel pour importer le FID et mettre à jour l'horodatage :

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 distribution 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 à partir duquel vous considérez les enregistrements 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 est susceptible d'être un appareil inactif. 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 FCM backend

Veillez à détecter les réponses non valides de FCM et à y 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 requête 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 votre 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 explicitement désinscrit. 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 enregistrements

Que vos enregistrements soient basés sur des FID ou des anciens jetons d'enregistrement, votre serveur doit toujours mettre à jour l'horodatage d'enregistrement dans votre base de données pour chaque requête d'importation. Cet horodatage sert de signal pour l'installation de l'application, indiquant que le client a ouvert l'application et s'est synchronisé avec le FCM backend. Selon les 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 lors de l'initialisation automatique, en fournissant régulièrement le FID actuel correct à votre onRegistered() rappel lors des synchronisations de routine au démarrage de l'application.

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

  • Initialisation automatique 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.
  • Initialisation automatique désactivée ou non compatible : appelez register() au démarrage de l'application (par exemple, sur Android, dans onCreate()) pour forcer la séquence d'enregistrement et déclencher la distribution du FID à votre onRegistered() rappel.

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

Les API de jeton d'enregistrement obsolètes

Si vous utilisez des 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 cela, vous devez :

  • Ajouter une logique d'application dans votre application cliente pour récupérer le jeton actuel à l'aide de l' appel d'API approprié (tel que token(completion): pour les plates-formes Apple ou getToken() pour Android), puis envoyer le jeton actuel au serveur de votre application pour le stockage (avec un horodatage). Il peut s'agir d'une tâche mensuelle configurée pour couvrir tous les clients ou jetons.
  • Ajouter une logique de serveur pour mettre à jour l'horodatage 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 Gérer les jetons Cloud Messaging sur le blog Firebase.

Quel que soit le modèle de timing 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 actualise son enregistrement lorsqu'il redevient actif. Il n'est pas utile d'effectuer l'actualisation plus d'une fois par semaine.

Supprimer les enregistrements obsolètes

Avant d'envoyer des messages à un appareil, assurez-vous que l'horodatage de l'enregistrement de l'appareil se trouve dans votre période d'obsolescence. Par exemple, vous pouvez implémenter Cloud Functions for Firebase pour exécuter une vérification quotidienne afin de vous assurer que l' horodatage se trouve dans une période d'obsolescence 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(); }); });

Désabonner les enregistrements obsolètes des sujets

Si vous utilisez des sujets, vous pouvez également désabonner les enregistrements obsolètes des sujets auxquels ils sont abonnés. Cela implique deux étapes :

  1. Votre application doit se réabonner aux sujets 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 sujets à l'aide du SDK Admin Firebase pour supprimer le mappage de l'ID d'installation Firebase vers le sujet du FCM backend.

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

Mesurer la réussite de la distribution

Pour obtenir une image plus précise de la distribution des messages, il est préférable d'envoyer des messages uniquement aux instances d'application activement utilisées. Cela est particulièrement important si vous envoyez régulièrement des messages à des sujets 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 :

  • Google Analytics, les données capturées dans BigQuery ou d'autres signaux de suivi indiquent-ils que l'enregistrement est actif ?
  • Les tentatives de distribution précédentes ont-elles échoué de manière constante 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 de données FCM signale-t-elle un pourcentage élevé d'échecs de distribution de messages en raison de droppedDeviceInactive ?

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