Firebase Summit で発表されたすべての情報をご覧ください。Firebase を使用してアプリ開発を加速し、自信を持ってアプリを実行する方法を紹介しています。詳細

JavaScriptでオフライン機能を有効にする

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

アプリのネットワーク接続が一時的に失われた場合でも、Firebase アプリケーションは動作します。プレゼンスを監視し、ローカルの状態をサーバーの状態と同期するためのツールをいくつか提供しています。これらは、このドキュメントで紹介されています。

プレゼンスの管理

リアルタイム アプリケーションでは、クライアントがいつ接続および切断するかを検出すると便利なことがよくあります。たとえば、クライアントが切断されたときにユーザーを「オフライン」としてマークしたい場合があります。

Firebase Database クライアントは、クライアントが Firebase Database サーバーから切断されたときにデータベースへの書き込みに使用できる単純なプリミティブを提供します。これらの更新は、クライアントが正常に切断されたかどうかにかかわらず発生するため、接続が切断されたり、クライアントがクラッシュしたりした場合でも、データをクリーンアップするために信頼できます。設定、更新、および削除を含むすべての書き込み操作は、切断時に実行できます。

onDisconnectプリミティブを使用して、切断時にデータを書き込む簡単な例を次に示します。

Web version 9

import { getDatabase, ref, onDisconnect } from "firebase/database";

const db = getDatabase();
const presenceRef = ref(db, "disconnectmessage");
// Write a string when this client loses connection
onDisconnect(presenceRef).set("I disconnected!");

Web version 8

var presenceRef = firebase.database().ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");

onDisconnect の仕組み

onDisconnect()操作を確立すると、その操作は Firebase Realtime Database サーバー上に存在します。サーバーはセキュリティをチェックして、要求された書き込みイベントをユーザーが実行できることを確認し、無効な場合はアプリに通知します。その後、サーバーは接続を監視します。いずれかの時点で接続がタイムアウトした場合、または Realtime Database クライアントによってアクティブに閉じられた場合、サーバーは (操作がまだ有効であることを確認するために) セキュリティを 2 回チェックしてから、イベントを呼び出します。

アプリは書き込み操作でコールバックを使用して、 onDisconnectが正しくアタッチされたことを確認できます。

Web version 9

onDisconnect(presenceRef).remove().catch((err) => {
  if (err) {
    console.error("could not establish onDisconnect event", err);
  }
});

Web version 8

presenceRef.onDisconnect().remove((err) => {
  if (err) {
    console.error("could not establish onDisconnect event", err);
  }
});

onDisconnectイベントは、 .cancel()を呼び出してキャンセルすることもできます:

Web version 9

const onDisconnectRef = onDisconnect(presenceRef);
onDisconnectRef.set("I disconnected");
// some time later when we change our minds
onDisconnectRef.cancel();

Web version 8

var onDisconnectRef = presenceRef.onDisconnect();
onDisconnectRef.set("I disconnected");
// some time later when we change our minds
onDisconnectRef.cancel();

接続状態の検出

多くのプレゼンス関連の機能では、アプリがオンラインかオフラインかを知ることができます。 Firebase Realtime Database は、Firebase Realtime Database クライアントの接続状態が変化するたびに更新される/.info/connectedという特別な場所を提供します。次に例を示します。

Web version 9

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const connectedRef = ref(db, ".info/connected");
onValue(connectedRef, (snap) => {
  if (snap.val() === true) {
    console.log("connected");
  } else {
    console.log("not connected");
  }
});

Web version 8

var connectedRef = firebase.database().ref(".info/connected");
connectedRef.on("value", (snap) => {
  if (snap.val() === true) {
    console.log("connected");
  } else {
    console.log("not connected");
  }
});

/.info/connectedはブール値であり、値はクライアントの状態に依存するため、Realtime Database クライアント間で同期されません。つまり、あるクライアントが/.info/connectedを false として読み取った場合、別のクライアントも false を読み取るという保証はありません。

待ち時間の処理

サーバーのタイムスタンプ

Firebase Realtime Database サーバーは、サーバー上で生成されたタイムスタンプをデータとして挿入するメカニズムを提供します。この機能をonDisconnectと組み合わせると、Realtime Database クライアントが切断された時刻を確実に記録する簡単な方法が提供されます。

Web version 9

import { getDatabase, ref, onDisconnect, serverTimestamp } from "firebase/database";

const db = getDatabase();
const userLastOnlineRef = ref(db, "users/joe/lastOnline");
onDisconnect(userLastOnlineRef).set(serverTimestamp());

Web version 8

var userLastOnlineRef = firebase.database().ref("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP);

クロック スキュー

firebase.database.ServerValue.TIMESTAMPははるかに正確であり、ほとんどの読み取り/書き込み操作に適していますが、Firebase Realtime Database のサーバーに対するクライアントのクロック スキューを推定するのに役立つ場合があります。 /.info/serverTimeOffsetの場所にコールバックをアタッチして、Firebase Realtime Database クライアントがサーバー時間を推定するためにローカルで報告された時間 (ミリ秒単位のエポック時間) に追加する値 (ミリ秒単位) を取得できます。このオフセットの精度はネットワーク レイテンシの影響を受ける可能性があるため、主にクロック時間の大きな (1 秒を超える) 不一致を検出するのに役立ちます。

Web version 9

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const offsetRef = ref(db, ".info/serverTimeOffset");
onValue(offsetRef, (snap) => {
  const offset = snap.val();
  const estimatedServerTimeMs = new Date().getTime() + offset;
});

Web version 8

var offsetRef = firebase.database().ref(".info/serverTimeOffset");
offsetRef.on("value", (snap) => {
  var offset = snap.val();
  var estimatedServerTimeMs = new Date().getTime() + offset;
});

サンプル プレゼンス アプリ

切断操作を接続状態の監視とサーバーのタイムスタンプと組み合わせることで、ユーザー プレゼンス システムを構築できます。このシステムでは、各ユーザーがデータベースの場所にデータを保存して、Realtime Database クライアントがオンラインかどうかを示します。クライアントは、オンラインになるとこの場所を true に設定し、切断するとタイムスタンプを設定します。このタイムスタンプは、特定のユーザーが最後にオンラインだった時間を示します。

両方のコマンドがサーバーに送信される前にクライアントのネットワーク接続が失われた場合に競合状態を回避するために、ユーザーがオンラインとしてマークされる前に、アプリは切断操作をキューに入れる必要があることに注意してください。

以下は、単純なユーザー プレゼンス システムです。

Web version 9

import { getDatabase, ref, onValue, push, onDisconnect, set, serverTimestamp } from "firebase/database";

// Since I can connect from multiple devices or browser tabs, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
const db = getDatabase();
const myConnectionsRef = ref(db, 'users/joe/connections');

// stores the timestamp of my last disconnect (the last time I was seen online)
const lastOnlineRef = ref(db, 'users/joe/lastOnline');

const connectedRef = ref(db, '.info/connected');
onValue(connectedRef, (snap) => {
  if (snap.val() === true) {
    // We're connected (or reconnected)! Do anything here that should happen only if online (or on reconnect)
    const con = push(myConnectionsRef);

    // When I disconnect, remove this device
    onDisconnect(con).remove();

    // Add this device to my connections list
    // this value could contain info about the device or a timestamp too
    set(con, true);

    // When I disconnect, update the last time I was seen online
    onDisconnect(lastOnlineRef).set(serverTimestamp());
  }
});

Web version 8

// Since I can connect from multiple devices or browser tabs, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
var myConnectionsRef = firebase.database().ref('users/joe/connections');

// stores the timestamp of my last disconnect (the last time I was seen online)
var lastOnlineRef = firebase.database().ref('users/joe/lastOnline');

var connectedRef = firebase.database().ref('.info/connected');
connectedRef.on('value', (snap) => {
  if (snap.val() === true) {
    // We're connected (or reconnected)! Do anything here that should happen only if online (or on reconnect)
    var con = myConnectionsRef.push();

    // When I disconnect, remove this device
    con.onDisconnect().remove();

    // Add this device to my connections list
    // this value could contain info about the device or a timestamp too
    con.set(true);

    // When I disconnect, update the last time I was seen online
    lastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP);
  }
});