Práticas recomendadas de gerenciamento do token de registro do FCM

Se você usa as APIs do FCM para criar solicitações de envio de maneira programática, poderá perceber que, com o tempo, você está desperdiçando recursos enviando mensagens para dispositivos inativos com tokens de registro desatualizados. Essa situação pode afetar os dados de entrega de mensagens informadas no Console do Firebase ou os dados exportados para o BigQuery, aparecendo como uma queda drástica (mas não válida) nas taxas de entrega. Veja neste guia algumas medidas que você pode tomar para ajudar a garantir uma segmentação de mensagens eficiente e relatórios de entrega válidos.

Tokens de registro desatualizados e expirados

Tokens de registro desatualizados são tokens associados a dispositivos inativos que não se conectam ao FCM há mais de um mês. Com o passar do tempo, torna-se menos provável que o dispositivo se conecte ao FCM novamente. É improvável que os envios de mensagens e os fanouts de tópicos para esses tokens desatualizados sejam entregues.

Há vários motivos para um token ficar desatualizado. Por exemplo, o dispositivo ao qual o token está associado pode ser perdido, destruído ou guardado e esquecido.

Quando os tokens desatualizados atingirem 270 dias de inatividade, eles serão considerados pelo FCM como tokens expirados. Quando um token expira, o FCM o marca como inválido e rejeita envios para ele. No entanto, o FCM emite um novo token para a instância do app no caso raro de o dispositivo se conectar novamente e o app ser aberto.

Práticas recomendadas

Há algumas práticas fundamentais que devem ser seguidas em qualquer aplicativo que use as APIs do FCM para criar solicitações de envio de maneira programática. As principais práticas recomendadas são:

  • Recupere tokens de registro do FCM e armazene-os no seu servidor. Um papel importante do servidor é controlar o token de cada cliente e manter uma lista atualizada de tokens ativos. Recomendamos implementar um carimbo de data/hora do token no código e nos servidores, além de atualizar esse carimbo em intervalos regulares.
  • Mantenha a atualidade dos tokens e remova os desatualizados. Além de remover tokens que o FCM não considera mais válidos, é possível monitorar outros sinais de que os tokens estão desatualizados e removê-los proativamente. Veja neste guia algumas das opções para conseguir fazer isso.

Recuperar e armazenar tokens de registro

Na primeira inicialização do app, o SDK do FCM gera um token de registro para a instância do app cliente. Esse é o token que você precisa incluir nas solicitações de envio segmentadas da API ou adicionar a assinaturas de tópicos para segmentar tópicos.

Recomendamos que o app recupere esse token na inicialização inicial e o salve no servidor do app junto com um carimbo de data/hora. Esse carimbo de data/hora precisa ser implementado pelo código e pelos servidores, já que não é fornecido pelos SDKs do FCM.

Além disso, é importante salvar o token no servidor e atualizar o carimbo de data/hora sempre que ele for alterado, como quando:

  • o app é restaurado em um novo dispositivo;
  • o usuário desinstala ou reinstala o app;
  • o usuário limpa os dados do app;
  • o app é ativado novamente depois que FCM expira o token atual.

Exemplo: armazenar tokens e carimbos de data/hora no Cloud Firestore

Por exemplo, é possível usar o Cloud Firestore para armazenar tokens em uma coleção chamada fcmTokens. Cada código de documento na coleção corresponde a um ID do usuário, e o documento armazena o token de registro atual e o carimbo de data/hora da última atualização dele. Use a função set, como mostrado neste exemplo em 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)
    }

Sempre que um token é recuperado, ele é armazenado no Cloud Firestore chamando 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()
            }
        }

Mantenha a atualidade dos tokens e remova os desatualizados

Determinar se um token está atualizado ou desatualizado nem sempre é simples. Para abranger todos os casos, você precisa adotar um limite para considerar que os tokens estão desatualizados. Por padrão, o FCM considera que um token está desatualizado se a instância do app não estiver conectada há um mês. Qualquer token com mais de um mês provavelmente será de um dispositivo inativo. Um dispositivo ativo teria atualizado seu token.

Dependendo do caso de uso, um mês pode ser muito curto ou muito longo, então você deve determinar o critério que funciona no seu caso.

Detectar respostas de token inválidas no back-end do FCM

Lembre-se de detectar respostas de token inválidas do FCM e responder excluindo do seu sistema os tokens de registro conhecidos como inválidos ou que expiraram. Com a API HTTP v1, essas mensagens de erro podem indicar que a solicitação de envio segmenta tokens inválidos ou expirados:

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

Se você tiver certeza de que o payload da mensagem é válido e receber uma dessas respostas para um token de destino, é seguro excluir o registro desse token, porque ele nunca mais será válido. Por exemplo, para excluir tokens inválidos do Cloud Firestore, implante e execute uma função como esta:

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

o FCM só retorna uma resposta de token inválido se um token tiver expirado após 270 dias ou se um cliente explicitamente cancelar a inscrição. Se você precisar acompanhar com precisão a inatividade de acordo com suas próprias definições, você pode remover de maneira proativa os tokens de registro desatualizados.

Atualizar tokens regularmente

Recomendamos que você recupere e atualize periodicamente todos os tokens de registro no servidor. Para isso:

  • Adicione uma lógica de aplicativo no aplicativo cliente para recuperar o token atual usando a chamada de API apropriada (como token(completion): para plataformas Apple ou getToken() para Android) e depois envie o token atual para o servidor do aplicativo para armazenamento (com carimbo de data/hora). Isso poderia ser um job mensal configurado para cobrir todos os clientes/tokens.
  • Adicione uma lógica de servidor para atualizar o carimbo de data/hora do token em intervalos regulares, mesmo que o token não tenha sido alterado.

Para ver um exemplo de lógica do Android para atualizar tokens usando o WorkManager, consulte Como gerenciar tokens do Cloud Messaging no blog do Firebase.

Seja qual for o padrão de tempo seguido, não se esqueça de atualizar os tokens periodicamente. Uma frequência de atualização mensal provavelmente estabelece um bom equilíbrio entre o impacto na bateria e a detecção de tokens de registro inativos. Ao fazer essa atualização, você também garante que qualquer dispositivo que fique inativo atualizará o registro quando ficar ativo novamente. Não há benefícios em fazer a atualização mais de uma vez na semana.

Remover tokens de registro desatualizados

Antes de enviar mensagens para um dispositivo, verifique se o carimbo de data/hora do token de registro do dispositivo está dentro do período da janela de inatividade. Por exemplo, é possível implementar o Cloud Functions for Firebase para executar uma verificação diária e garantir que o carimbo de data/hora esteja dentro de um período de inatividade definido, como const EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30;, e, em seguida, remover tokens desatualizados:

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

Cancelar a inscrição de tokens desatualizados nos tópicos

Se você usa tópicos, também é recomendável cancelar a inscrição de tokens desatualizados dos tópicos em que eles estão inscritos. Isso requer duas etapas:

  1. O aplicativo precisa assinar novamente os tópicos uma vez por mês e/ou sempre que o token de registro mudar. Isso forma uma solução de autocorreção, em que as assinaturas reaparecem automaticamente quando um app é ativado novamente.
  2. Se uma instância de aplicativo ficar inativa por um mês (ou por sua própria janela de inatividade), você deve cancelar a inscrição nos tópicos usando o SDK Admin do Firebase para excluir o mapeamento de token para tópico do back-end do FCM.

A vantagem dessas duas etapas é que os fanouts ocorrerão mais rapidamente porque há menos tokens desatualizados para distribuir, e as instâncias dos aplicativos desatualizados assinarão novamente automaticamente quando estiverem ativas.

Medir o sucesso da entrega

Para se ter uma ideia mais precisa do envio de mensagens, é melhor enviar mensagens apenas para instâncias de apps usadas ativamente. Isso é especialmente importante se você costuma enviar mensagens para tópicos com um grande número de inscritos. Se parte deles estiver inativa, o impacto nas estatísticas da entrega poderá ser significativo ao longo do tempo.

Antes de segmentar mensagens para um token, considere:

  • o Google Analytics, os dados capturados no BigQuery ou outros sinais de rastreamento indicam que o token está ativo?
  • as tentativas de entrega anteriores falharam de forma consistente durante um período?
  • o token de registro foi atualizado nos servidores no último mês?
  • em dispositivos Android, a API Data do FCM informa uma alta porcentagem de falhas na entrega de mensagens devido a droppedDeviceInactive?

Para mais informações sobre a entrega, consulte Noções básicas sobre a entrega de mensagens.