Meilleures pratiques pour la gestion des jetons d'enregistrement FCM

Si vous utilisez les API FCM pour créer des requêtes d'envoi par programmation, vous constaterez peut-être qu'au fil du temps, vous gaspillez des ressources en envoyant des messages à des appareils inactifs avec des jetons d'enregistrement obsolètes. Cette situation peut affecter les données de livraison des messages signalées dans la console Firebase ou les données exportées vers BigQuery, se traduisant par une baisse spectaculaire (mais pas réellement valide) des taux de livraison. Ce guide présente certaines mesures que vous pouvez prendre pour garantir un ciblage efficace des messages et des rapports de diffusion valides.

Bonnes pratiques de base

Il existe certaines pratiques fondamentales que vous devez suivre dans toute application qui utilise les API FCM pour créer des requêtes d'envoi par programmation. Les principales bonnes pratiques sont :

  • Stockez les jetons d'enregistrement sur votre serveur. Un rôle important du serveur est de garder une trace du jeton de chaque client et de conserver une liste à jour des jetons actifs. Nous vous recommandons fortement d'implémenter un horodatage de jeton dans votre code et vos serveurs, et de mettre à jour cet horodatage à intervalles réguliers.
  • Supprimez les jetons stockés qui deviennent obsolètes . En plus de supprimer les jetons dans les cas évidents de réponses de jeton non valides, vous devrez probablement surveiller d'autres signes indiquant que le jeton est obsolète. Ce guide présente certaines de vos options pour y parvenir.

Récupérer et stocker les jetons d'enregistrement

Au démarrage initial de votre application, le SDK FCM génère un jeton d'enregistrement pour l'instance d'application client. Il s'agit du jeton que vous devez inclure dans les demandes d'envoi ciblé de l'API ou ajouter aux abonnements aux sujets pour cibler les sujets.

Comme indiqué dans nos guides de configuration client, votre application doit récupérer ce jeton au démarrage initial et l'enregistrer sur votre serveur d'application avec un horodatage . Cet horodatage doit être implémenté par votre code et vos serveurs, car il ne vous est pas fourni par les SDK FCM.

En outre, il est important d'enregistrer le jeton sur le serveur et de mettre à jour l'horodatage chaque fois qu'il change, par exemple lorsque :

  • L'application est restaurée sur un nouvel appareil
  • L'utilisateur désinstalle/réinstalle l'application
  • L'utilisateur efface les données de l'application.

Exemple : stocker les jetons et les horodatages dans Cloud Firestore

Par exemple, vous pouvez utiliser Cloud Firestore pour stocker les jetons dans une collection appelée fcmTokens . Chaque ID de document de la collection correspond à un ID utilisateur et le document stocke le jeton d'enregistrement actuel et son horodatage de la dernière mise à jour. Utilisez la fonction set comme indiqué dans cet exemple Kotlin :

    /**
     * Persist token to third-party servers.
     *
     * Modify this method to associate the user's FCM registration token with any server-side account
     * maintained by your application.
     *
     * @param token The new token.
     */
    private fun sendTokenToServer(token: String?) {
        // If you're running your own server, call API to send token and today's date for the user

        // Example shown below with Firestore
        // Add token and timestamp to Firestore for this user
        val deviceToken = hashMapOf(
            "token" to token,
            "timestamp" to FieldValue.serverTimestamp(),
        )
        // Get user ID from Firebase Auth or your own server
        Firebase.firestore.collection("fcmTokens").document("myuserid")
            .set(deviceToken)
    }

Chaque fois qu'un jeton est récupéré, il est stocké dans Cloud Firestore en appelant sendTokenToServer :

    /**
     * Called if the FCM registration token is updated. This may occur if the security of
     * the previous token had been compromised. Note that this is called when the
     * FCM registration token is initially generated so this is where you would retrieve the token.
     */
    override fun onNewToken(token: String) {
        Log.d(TAG, "Refreshed token: $token")

        // If you want to send messages to this application instance or
        // manage this apps subscriptions on the server side, send the
        // FCM registration token to your app server.
        sendTokenToServer(token)
    }
        var token = Firebase.messaging.token.await()

        // Check whether the retrieved token matches the one on your server for this user's device
        val preferences = this.getPreferences(Context.MODE_PRIVATE)
        val tokenStored = preferences.getString("deviceToken", "")
        lifecycleScope.launch {
            if (tokenStored == "" || tokenStored != token)
            {
                // If you have your own server, call API to send the above token and Date() for this user's device

                // Example shown below with Firestore
                // Add token and timestamp to Firestore for this user
                val deviceToken = hashMapOf(
                    "token" to token,
                    "timestamp" to FieldValue.serverTimestamp(),
                )

                // Get user ID from Firebase Auth or your own server
                Firebase.firestore.collection("fcmTokens").document("myuserid")
                    .set(deviceToken).await()
            }
        }

Détecter les réponses de jeton non valides à partir du backend FCM

Assurez-vous de détecter les réponses de jetons non valides de FCM et répondez en supprimant de votre système tous les jetons d'enregistrement connus pour être non valides. Avec l'API HTTP v1, ces messages d'erreur peuvent indiquer que votre demande d'envoi ciblait des jetons obsolètes ou non valides :

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

Notez que, étant donné que INVALID_ARGUMENT est également lancé en cas de problèmes dans la charge utile du message, il signale un jeton non valide uniquement si la charge utile est complètement valide. Voir Codes d'erreur pour plus d'informations.

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

    // Registration token comes from the client FCM SDKs
    const registrationToken = 'YOUR_REGISTRATION_TOKEN';

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

    // Send message to device with provided registration token
    getMessaging().send(message)
    .then((response) => {
        // Response is a message ID string.
    })
    .catch((error) => {
        // Delete token for user if error code is UNREGISTERED or INVALID_ARGUMENT.
        if (errorCode == "messaging/registration-token-not-registered") {
            // If you're running your own server, call API to delete the token for the user

            // Example shown below with Firestore
            // Get user ID from Firebase Auth or your own server
            Firebase.firestore.collection("fcmTokens").document(user.uid).delete()
        }
    });

Assurer la fraîcheur des jetons d’enregistrement

Déterminer si un jeton est récent ou obsolète n’est pas toujours simple. Pour couvrir tous les cas, vous devez adopter un seuil à partir duquel vous considérez les jetons comme obsolètes ; notre recommandation est de deux mois. Tout jeton datant de plus de deux mois est susceptible d’être un appareil inactif ; un appareil actif aurait autrement actualisé son jeton.

Mettre à jour les jetons régulièrement

Nous vous recommandons de récupérer et de mettre à jour périodiquement tous les jetons d'enregistrement sur votre serveur. Cela vous oblige à :

  • Ajoutez une logique d'application dans votre application client pour récupérer le jeton actuel à l'aide de l'appel API approprié (tel que token(completion): pour les plates-formes Apple ou getToken() pour Android), puis envoyez le jeton actuel à votre serveur d'application pour le stockage (avec horodatage ). Il peut s'agir d'une tâche mensuelle configurée pour couvrir tous les clients/jetons.
  • Ajoutez une logique de serveur pour mettre à jour l'horodatage du jeton à intervalles réguliers, que le jeton ait changé ou non.

Pour un exemple de logique Android pour la mise à jour des jetons à l'aide de WorkManager , consultez Gestion des jetons de messagerie cloud sur le blog Firebase.

Quel que soit le modèle de timing que vous suivez, assurez-vous de mettre à jour les jetons périodiquement. Une fréquence de mise à jour d'une fois tous les deux mois offre probablement un bon équilibre entre l'impact de la batterie et la détection des jetons d'enregistrement inactifs. En effectuant cette actualisation, vous vous assurez également que tout appareil devenu inactif actualisera son enregistrement lorsqu'il redeviendra actif. Il n’y a aucun avantage à effectuer l’actualisation plus fréquemment qu’une fois par semaine.

Supprimer les jetons d'enregistrement obsolètes

Avant d'envoyer des messages à un appareil, assurez-vous que l'horodatage du jeton d'enregistrement de l'appareil se situe dans la période de votre fenêtre d'obsolescence. Par exemple, vous pouvez implémenter Cloud Functions pour Firebase pour exécuter une vérification quotidienne afin de garantir que l'horodatage se situe dans une période de fenêtre d'obsolescence définie, telle que const EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 60; puis supprimez les jetons obsolètes :

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 jetons obsolètes des sujets

La gestion des abonnements aux sujets pour supprimer les jetons d’enregistrement obsolètes est une autre considération. Cela implique deux étapes :

  1. Votre application doit se réabonner aux sujets une fois par mois et/ou chaque fois que le jeton d'inscription change. Cela constitue une solution d'auto-réparation, dans laquelle les abonnements réapparaissent automatiquement lorsqu'une application redevient active.
  2. Si une instance d'application est inactive pendant 2 mois (ou pendant votre propre fenêtre d'obsolescence), vous devez la désabonner des sujets à l'aide du SDK d'administration Firebase pour supprimer le mappage jeton/sujet du backend FCM.

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

Mesurer le succès de la livraison

En règle générale, nous conseillons de cibler les messages en fonction des actions observées ou capturées à partir des instances d'application activement utilisées. Ceci est particulièrement important si vous envoyez régulièrement des messages sur des sujets comptant un grand nombre d'abonnés ; si une partie de ces abonnés est réellement inactive, l’impact sur vos statistiques de diffusion peut être important au fil du temps.

Avant de cibler les messages sur un jeton, considérez :

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

Pour plus d'informations sur la remise, consultez Comprendre la remise des messages .