בניית נוכחות ב-Cloud Firestore

בהתאם לסוג האפליקציה שאתם מפתחים, יכול להיות שכדאי לזהות אילו מהמשתמשים או המכשירים שלך מחוברים לאינטרנט באופן פעיל - כלומר זיהוי 'נוכחות'.

לדוגמה, אם אתם מפתחים אפליקציה, כמו רשת חברתית או פורסים של מכשירי IoT, תוכלו להשתמש במידע הזה כדי להציג רשימה חברים שמחוברים לצ'אט בחינם, או ממיינים את מכשירי IoT לפי "נראה לאחרונה".

ב-Cloud Firestore אין תמיכה בנוכחות אונליין, אבל אפשר להשתמש במוצרים אחרים של Firebase כדי לבנות מערכת נוכחות.

הפתרון: Cloud Functions עם מסד נתונים בזמן אמת

כדי לחבר את Cloud Firestore לתוכן המקורי של מסד הנתונים בזמן אמת ב-Firebase נוכחות, להשתמש ב-Cloud Functions.

שימוש במסד נתונים בזמן אמת לדיווח על סטטוס חיבור, ולאחר מכן שימוש ב-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);
    });
});

הדוגמה הזו היא מערכת מלאה למעקב אחרי נוכחות ב-Realtime Database. הוא מטפל ניתוקים מרובים, קריסות וכו'.

מתבצע חיבור אל Cloud Firestore

כדי להטמיע פתרון דומה ב-Cloud Firestore, צריך להשתמש את הקוד של מסד נתונים בזמן אמת, ולאחר מכן להשתמש ב-Cloud Functions כדי לשמור על מסד הנתונים 'זמן אמת' Cloud Firestore מסונכרנים.

אם עדיין לא עשיתם זאת, מוסיפים את מסד נתונים בזמן אמת לפרויקט. ולכלול את פתרון הנוכחות שצוינה למעלה.

בשלב הבא תסנכרנו את סטטוס הנוכחות עם Cloud Firestore באמצעות השיטות הבאות:

  1. באופן מקומי, למטמון של Cloud Firestore של המכשיר אופליין, כדי שהאפליקציה כי הוא לא מחובר לאינטרנט.
  2. שימוש בפונקציה של Cloud Functions ברחבי העולם כדי שכל שאר המכשירים שיש להם גישה 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 כי הכתיבה של הסטטוס 'אופליין' היא מקומית בלבד, והיא לא תנוהל בסנכרון כשהחיבור ישוחזר. כדי להתמודד עם הבעיה הזו, נשתמש ב-Cloud Function שתעקוב אחרי הנתיב status/{uid} ב-Realtime Database. כאשר הערך של מסד הנתונים בזמן אמת משתנה, הערך יסונכרן עם 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().

אינטרנט

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 שניות של חוסר פעילות. חוסר פעילות פירושו שלא מאזינים פתוחים או פעולות בהמתנה. כדי שהחיבור יישאר פתוח, מומלץ להוסיף פונקציות event listener ל-value לנתיב מלבד .info/connected. לדוגמה, אפשר להריץ את הפונקציה FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() בתחילת כל סשן. מידע נוסף זמין במאמר הבא: זיהוי מצב החיבור.