Buduj obecność w Cloud Firestore

W zależności od typu tworzonej aplikacji przydatne może być wykrycie, którzy użytkownicy lub urządzenia są aktywnie online — inaczej nazywa się to wykrywaniem „obecności”.

Na przykład, jeśli tworzysz aplikację taką jak sieć społecznościowa lub wdrażasz flotę urządzeń IoT, możesz użyć tych informacji, aby wyświetlić listę znajomych, którzy są online i mogą swobodnie rozmawiać, lub posortować urządzenia IoT według „ostatnio widzianych” .”

Cloud Firestore nie obsługuje natywnie obecności, ale możesz wykorzystać inne produkty Firebase do zbudowania systemu obecności.

Rozwiązanie: Funkcje chmury z bazą danych czasu rzeczywistego

Aby połączyć Cloud Firestore z natywną funkcją obecności Firebase Realtime Database, użyj Cloud Functions.

Użyj bazy danych czasu rzeczywistego, aby zgłosić stan połączenia, a następnie użyj Cloud Functions, aby odzwierciedlić te dane w Cloud Firestore.

Korzystanie z obecności w bazie danych czasu rzeczywistego

Najpierw zastanów się, jak działa tradycyjny system obecności w Realtime Database.

Sieć

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

Ten przykład to kompletny system obecności bazy danych w czasie rzeczywistym. Obsługuje wielokrotne rozłączenia, awarie i tak dalej.

Łączenie z Cloud Firestore

Aby wdrożyć podobne rozwiązanie w Cloud Firestore, użyj tego samego kodu bazy danych Realtime Database, a następnie użyj Cloud Functions, aby zsynchronizować bazę danych Realtime i Cloud Firestore.

Jeśli jeszcze tego nie zrobiłeś, dodaj bazę danych czasu rzeczywistego do swojego projektu i dołącz powyższe rozwiązanie obecności.

Następnie zsynchronizujesz stan obecności z Cloud Firestore za pomocą następujących metod:

  1. Lokalnie, do pamięci podręcznej Cloud Firestore urządzenia offline, aby aplikacja wiedziała, że ​​jest w trybie offline.
  2. Globalnie, korzystanie z funkcji chmury, aby wszystkie inne urządzenia uzyskujące dostęp do Cloud Firestore wiedziały, że to konkretne urządzenie jest offline.

Aktualizowanie lokalnej pamięci podręcznej Cloud Firestore

Przyjrzyjmy się zmianom wymaganym do spełnienia pierwszego problemu - aktualizacji lokalnej pamięci podręcznej Cloud Firestore.

Sieć

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

Dzięki tym zmianom zapewniliśmy, że stan lokalny Cloud Firestore będzie zawsze odzwierciedlał stan online/offline urządzenia. Oznacza to, że możesz odsłuchać dokument /status/{uid} i użyć danych do zmiany interfejsu użytkownika w celu odzwierciedlenia stanu połączenia.

Sieć

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

Globalna aktualizacja Cloud Firestore

Chociaż nasza aplikacja poprawnie zgłasza sobie obecność online, ten status nie będzie jeszcze dokładny w innych aplikacjach Cloud Firestore, ponieważ nasz zapis statusu „offline” odbywa się tylko lokalnie i nie zostanie zsynchronizowany po przywróceniu połączenia. Aby temu przeciwdziałać, użyjemy funkcji chmury, która obserwuje ścieżkę status/{uid} w bazie danych czasu rzeczywistego. Kiedy wartość bazy danych czasu rzeczywistego ulegnie zmianie, wartość zostanie zsynchronizowana z Cloud Firestore, dzięki czemu statusy wszystkich użytkowników będą prawidłowe.

Node.js

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

Po wdrożeniu tej funkcji będziesz mieć kompletny system obecności działający w Cloud Firestore. Poniżej znajduje się przykład monitorowania użytkowników, którzy wchodzą do trybu online lub przechodzą do trybu offline za pomocą zapytania where() .

Sieć

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

Ograniczenia

Korzystanie z bazy danych czasu rzeczywistego w celu dodania obecności do aplikacji Cloud Firestore jest skalowalne i skuteczne, ale ma pewne ograniczenia:

  • Odbijanie — podczas słuchania zmian w czasie rzeczywistym w Cloud Firestore to rozwiązanie prawdopodobnie wywoła wiele zmian. Jeśli te zmiany wywołają więcej zdarzeń, niż chcesz, ręcznie odrzuć zdarzenia Cloud Firestore.
  • Łączność — ta implementacja mierzy łączność z bazą danych Realtime, a nie z Cloud Firestore. Jeśli stan połączenia z każdą bazą danych nie jest taki sam, to rozwiązanie może zgłosić nieprawidłowy stan obecności.
  • Android — w systemie Android baza danych czasu rzeczywistego rozłącza się z backendem po 60 sekundach bezczynności. Brak aktywności oznacza brak otwartych słuchaczy i oczekujących operacji. Aby połączenie pozostało otwarte, zalecamy dodanie detektora zdarzeń wartości do ścieżki innej niż .info/connected . Na przykład możesz wykonać FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() na początku każdej sesji. Aby uzyskać więcej informacji, zobacz temat Wykrywanie stanu połączenia .