Confira tudo que foi anunciado no Firebase Summit e saiba como usar nossa plataforma para acelerar o desenvolvimento de apps e executar os aplicativos com confiança. Saiba mais

Crie presença no Cloud Firestore

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Dependendo do tipo de aplicativo que você está criando, pode ser útil detectar quais de seus usuários ou dispositivos estão ativamente online - também conhecido como detecção de "presença".

Por exemplo, se você estiver criando um aplicativo como uma rede social ou implantando uma frota de dispositivos IoT, poderá usar essas informações para exibir uma lista de amigos que estão online e livres para conversar ou classificar seus dispositivos IoT por "visto pela última vez ."

O Cloud Firestore não oferece suporte nativo à presença, mas você pode aproveitar outros produtos do Firebase para criar um sistema de presença.

Solução: Cloud Functions com Realtime Database

Para conectar o Cloud Firestore ao recurso de presença nativa do Firebase Realtime Database, use o Cloud Functions.

Use o Realtime Database para relatar o status da conexão e use o Cloud Functions para espelhar esses dados no Cloud Firestore.

Como usar a presença no Realtime Database

Primeiro, considere como um sistema de presença tradicional funciona no Realtime Database.

Rede

// Fetch the current user's ID from Firebase Authentication.
var uid = firebase.auth().currentUser.uid;

// Create a reference to this user's specific status node.
// This is where we will store data about being online/offline.
var userStatusDatabaseRef = firebase.database().ref('/status/' + uid);

// We'll create two constants which we will write to 
// the Realtime database when this device is offline
// or online.
var isOfflineForDatabase = {
    state: 'offline',
    last_changed: firebase.database.ServerValue.TIMESTAMP,
};

var isOnlineForDatabase = {
    state: 'online',
    last_changed: firebase.database.ServerValue.TIMESTAMP,
};

// Create a reference to the special '.info/connected' path in 
// Realtime Database. This path returns `true` when connected
// and `false` when disconnected.
firebase.database().ref('.info/connected').on('value', function(snapshot) {
    // If we're not currently connected, don't do anything.
    if (snapshot.val() == false) {
        return;
    };

    // If we are currently connected, then use the 'onDisconnect()' 
    // method to add a set which will only trigger once this 
    // client has disconnected by closing the app, 
    // losing internet, or any other means.
    userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() {
        // The promise returned from .onDisconnect().set() will
        // resolve as soon as the server acknowledges the onDisconnect() 
        // request, NOT once we've actually disconnected:
        // https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect

        // We can now safely set ourselves as 'online' knowing that the
        // server will mark us as offline once we lose connection.
        userStatusDatabaseRef.set(isOnlineForDatabase);
    });
});

Este exemplo é um sistema de presença completo do Realtime Database. Ele lida com várias desconexões, falhas e assim por diante.

Como se conectar ao Cloud Firestore

Para implementar uma solução semelhante no Cloud Firestore, use o mesmo código do Realtime Database e, em seguida, use o Cloud Functions para manter o Realtime Database e o Cloud Firestore sincronizados.

Se você ainda não o fez, adicione o Realtime Database ao seu projeto e inclua a solução de presença acima.

Em seguida, você sincronizará o estado de presença com o Cloud Firestore por meio dos seguintes métodos:

  1. Localmente, para o cache do Cloud Firestore do dispositivo offline para que o aplicativo saiba que está offline.
  2. Globalmente, usando uma Função do Cloud para que todos os outros dispositivos que acessam o Cloud Firestore saibam que esse dispositivo específico está off-line.

Atualizando o cache local do Cloud Firestore

Vamos dar uma olhada nas mudanças necessárias para atender ao primeiro problema - atualizar o cache local do Cloud Firestore.

Rede

// ...
var userStatusFirestoreRef = firebase.firestore().doc('/status/' + uid);

// Firestore uses a different server timestamp value, so we'll 
// create two more constants for Firestore state.
var isOfflineForFirestore = {
    state: 'offline',
    last_changed: firebase.firestore.FieldValue.serverTimestamp(),
};

var isOnlineForFirestore = {
    state: 'online',
    last_changed: firebase.firestore.FieldValue.serverTimestamp(),
};

firebase.database().ref('.info/connected').on('value', function(snapshot) {
    if (snapshot.val() == false) {
        // Instead of simply returning, we'll also set Firestore's state
        // to 'offline'. This ensures that our Firestore cache is aware
        // of the switch to 'offline.'
        userStatusFirestoreRef.set(isOfflineForFirestore);
        return;
    };

    userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() {
        userStatusDatabaseRef.set(isOnlineForDatabase);

        // We'll also add Firestore set here for when we come online.
        userStatusFirestoreRef.set(isOnlineForFirestore);
    });
});

Com essas alterações, garantimos que o estado local do Cloud Firestore sempre refletirá o status online/offline do dispositivo. Isso significa que você pode ouvir o documento /status/{uid} e usar os dados para alterar sua interface do usuário para refletir o status da conexão.

Rede

userStatusFirestoreRef.onSnapshot(function(doc) {
    var isOnline = doc.data().state == 'online';
    // ... use isOnline
});

Como atualizar o Cloud Firestore globalmente

Embora nosso aplicativo relate corretamente a presença on-line para si mesmo, esse status ainda não será preciso em outros aplicativos do Cloud Firestore porque nossa gravação de status "offline" é apenas local e não será sincronizada quando uma conexão for restaurada. Para combater isso, usaremos uma Função do Cloud que observa o caminho status/{uid} no Realtime Database. Quando o valor do Realtime Database for alterado, o valor será sincronizado com o Cloud Firestore para que os status de todos os usuários estejam corretos.

Node.js

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

// Since this code will be running in the Cloud Functions environment
// we call initialize Firestore without any arguments because it
// detects authentication from the environment.
const firestore = admin.firestore();

// Create a new function which is triggered on changes to /status/{uid}
// Note: This is a Realtime Database trigger, *not* Firestore.
exports.onUserStatusChanged = functions.database.ref('/status/{uid}').onUpdate(
    async (change, context) => {
      // Get the data written to Realtime Database
      const eventStatus = change.after.val();

      // Then use other event data to create a reference to the
      // corresponding Firestore document.
      const userStatusFirestoreRef = firestore.doc(`status/${context.params.uid}`);

      // It is likely that the Realtime Database change that triggered
      // this event has already been overwritten by a fast change in
      // online / offline status, so we'll re-read the current data
      // and compare the timestamps.
      const statusSnapshot = await change.after.ref.once('value');
      const status = statusSnapshot.val();
      functions.logger.log(status, eventStatus);
      // If the current timestamp for this data is newer than
      // the data that triggered this event, we exit this function.
      if (status.last_changed > eventStatus.last_changed) {
        return null;
      }

      // Otherwise, we convert the last_changed field to a Date
      eventStatus.last_changed = new Date(eventStatus.last_changed);

      // ... and write it to Firestore.
      return userStatusFirestoreRef.set(eventStatus);
    });

Depois de implantar essa função, você terá um sistema de presença completo em execução com o Cloud Firestore. Abaixo está um exemplo de monitoramento para qualquer usuário que fique online ou offline usando uma consulta where() .

Rede

firebase.firestore().collection('status')
    .where('state', '==', 'online')
    .onSnapshot(function(snapshot) {
        snapshot.docChanges().forEach(function(change) {
            if (change.type === 'added') {
                var msg = 'User ' + change.doc.id + ' is online.';
                console.log(msg);
                // ...
            }
            if (change.type === 'removed') {
                var msg = 'User ' + change.doc.id + ' is offline.';
                console.log(msg);
                // ...
            }
        });
    });

Limitações

Usar o Realtime Database para adicionar presença ao seu aplicativo Cloud Firestore é escalável e eficaz, mas tem algumas limitações:

  • Debouncing - ao ouvir alterações em tempo real no Cloud Firestore, essa solução provavelmente acionará várias alterações. Se essas alterações acionarem mais eventos do que você deseja, debounce manualmente os eventos do Cloud Firestore.
  • Conectividade - esta implementação mede a conectividade com o Realtime Database, não com o Cloud Firestore. Se o status da conexão com cada banco de dados não for o mesmo, esta solução poderá relatar um estado de presença incorreto.
  • Android - no Android, o Realtime Database se desconecta do back-end após 60 segundos de inatividade. Inatividade significa que não há ouvintes abertos ou operações pendentes. Para manter a conexão aberta, recomendamos adicionar um ouvinte de evento de valor a um caminho além de .info/connected . Por exemplo, você pode fazer FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() no início de cada sessão. Para obter mais informações, consulte Detectando o estado da conexão .