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.

Jetons d'enregistrement périmés et expirés

Les jetons d'enregistrement obsolètes sont des jetons 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 reconnecte au FCM. Il est peu probable que les envois de messages et les diffusions de sujets pour ces jetons obsolètes soient jamais livrés.

Il existe plusieurs raisons pour lesquelles un jeton peut devenir obsolète. Par exemple, l’appareil auquel le jeton est associé peut être perdu, détruit ou stocké et oublié.

Lorsque les jetons périmés atteignent 270 jours d'inactivité, FCM les considérera comme des jetons expirés . Une fois qu'un jeton expire, FCM le marque comme invalide et rejette les envois. Cependant, FCM émet un nouveau jeton pour l'instance d'application dans les rares cas où l'appareil se reconnecte et où l'application est ouverte.

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 :

  • Récupérez les jetons d'enregistrement de FCM et stockez-les 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.
  • Maintenez la fraîcheur des jetons et supprimez les jetons obsolètes. En plus de supprimer les jetons que FCM ne considère plus valides, vous souhaiterez peut-être surveiller d'autres signes indiquant que les jetons sont devenus obsolètes et les supprimer de manière proactive. 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.

Nous recommandons fortement à votre application de récupérer ce jeton au démarrage initial et de 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 ou réinstalle l'application
  • L'utilisateur efface les données de l'application
  • L'application redevient active une fois que FCM a expiré son jeton existant

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()
            }
        }

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

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. Par défaut, FCM considère qu'un jeton est obsolète si son instance d'application ne s'est pas connectée depuis un mois. Tout jeton datant de plus d’un mois est susceptible d’être un appareil inactif ; un appareil actif aurait autrement actualisé son jeton.

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 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 comme non valides ou ayant expiré. Avec l'API HTTP v1, ces messages d'erreur peuvent indiquer que votre demande d'envoi ciblait des jetons invalides 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 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()
        }
    });

FCM ne renverra une réponse de jeton non valide que si un jeton a expiré après 270 jours ou si un client s'est explicitement désenregistré. 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 jetons d'enregistrement obsolètes .

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 plateformes Apple ou getToken() pour Android), puis envoyez le jeton actuel à votre serveur d'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.
  • 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 par mois offre 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 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 * 30; 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

Si vous utilisez des sujets, vous souhaiterez peut-être également désenregistrer les jetons obsolètes des sujets auxquels ils sont abonnés. Cela implique deux étapes :

  1. Votre application doit se réabonner aux sujets une fois par mois et 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 un 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 jeton vers le mappage de 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

Pour obtenir l’image la plus précise de la livraison des messages, il est préférable d’envoyer des messages uniquement aux 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 du mois dernier ?
  • 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 .