Google is committed to advancing racial equity for Black communities. See how.
Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Crea presenza in Cloud Firestore

A seconda del tipo di app che stai creando, potresti trovare utile rilevare quali dei tuoi utenti o dispositivi sono attivamente online, altrimenti noto come rilevamento della "presenza".

Ad esempio, se stai creando un'app come un social network o distribuendo una flotta di dispositivi IoT, potresti utilizzare queste informazioni per visualizzare un elenco di amici che sono online e liberi di chattare, oppure ordinare i tuoi dispositivi IoT in base all '"ultima visualizzazione . "

Cloud Firestore non supporta nativamente la presenza, ma puoi sfruttare altri prodotti Firebase per creare un sistema di presenza.

Soluzione: Cloud Functions con Realtime Database

Per connettere Cloud Firestore alla funzione di presenza nativa di Firebase Realtime Database, utilizza Cloud Functions.

Utilizza Realtime Database per segnalare lo stato della connessione, quindi utilizza Cloud Functions per eseguire il mirroring di tali dati in Cloud Firestore.

Utilizzo della presenza nel database in tempo reale

Innanzitutto, considera come funziona un sistema di presenza tradizionale in Realtime Database.

ragnatela

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

Questo esempio è un sistema completo di presenza del database in tempo reale. Gestisce più disconnessioni, arresti anomali e così via.

Connessione a Cloud Firestore

Per implementare una soluzione simile in Cloud Firestore, utilizza lo stesso codice di Realtime Database, quindi utilizza Cloud Functions per mantenere sincronizzati Realtime Database e Cloud Firestore.

Se non lo hai già fatto, aggiungi Realtime Database al tuo progetto e includi la soluzione di presenza di cui sopra.

Successivamente sincronizzerai lo stato di presenza su Cloud Firestore tramite i seguenti metodi:

  1. A livello locale, nella cache Cloud Firestore del dispositivo offline in modo che l'app sappia che è offline.
  2. A livello globale, utilizzando una funzione cloud in modo che tutti gli altri dispositivi che accedono a Cloud Firestore sappiano che questo dispositivo specifico è offline.

Aggiornamento della cache locale di Cloud Firestore

Diamo un'occhiata alle modifiche necessarie per soddisfare il primo problema: l'aggiornamento della cache locale di Cloud Firestore.

ragnatela

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

Con queste modifiche ora ci siamo assicurati che lo stato locale di Cloud Firestore rifletta sempre lo stato online / offline del dispositivo. Ciò significa che puoi ascoltare il documento /status/{uid} e utilizzare i dati per modificare la tua interfaccia utente in modo da riflettere lo stato della connessione.

ragnatela

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

Aggiornamento di Cloud Firestore a livello globale

Sebbene la nostra applicazione segnali correttamente la presenza online a se stessa, questo stato non sarà ancora preciso in altre app Cloud Firestore perché la nostra scrittura dello stato "offline" è solo locale e non verrà sincronizzata quando viene ripristinata una connessione. Per contrastare questo problema, utilizzeremo una funzione cloud che controlla il percorso status/{uid} nel database in tempo reale. Quando il valore del database in tempo reale cambia, il valore verrà sincronizzato con Cloud Firestore in modo che gli stati di tutti gli utenti siano corretti.

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* Cloud 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();
      console.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);
    });

Una volta distribuita questa funzione, avrai un sistema di presenza completo in esecuzione con Cloud Firestore. Di seguito è riportato un esempio di monitoraggio per tutti gli utenti che sono online o offline utilizzando una query where() .

ragnatela

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

Limitazioni

L'utilizzo di Realtime Database per aggiungere presenza alla tua app Cloud Firestore è scalabile ed efficace ma presenta alcune limitazioni:

  • Antirimbalzo : durante l'ascolto di modifiche in tempo reale in Cloud Firestore, questa soluzione è probabile che attivi più modifiche. Se queste modifiche attivano più eventi di quanti desideri, annulla manualmente gli eventi di Cloud Firestore.
  • Connettività : questa implementazione misura la connettività a Realtime Database, non a Cloud Firestore. Se lo stato della connessione a ogni database non è lo stesso, questa soluzione potrebbe segnalare uno stato di presenza non corretto.
  • Android : su Android, il database in tempo reale si disconnette dal back-end dopo 60 secondi di inattività. Inattività significa assenza di listener aperti o operazioni in sospeso. Per mantenere la connessione aperta, ti consigliamo di aggiungere un listener di eventi di valore a un percorso oltre a .info/connected . Ad esempio potresti fare FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() all'inizio di ogni sessione. Per ulteriori informazioni, vedere Rilevamento dello stato di connessione .