Расширьте присутствие в 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 база данных Realtime отключается от серверной части после 60 секунд бездействия. Неактивность означает отсутствие открытых прослушивателей или ожидающих операций. Чтобы соединение оставалось открытым, мы рекомендуем добавить прослушиватель событий значения к пути, кроме .info/connected . Например, вы можете выполнить FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() в начале каждого сеанса. Дополнительные сведения см. в разделе «Определение состояния соединения» .