Firebase Summit のすべての発表内容に目を通し、Firebase を活用してアプリ開発を加速し、自信を持ってアプリを実行できる方法をご確認ください。 詳細

オフラインでデータにアクセスする

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

Cloud Firestore は、オフライン データの永続性をサポートしています。この機能は、アプリがアクティブに使用している Cloud Firestore データのコピーをキャッシュするため、デバイスがオフラインのときにアプリがデータにアクセスできます。キャッシュされたデータの書き込み、読み取り、リッスン、およびクエリを実行できます。デバイスがオンラインに戻ると、Cloud Firestore はアプリによって加えられたローカルの変更を Cloud Firestore バックエンドに同期します。

オフライン永続性を使用するために、Cloud Firestore データへのアクセスに使用するコードを変更する必要はありません。オフライン永続性を有効にすると、Cloud Firestore クライアント ライブラリは、オンラインとオフラインのデータ アクセスを自動的に管理し、デバイスがオンラインに戻ったときにローカル データを同期します。

オフライン永続性の構成

Cloud Firestore を初期化するときに、オフライン パーシステンスを有効または無効にできます。

  • Android および Apple プラットフォームの場合、オフライン永続性はデフォルトで有効になっています。永続性を無効にするには、 PersistenceEnabledオプションをfalseに設定します。
  • Web の場合、オフライン永続性はデフォルトで無効になっています。永続性を有効にするには、 enablePersistenceメソッドを呼び出します。 Cloud Firestore のキャッシュは、セッション間で自動的にクリアされません。したがって、Web アプリが機密情報を処理する場合は、永続化を有効にする前に、信頼できるデバイスを使用しているかどうかをユーザーに確認してください。

Web version 9

import { enableIndexedDbPersistence } from "firebase/firestore"; 

enableIndexedDbPersistence(db)
  .catch((err) => {
      if (err.code == 'failed-precondition') {
          // Multiple tabs open, persistence can only be enabled
          // in one tab at a a time.
          // ...
      } else if (err.code == 'unimplemented') {
          // The current browser does not support all of the
          // features required to enable persistence
          // ...
      }
  });
// Subsequent queries will use persistence, if it was enabled successfully

Web version 8

firebase.firestore().enablePersistence()
  .catch((err) => {
      if (err.code == 'failed-precondition') {
          // Multiple tabs open, persistence can only be enabled
          // in one tab at a a time.
          // ...
      } else if (err.code == 'unimplemented') {
          // The current browser does not support all of the
          // features required to enable persistence
          // ...
      }
  });
// Subsequent queries will use persistence, if it was enabled successfully
迅速
注:この製品は、watchOS および App Clip ターゲットでは使用できません。
let settings = FirestoreSettings()
settings.isPersistenceEnabled = true

// Any additional options
// ...

// Enable offline data persistence
let db = Firestore.firestore()
db.settings = settings
Objective-C
注:この製品は、watchOS および App Clip ターゲットでは使用できません。
FIRFirestoreSettings *settings = [[FIRFirestoreSettings alloc] init];
settings.persistenceEnabled = YES;

// Any additional options
// ...

// Enable offline data persistence
FIRFirestore *db = [FIRFirestore firestore];
db.settings = settings;

Java

FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
        .setPersistenceEnabled(true)
        .build();
db.setFirestoreSettings(settings);

Kotlin+KTX

val settings = firestoreSettings {
    isPersistenceEnabled = true
}
db.firestoreSettings = settings

Dart

// Apple and Android
db.settings = const Settings(persistenceEnabled: true);

// Web
await db
    .enablePersistence(const PersistenceSettings(synchronizeTabs: true));

キャッシュ サイズの構成

永続性が有効になっている場合、Cloud Firestore はオフライン アクセス用にバックエンドから受信したすべてのドキュメントをキャッシュします。 Cloud Firestore は、キャッシュ サイズのデフォルトのしきい値を設定します。デフォルトを超えると、Cloud Firestore は定期的に古い未使用のドキュメントのクリーンアップを試みます。別のキャッシュ サイズのしきい値を構成するか、クリーンアップ プロセスを完全に無効にすることができます。

Web version 9

import { initializeFirestore, CACHE_SIZE_UNLIMITED } from "firebase/firestore";

const firestoreDb = initializeFirestore(app, {
  cacheSizeBytes: CACHE_SIZE_UNLIMITED
});

Web version 8

firebase.firestore().settings({
    cacheSizeBytes: firebase.firestore.CACHE_SIZE_UNLIMITED
});
迅速
注:この製品は、watchOS および App Clip ターゲットでは使用できません。
// The default cache size threshold is 100 MB. Configure "cacheSizeBytes"
// for a different threshold (minimum 1 MB) or set to "FirestoreCacheSizeUnlimited"
// to disable clean-up.
let settings = Firestore.firestore().settings
settings.cacheSizeBytes = FirestoreCacheSizeUnlimited
Firestore.firestore().settings = settings
Objective-C
注:この製品は、watchOS および App Clip ターゲットでは使用できません。
// The default cache size threshold is 100 MB. Configure "cacheSizeBytes"
// for a different threshold (minimum 1 MB) or set to "kFIRFirestoreCacheSizeUnlimited"
// to disable clean-up.
FIRFirestoreSettings *settings = [FIRFirestore firestore].settings;
settings.cacheSizeBytes = kFIRFirestoreCacheSizeUnlimited;
[FIRFirestore firestore].settings = settings;
  

Java


// The default cache size threshold is 100 MB. Configure "setCacheSizeBytes"
// for a different threshold (minimum 1 MB) or set to "CACHE_SIZE_UNLIMITED"
// to disable clean-up.
FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
        .setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED)
        .build();
db.setFirestoreSettings(settings);

Kotlin+KTX


// The default cache size threshold is 100 MB. Configure "setCacheSizeBytes"
// for a different threshold (minimum 1 MB) or set to "CACHE_SIZE_UNLIMITED"
// to disable clean-up.
val settings = FirebaseFirestoreSettings.Builder()
        .setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED)
        .build()
db.firestoreSettings = settings

Dart

db.settings = const Settings(
  persistenceEnabled: true,
  cacheSizeBytes: Settings.CACHE_SIZE_UNLIMITED,
);

オフライン データを聞く

オフライン永続性を有効にしている場合、デバイスがオフラインの間、ローカルにキャッシュされたデータが変更されると、リスナーはリッスン イベントを受け取ります。ドキュメント、コレクション、クエリを聞くことができます。

サーバーまたはキャッシュからデータを受信して​​いるかどうかを確認するには、スナップショット イベントのSnapshotMetadatafromCacheプロパティを使用します。 fromCachetrue場合、データはキャッシュからのものであり、古いか不完全である可能性があります。 fromCachefalseの場合、データは完全であり、サーバー上の最新の更新により最新です。

デフォルトでは、 SnapshotMetadataのみが変更された場合、イベントは発生しません。 fromCache値に依存する場合は、リッスン ハンドラーをアタッチするときにincludeMetadataChangesリッスン オプションを指定します。

Web version 9

import { collection, onSnapshot, where, query } from "firebase/firestore"; 

const q = query(collection(db, "cities"), where("state", "==", "CA"));
onSnapshot(q, { includeMetadataChanges: true }, (snapshot) => {
    snapshot.docChanges().forEach((change) => {
        if (change.type === "added") {
            console.log("New city: ", change.doc.data());
        }

        const source = snapshot.metadata.fromCache ? "local cache" : "server";
        console.log("Data came from " + source);
    });
});

Web version 8

db.collection("cities").where("state", "==", "CA")
  .onSnapshot({ includeMetadataChanges: true }, (snapshot) => {
      snapshot.docChanges().forEach((change) => {
          if (change.type === "added") {
              console.log("New city: ", change.doc.data());
          }

          var source = snapshot.metadata.fromCache ? "local cache" : "server";
          console.log("Data came from " + source);
      });
  });
迅速
注:この製品は、watchOS および App Clip ターゲットでは使用できません。
// Listen to metadata updates to receive a server snapshot even if
// the data is the same as the cached data.
db.collection("cities").whereField("state", isEqualTo: "CA")
    .addSnapshotListener(includeMetadataChanges: true) { querySnapshot, error in
        guard let snapshot = querySnapshot else {
            print("Error retreiving snapshot: \(error!)")
            return
        }

        for diff in snapshot.documentChanges {
            if diff.type == .added {
                print("New city: \(diff.document.data())")
            }
        }

        let source = snapshot.metadata.isFromCache ? "local cache" : "server"
        print("Metadata: Data fetched from \(source)")
}
Objective-C
注:この製品は、watchOS および App Clip ターゲットでは使用できません。
// Listen to metadata updates to receive a server snapshot even if
// the data is the same as the cached data.
[[[db collectionWithPath:@"cities"] queryWhereField:@"state" isEqualTo:@"CA"]
    addSnapshotListenerWithIncludeMetadataChanges:YES
    listener:^(FIRQuerySnapshot *snapshot, NSError *error) {
      if (snapshot == nil) {
        NSLog(@"Error retreiving snapshot: %@", error);
        return;
      }
      for (FIRDocumentChange *diff in snapshot.documentChanges) {
        if (diff.type == FIRDocumentChangeTypeAdded) {
          NSLog(@"New city: %@", diff.document.data);
        }
      }

      NSString *source = snapshot.metadata.isFromCache ? @"local cache" : @"server";
      NSLog(@"Metadata: Data fetched from %@", source);
    }];

Java

db.collection("cities").whereEqualTo("state", "CA")
        .addSnapshotListener(MetadataChanges.INCLUDE, new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable QuerySnapshot querySnapshot,
                                @Nullable FirebaseFirestoreException e) {
                if (e != null) {
                    Log.w(TAG, "Listen error", e);
                    return;
                }

                for (DocumentChange change : querySnapshot.getDocumentChanges()) {
                    if (change.getType() == Type.ADDED) {
                        Log.d(TAG, "New city:" + change.getDocument().getData());
                    }

                    String source = querySnapshot.getMetadata().isFromCache() ?
                            "local cache" : "server";
                    Log.d(TAG, "Data fetched from " + source);
                }

            }
        });

Kotlin+KTX

db.collection("cities").whereEqualTo("state", "CA")
        .addSnapshotListener(MetadataChanges.INCLUDE) { querySnapshot, e ->
            if (e != null) {
                Log.w(TAG, "Listen error", e)
                return@addSnapshotListener
            }

            for (change in querySnapshot!!.documentChanges) {
                if (change.type == DocumentChange.Type.ADDED) {
                    Log.d(TAG, "New city: ${change.document.data}")
                }

                val source = if (querySnapshot.metadata.isFromCache)
                    "local cache"
                else
                    "server"
                Log.d(TAG, "Data fetched from $source")
            }
        }

Dart

db
    .collection("cities")
    .where("state", isEqualTo: "CA")
    .snapshots(includeMetadataChanges: true)
    .listen((querySnapshot) {
  for (var change in querySnapshot.docChanges) {
    if (change.type == DocumentChangeType.added) {
      final source =
          (querySnapshot.metadata.isFromCache) ? "local cache" : "server";

      print("Data fetched from $source}");
    }
  }
});

オフライン データを取得する

デバイスがオフラインのときにドキュメントを取得すると、Cloud Firestore はキャッシュからデータを返します。

コレクションをクエリすると、キャッシュされたドキュメントがない場合、空の結果が返されます。特定のドキュメントを取得すると、代わりにエラーが返されます。

オフライン データのクエリ

クエリは、オフラインの持続性で機能します。前のセクションで説明したように、直接取得するかリッスンすることで、クエリの結果を取得できます。デバイスがオフラインのときにローカルに保持されたデータに対して新しいクエリを作成することもできますが、クエリは最初はキャッシュされたドキュメントに対してのみ実行されます。

オフライン クエリ インデックスの構成

デフォルトでは、オフライン クエリの実行時に、Firestore SDK はローカル キャッシュ内のコレクション内のすべてのドキュメントをスキャンします。この既定の動作では、ユーザーが長時間オフラインになると、オフライン クエリのパフォーマンスが低下する可能性があります。

Apple プラットフォームAndroid 、およびJavaScript SDK には、オフライン クエリのパフォーマンスを向上させるためにローカル クエリ インデックスを構成できるsetIndexConfigurationメソッドが用意されています。

これらのメソッドは、同じインデックス定義形式に従って、サーバー上でインデックスを構成するために使用されるものと同じ JSON 構造の構成を読み取ります。

使用するオフライン インデックス構成は、アプリがオフライン中に頻繁にアクセスするコレクションとドキュメント、および必要なオフライン パフォーマンスによって異なります。クライアントで使用するためにバックエンド インデックス構成をエクスポートできますが、アプリのオフライン アクセス パターンはオンライン アクセス パターンとは大きく異なる可能性が高いため、オンライン インデックス構成はオフラインでの使用に適していない可能性があります。アプリが高パフォーマンスでオフラインでアクセスできるコレクションとドキュメントはどれですか?アプリの動作を分析したら、インデックス作成ガイドのインデックス定義の原則に従います。

クライアント アプリでの読み込みにオフライン インデックス構成を使用できるようにするには:

  • アプリでそれらをコンパイルして配布する
  • CDN からダウンロードする
  • Cloud Storage for Firebaseなどのストレージ システムからそれらをフェッチします。

ネットワーク アクセスの無効化と有効化

以下の方法を使用して、Cloud Firestore クライアントのネットワーク アクセスを無効にすることができます。ネットワーク アクセスが無効になっている間、すべてのスナップショット リスナーとドキュメント リクエストはキャッシュから結果を取得します。ネットワーク アクセスが再び有効になるまで、書き込み操作はキューに入れられます。

Web version 9

import { disableNetwork } from "firebase/firestore"; 

await disableNetwork(db);
console.log("Network disabled!");
// Do offline actions
// ...

Web version 8

firebase.firestore().disableNetwork()
    .then(() => {
        // Do offline actions
        // ...
    });
迅速
注:この製品は、watchOS および App Clip ターゲットでは使用できません。
Firestore.firestore().disableNetwork { (error) in
    // Do offline things
    // ...
}
Objective-C
注:この製品は、watchOS および App Clip ターゲットでは使用できません。
[[FIRFirestore firestore] disableNetworkWithCompletion:^(NSError *_Nullable error) {
  // Do offline actions
  // ...
}];

Java

db.disableNetwork()
        .addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                // Do offline things
                // ...
            }
        });

Kotlin+KTX

db.disableNetwork().addOnCompleteListener {
    // Do offline things
    // ...
}

Dart

db.disableNetwork().then((_) {
  // Do offline things
});

ネットワーク アクセスを再度有効にするには、次の方法を使用します。

Web version 9

import { enableNetwork } from "firebase/firestore"; 

await enableNetwork(db);
// Do online actions
// ...

Web version 8

firebase.firestore().enableNetwork()
    .then(() => {
        // Do online actions
        // ...
    });
迅速
注:この製品は、watchOS および App Clip ターゲットでは使用できません。
Firestore.firestore().enableNetwork { (error) in
    // Do online things
    // ...
}
Objective-C
注:この製品は、watchOS および App Clip ターゲットでは使用できません。
[[FIRFirestore firestore] enableNetworkWithCompletion:^(NSError *_Nullable error) {
  // Do online actions
  // ...
}];

Java

db.enableNetwork()
        .addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                // Do online things
                // ...
            }
        });

Kotlin+KTX

db.enableNetwork().addOnCompleteListener {
    // Do online things
    // ...
}

Dart

db.enableNetwork().then((_) {
  // Back online
});