Расширьте присутствие в Cloud Firestore

В зависимости от типа приложения, которое вы создаете, вам может оказаться полезным определить, какие из ваших пользователей или устройств активно находятся в сети — иначе это называется обнаружением «присутствия».

Например, если вы создаете приложение, такое как социальная сеть, или развертываете парк устройств IoT, вы можете использовать эту информацию для отображения списка друзей, которые находятся в сети и могут свободно общаться в чате, или отсортировать свои устройства IoT по «последнему посещению». ."

Cloud Firestore изначально не поддерживает присутствие, но вы можете использовать другие продукты Firebase для создания системы присутствия.

Решение: облачные функции с базой данных реального времени

Чтобы подключить Cloud Firestore к встроенной функции присутствия базы данных Firebase Realtime, используйте Cloud Functions.

Используйте базу данных реального времени, чтобы сообщить о состоянии соединения, а затем используйте облачные функции для зеркалирования этих данных в Cloud Firestore.

Использование присутствия в базе данных реального времени

Сначала рассмотрим, как работает традиционная система присутствия в базе данных реального времени.

Интернет

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

Этот пример представляет собой полную систему присутствия базы данных реального времени. Он обрабатывает множественные отключения, сбои и так далее.

Подключение к Cloud Firestore

Чтобы реализовать аналогичное решение в Cloud Firestore, используйте тот же код базы данных реального времени, а затем используйте облачные функции для синхронизации базы данных реального времени и Cloud Firestore.

Если вы еще этого не сделали, добавьте базу данных реального времени в свой проект и включите вышеуказанное решение присутствия.

Далее вы синхронизируете состояние присутствия с Cloud Firestore следующими способами:

  1. Локально, в кеш Cloud Firestore автономного устройства, чтобы приложение знало, что оно отключено.
  2. В глобальном масштабе используется облачная функция, чтобы все другие устройства, имеющие доступ к Cloud Firestore, знали, что это конкретное устройство находится в автономном режиме.

Обновление локального кеша Cloud Firestore

Давайте посмотрим на изменения, необходимые для решения первой задачи — обновления локального кэша Cloud Firestore.

Интернет

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

Благодаря этим изменениям мы теперь добились того, что локальное состояние Cloud Firestore всегда будет отражать онлайн-/оффлайн-статус устройства. Это означает, что вы можете прослушать документ /status/{uid} и использовать данные для изменения пользовательского интерфейса, чтобы он отражал состояние соединения.

Интернет

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

Глобальное обновление Cloud Firestore

Хотя наше приложение правильно сообщает себе о присутствии в сети, этот статус пока не будет точным в других приложениях Cloud Firestore, поскольку наша запись статуса «офлайн» является только локальной и не будет синхронизироваться при восстановлении соединения. Чтобы противостоять этому, мы будем использовать облачную функцию, которая отслеживает путь status/{uid} в базе данных реального времени. Когда значение базы данных реального времени изменится, оно будет синхронизировано с Cloud Firestore, чтобы статусы всех пользователей были правильными.

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

После развертывания этой функции у вас будет полноценная система присутствия, работающая с Cloud Firestore. Ниже приведен пример мониторинга всех пользователей, которые подключаются к сети или выходят из сети с помощью запросаwhere where() .

Интернет

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

Ограничения

Использование базы данных реального времени для добавления присутствия в ваше приложение Cloud Firestore масштабируемо и эффективно, но имеет некоторые ограничения:

  • Устранение дребезга — при прослушивании изменений в реальном времени в Cloud Firestore это решение может вызвать несколько изменений. Если эти изменения вызывают больше событий, чем вы хотите, вручную отключите события Cloud Firestore.
  • Возможность подключения . Эта реализация измеряет подключение к базе данных реального времени, а не к Cloud Firestore. Если статус подключения к каждой базе данных не одинаков, это решение может сообщить о неправильном состоянии присутствия.
  • Android — на Android база данных реального времени отключается от серверной части после 60 секунд бездействия. Неактивность означает отсутствие открытых прослушивателей или ожидающих операций. Чтобы соединение оставалось открытым, мы рекомендуем добавить прослушиватель событий значения к пути, кроме .info/connected . Например, вы можете выполнить FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() в начале каждого сеанса. Дополнительную информацию см. в разделе «Определение состояния соединения» .