Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Renforcez votre présence dans Cloud Firestore

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Selon le type d'application que vous créez, il peut s'avérer utile de détecter lesquels de vos utilisateurs ou appareils sont activement en ligne, ce que l'on appelle la détection de « présence ».

Par exemple, si vous créez une application comme un réseau social ou déployez une flotte d'appareils IoT, vous pouvez utiliser ces informations pour afficher une liste d'amis qui sont en ligne et libres de discuter, ou trier vos appareils IoT par "dernier vu ."

Cloud Firestore ne prend pas en charge la présence de manière native, mais vous pouvez tirer parti d'autres produits Firebase pour créer un système de présence.

Solution : fonctions cloud avec base de données en temps réel

Pour connecter Cloud Firestore à la fonctionnalité de présence native de Firebase Realtime Database, utilisez Cloud Functions.

Utilisez la base de données en temps réel pour signaler l'état de la connexion, puis utilisez Cloud Functions pour mettre en miroir ces données dans Cloud Firestore.

Utilisation de la présence dans la base de données en temps réel

Tout d'abord, considérez le fonctionnement d'un système de présence traditionnel dans la base de données en temps réel.

la toile

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

Cet exemple est un système de présence de base de données en temps réel complet. Il gère les déconnexions multiples, les plantages, etc.

Connexion à Cloud Firestore

Pour implémenter une solution similaire dans Cloud Firestore, utilisez le même code de base de données en temps réel, puis utilisez Cloud Functions pour synchroniser la base de données en temps réel et Cloud Firestore.

Si vous ne l'avez pas déjà fait, ajoutez Realtime Database à votre projet et incluez la solution de présence ci-dessus.

Vous allez ensuite synchroniser l'état de présence avec Cloud Firestore via les méthodes suivantes :

  1. Localement, dans le cache Cloud Firestore de l'appareil hors ligne afin que l'application sache qu'il est hors ligne.
  2. Globalement, utilisez une fonction Cloud pour que tous les autres appareils accédant à Cloud Firestore sachent que cet appareil spécifique est hors ligne.

Mettre à jour le cache local de Cloud Firestore

Examinons les modifications requises pour résoudre le premier problème : mettre à jour le cache local de Cloud Firestore.

la toile

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

Avec ces modifications, nous nous sommes assurés que l'état local de Cloud Firestore reflétera toujours l'état en ligne/hors ligne de l'appareil. Cela signifie que vous pouvez écouter le document /status/{uid} et utiliser les données pour modifier votre interface utilisateur afin de refléter l'état de la connexion.

la toile

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

Mettre à jour Cloud Firestore à l'échelle mondiale

Bien que notre application signale correctement la présence en ligne à elle-même, ce statut ne sera pas encore précis dans les autres applications Cloud Firestore, car notre écriture de statut "hors ligne" est uniquement locale et ne sera pas synchronisée lorsqu'une connexion est restaurée. Pour contrer cela, nous utiliserons une fonction Cloud qui surveille le chemin status/{uid} dans la base de données en temps réel. Lorsque la valeur de la base de données en temps réel change, la valeur est synchronisée avec Cloud Firestore afin que tous les statuts des utilisateurs soient corrects.

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

Une fois que vous aurez déployé cette fonction, vous disposerez d'un système de présence complet fonctionnant avec Cloud Firestore. Vous trouverez ci-dessous un exemple de surveillance de tous les utilisateurs qui se connectent ou se déconnectent à l'aide d'une requête where() .

la toile

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

Limites

L'utilisation de la base de données en temps réel pour ajouter une présence à votre application Cloud Firestore est évolutive et efficace, mais présente certaines limites :

  • Anti -rebond : lors de l'écoute de modifications en temps réel dans Cloud Firestore, cette solution est susceptible de déclencher plusieurs modifications. Si ces modifications déclenchent plus d'événements que vous ne le souhaitez, annulez manuellement les événements Cloud Firestore.
  • Connectivité : cette implémentation mesure la connectivité à la base de données en temps réel, et non à Cloud Firestore. Si l'état de la connexion à chaque base de données n'est pas le même, cette solution peut signaler un état de présence incorrect.
  • Android - sur Android, la base de données en temps réel se déconnecte du backend après 60 secondes d'inactivité. L'inactivité signifie qu'il n'y a pas d'écouteurs ouverts ou d'opérations en attente. Pour garder la connexion ouverte, nous vous recommandons d'ajouter un écouteur d'événement de valeur à un chemin en plus de .info/connected . Par exemple, vous pouvez faire FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() au début de chaque session. Pour plus d'informations, consultez Détection de l'état de la connexion .