Access data offline

Cloud Firestore supports offline data persistence. This feature caches a copy of the Cloud Firestore data that your app is actively using, so your app can access the data when the device is offline. You can write, read, listen to, and query the cached data. When the device comes back online, Cloud Firestore synchronizes any local changes made by your app to the Cloud Firestore backend.

To use offline persistence, you don't need to make any changes to the code that you use to access Cloud Firestore data. With offline persistence enabled, the Cloud Firestore client library automatically manages online and offline data access and synchronizes local data when the device is back online.

Configure offline persistence

When you initialize Cloud Firestore, you can enable or disable offline persistence:

  • For Android and Apple platforms, offline persistence is enabled by default. To disable persistence, set the PersistenceEnabled option to false.
  • For the web, offline persistence is disabled by default. To enable persistence, call the enablePersistence method. Cloud Firestore's cache isn't automatically cleared between sessions. Consequently, if your web app handles sensitive information, make sure to ask the user if they're on a trusted device before enabling persistence.

Web

// Memory cache is the default if no config is specified.initializeFirestore(app);// This is the default behavior if no persistence is specified.initializeFirestore(app,{localCache:memoryLocalCache()});// Defaults to single-tab persistence if no tab manager is specified.initializeFirestore(app,{localCache:persistentLocalCache(/*settings*/{})});// Same as `initializeFirestore(app, {localCache: persistentLocalCache(/*settings*/{})})`,// but more explicit about tab management.initializeFirestore(app,{localCache:persistentLocalCache(/*settings*/{tabManager:persistentSingleTabManager()})});// Use multi-tab IndexedDb persistence.initializeFirestore(app,{localCache:persistentLocalCache(/*settings*/{tabManager:persistentMultipleTabManager()})});

Web

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.// ...}elseif(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
Swift
Note: This product is not available on watchOS and App Clip targets.
letsettings=FirestoreSettings()// Use memory-only cachesettings.cacheSettings=MemoryCacheSettings(garbageCollectorSettings:MemoryLRUGCSettings())// Use persistent disk cache, with 100 MB cache sizesettings.cacheSettings=PersistentCacheSettings(sizeBytes:100*1024*1024asNSNumber)// Any additional options// ...// Enable offline data persistenceletdb=Firestore.firestore()db.settings=settings
Objective-C
Note: This product is not available on watchOS and App Clip targets.
FIRFirestoreSettings*settings=[[FIRFirestoreSettingsalloc]init];// Use memory-only cachesettings.cacheSettings=[[FIRMemoryCacheSettingsalloc]initWithGarbageCollectorSettings:[[FIRMemoryLRUGCSettingsalloc]init]];// Use persistent disk cache (default behavior)// This example uses 100 MB.settings.cacheSettings=[[FIRPersistentCacheSettingsalloc]initWithSizeBytes:@(100*1024*1024)];// Any additional options// ...// Enable offline data persistenceFIRFirestore*db=[FIRFirestorefirestore];db.settings=settings;

Kotlin

valsettings=firestoreSettings{// Use memory cachesetLocalCacheSettings(memoryCacheSettings{})// Use persistent disk cache (default)setLocalCacheSettings(persistentCacheSettings{})}db.firestoreSettings=settings

Java

FirebaseFirestoreSettingssettings=newFirebaseFirestoreSettings.Builder(db.getFirestoreSettings())// Use memory-only cache.setLocalCacheSettings(MemoryCacheSettings.newBuilder().build())// Use persistent disk cache (default).setLocalCacheSettings(PersistentCacheSettings.newBuilder().build()).build();db.setFirestoreSettings(settings);

Dart

// Apple and Androiddb.settings=constSettings(persistenceEnabled:true);// Webawaitdb.enablePersistence(constPersistenceSettings(synchronizeTabs:true));

Configure cache size

When persistence is enabled, Cloud Firestore caches every document received from the backend for offline access. Cloud Firestore sets a default threshold for cache size. After exceeding the default, Cloud Firestore periodically attempts to clean up older, unused documents. You can configure a different cache size threshold or disable the clean-up process completely:

Web

import{initializeFirestore,CACHE_SIZE_UNLIMITED}from"firebase/firestore";constfirestoreDb=initializeFirestore(app,{cacheSizeBytes:CACHE_SIZE_UNLIMITED});

Web

firebase.firestore().settings({cacheSizeBytes:firebase.firestore.CACHE_SIZE_UNLIMITED});
Swift
Note: This product is not available on watchOS and App Clip targets.
// 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.letsettings=Firestore.firestore().settings// Set cache size to 100 MBsettings.cacheSettings=PersistentCacheSettings(sizeBytes:100*1024*1024asNSNumber)Firestore.firestore().settings=settings
Objective-C
Note: This product is not available on watchOS and App Clip targets.
// 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=[FIRFirestorefirestore].settings;// Set cache size to 100 MBsettings.cacheSettings=[[FIRPersistentCacheSettingsalloc]initWithSizeBytes:@(100*1024*1024)];[FIRFirestorefirestore].settings=settings;

Kotlin

// 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.valsettings=FirebaseFirestoreSettings.Builder().setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED).build()db.firestoreSettings=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.FirebaseFirestoreSettingssettings=newFirebaseFirestoreSettings.Builder().setCacheSizeBytes(FirebaseFirestoreSettings.CACHE_SIZE_UNLIMITED).build();db.setFirestoreSettings(settings);

Dart

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

Listen to offline data

While the device is offline, if you have enabled offline persistence, your listeners will receive listen events when the locally cached data changes. You can listen to documents, collections, and queries.

To check whether you're receiving data from the server or the cache, use the fromCache property on the SnapshotMetadata in your snapshot event. If fromCache is true, the data came from the cache and might be stale or incomplete. If fromCache is false, the data is complete and current with the latest updates on the server.

By default, no event is raised if only the SnapshotMetadata changed. If you rely on the fromCache values, specify the includeMetadataChanges listen option when you attach your listen handler.

Web

import{collection,onSnapshot,where,query}from"firebase/firestore";constq=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());}constsource=snapshot.metadata.fromCache?"local cache":"server";console.log("Data came from "+source);});});

Web

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());}varsource=snapshot.metadata.fromCache?"local cache":"server";console.log("Data came from "+source);});});
Swift
Note: This product is not available on watchOS and App Clip targets.
// 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,erroringuardletsnapshot=querySnapshotelse{print("Error retreiving snapshot: \(error!)")return}fordiffinsnapshot.documentChanges{ifdiff.type==.added{print("New city: \(diff.document.data())")}}letsource=snapshot.metadata.isFromCache?"local cache":"server"print("Metadata: Data fetched from \(source)")}
Objective-C
Note: This product is not available on watchOS and App Clip targets.
// Listen to metadata updates to receive a server snapshot even if// the data is the same as the cached data.[[[dbcollectionWithPath:@"cities"]queryWhereField:@"state"isEqualTo:@"CA"]addSnapshotListenerWithIncludeMetadataChanges:YESlistener:^(FIRQuerySnapshot*snapshot,NSError*error){if(snapshot==nil){NSLog(@"Error retreiving snapshot: %@",error);return;}for(FIRDocumentChange*diffinsnapshot.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);}];

Kotlin

db.collection("cities").whereEqualTo("state","CA").addSnapshotListener(MetadataChanges.INCLUDE){querySnapshot,e-> if(e!=null){Log.w(TAG,"Listen error",e)return@addSnapshotListener}for(changeinquerySnapshot!!.documentChanges){if(change.type==DocumentChange.Type.ADDED){Log.d(TAG,"New city: ${change.document.data}")}valsource=if(querySnapshot.metadata.isFromCache){"local cache"}else{"server"}Log.d(TAG,"Data fetched from $source")}}

Java

db.collection("cities").whereEqualTo("state","CA").addSnapshotListener(MetadataChanges.INCLUDE,newEventListener<QuerySnapshot>(){@OverridepublicvoidonEvent(@NullableQuerySnapshotquerySnapshot,@NullableFirebaseFirestoreExceptione){if(e!=null){Log.w(TAG,"Listen error",e);return;}for(DocumentChangechange:querySnapshot.getDocumentChanges()){if(change.getType()==Type.ADDED){Log.d(TAG,"New city:"+change.getDocument().getData());}Stringsource=querySnapshot.getMetadata().isFromCache()?"local cache":"server";Log.d(TAG,"Data fetched from "+source);}}});

Dart

db.collection("cities").where("state",isEqualTo:"CA").snapshots(includeMetadataChanges:true).listen((querySnapshot){for(varchangeinquerySnapshot.docChanges){if(change.type==DocumentChangeType.added){finalsource=(querySnapshot.metadata.isFromCache)?"local cache":"server";print("Data fetched from $source}");}}});

Get offline data

If you get a document while the device is offline, Cloud Firestore returns data from the cache.

When querying a collection, an empty result is returned if there are no cached documents. When fetching a specific document, an error is returned instead.

Query offline data

Querying works with offline persistence. You can retrieve the results of queries with either a direct get or by listening, as described in the preceding sections. You can also create new queries on locally persisted data while the device is offline, but the queries will initially run only against the cached documents.

Configure offline query indexes

By default, the Firestore SDK scans all documents in a collection in its local cache when executing offline queries. With this default behavior, offline query performance can suffer when users are offline for long periods of time.

With persistent cache enabled, you can improve the performance of offline queries by allowing the SDK to create local query indexes automatically.

Automatic indexing is disabled by default. Your app must enable automatic indexing each time it starts. Control whether automatic indexing is enabled as shown below.

Swift
ifletindexManager=Firestore.firestore().persistentCacheIndexManager{// Indexing is disabled by defaultindexManager.enableIndexAutoCreation()}else{print("indexManager is nil")}
Objective-C
PersistentCacheIndexManager*indexManager=[FIRFirestorefirestore].persistentCacheIndexManager;if(indexManager){// Indexing is disabled by default[indexManagerenableIndexAutoCreation];}

Kotlin

// return type: PersistentCacheManager?Firebase.firestore.persistentCacheIndexManager?.apply{// Indexing is disabled by defaultenableIndexAutoCreation()}?:println("indexManager is null")

Java

// return type: @Nullable PersistentCacheIndexManagerPersistentCacheIndexManagerindexManager=FirebaseFirestore.getInstance().getPersistentCacheIndexManager();if(indexManager!=null){// Indexing is disabled by defaultindexManager.enableIndexAutoCreation();}// If not check indexManager != null, IDE shows warning: Method invocation 'enableIndexAutoCreation' may produce 'NullPointerException' FirebaseFirestore.getInstance().getPersistentCacheIndexManager().enableIndexAutoCreation();

Once automatic indexing is enabled, the SDK evaluates which collections have a large number of cached documents and optimizes performance of local queries.

The SDK provides a method for deleting query indexes.

Disable and enable network access

You can use the method below to disable network access for your Cloud Firestore client. While network access is disabled, all snapshot listeners and document requests retrieve results from the cache. Write operations are queued until network access is re-enabled.

Web

import{disableNetwork}from"firebase/firestore";awaitdisableNetwork(db);console.log("Network disabled!");// Do offline actions// ...

Web

firebase.firestore().disableNetwork().then(()=>{// Do offline actions// ...});
Swift
Note: This product is not available on watchOS and App Clip targets.
Firestore.firestore().disableNetwork{(error)in// Do offline things// ...}
Objective-C
Note: This product is not available on watchOS and App Clip targets.
[[FIRFirestorefirestore]disableNetworkWithCompletion:^(NSError*_Nullableerror){// Do offline actions// ...}];

Kotlin

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

Java

db.disableNetwork().addOnCompleteListener(newOnCompleteListener<Void>(){@OverridepublicvoidonComplete(@NonNullTask<Void>task){// Do offline things// ...}});

Dart

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

Use the following method to re-enable network access:

Web

import{enableNetwork}from"firebase/firestore";awaitenableNetwork(db);// Do online actions// ...

Web

firebase.firestore().enableNetwork().then(()=>{// Do online actions// ...});
Swift
Note: This product is not available on watchOS and App Clip targets.
Firestore.firestore().enableNetwork{(error)in// Do online things// ...}
Objective-C
Note: This product is not available on watchOS and App Clip targets.
[[FIRFirestorefirestore]enableNetworkWithCompletion:^(NSError*_Nullableerror){// Do online actions// ...}];

Kotlin

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

Java

db.enableNetwork().addOnCompleteListener(newOnCompleteListener<Void>(){@OverridepublicvoidonComplete(@NonNullTask<Void>task){// Do online things// ...}});

Dart

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