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 tofalse
. - 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
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
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
// 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
// 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
// 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
// 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
Firestore.firestore().disableNetwork{(error)in// Do offline things// ...}
Objective-C
[[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
Firestore.firestore().enableNetwork{(error)in// Do online things// ...}
Objective-C
[[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});