0
# Offline & Network Management
1
2
Comprehensive offline support with configurable persistence, network management, and cache control.
3
4
## Capabilities
5
6
### Network Control
7
8
Manage network connectivity to control when Firestore communicates with the server.
9
10
```typescript { .api }
11
/**
12
* Enable network connectivity for Firestore
13
* @returns Promise resolving when network is enabled
14
*/
15
enableNetwork(): Promise<void>;
16
17
/**
18
* Disable network connectivity for Firestore
19
* @returns Promise resolving when network is disabled
20
*/
21
disableNetwork(): Promise<void>;
22
```
23
24
**Usage Examples:**
25
26
```typescript
27
import firestore from '@react-native-firebase/firestore';
28
29
// Enable network (default state)
30
await firestore().enableNetwork();
31
console.log('Network enabled - can sync with server');
32
33
// Disable network for offline-only operation
34
await firestore().disableNetwork();
35
console.log('Network disabled - operating in offline mode');
36
37
// Re-enable network
38
await firestore().enableNetwork();
39
console.log('Network re-enabled - will sync pending changes');
40
41
// Network state management based on app state
42
import { AppState } from 'react-native';
43
44
class NetworkManager {
45
constructor() {
46
AppState.addEventListener('change', this.handleAppStateChange);
47
}
48
49
handleAppStateChange = async (nextAppState: string) => {
50
const db = firestore();
51
52
if (nextAppState === 'background') {
53
// Disable network to save battery/data when app is backgrounded
54
await db.disableNetwork();
55
console.log('App backgrounded - network disabled');
56
} else if (nextAppState === 'active') {
57
// Re-enable network when app becomes active
58
await db.enableNetwork();
59
console.log('App active - network enabled');
60
}
61
};
62
}
63
```
64
65
### Persistence Management
66
67
Control offline data persistence and cache behavior.
68
69
```typescript { .api }
70
/**
71
* Clear all cached data and pending writes
72
* @returns Promise resolving when persistence is cleared
73
*/
74
clearPersistence(): Promise<void>;
75
76
/**
77
* Wait for all pending writes to complete
78
* @returns Promise resolving when all writes are committed
79
*/
80
waitForPendingWrites(): Promise<void>;
81
82
/**
83
* Terminate the Firestore instance and clean up resources
84
* @returns Promise resolving when termination is complete
85
*/
86
terminate(): Promise<void>;
87
```
88
89
**Usage Examples:**
90
91
```typescript
92
import firestore from '@react-native-firebase/firestore';
93
94
// Clear all cached data (useful for user logout)
95
async function clearUserData() {
96
const db = firestore();
97
98
// First disable network to prevent new data
99
await db.disableNetwork();
100
101
// Clear all cached data
102
await db.clearPersistence();
103
104
console.log('All cached data cleared');
105
106
// Re-enable network for fresh start
107
await db.enableNetwork();
108
}
109
110
// Wait for pending writes before performing sensitive operations
111
async function performSecureOperation() {
112
const db = firestore();
113
114
// Ensure all pending writes are committed
115
await db.waitForPendingWrites();
116
console.log('All writes committed to server');
117
118
// Now safe to perform secure operation
119
await performSensitiveTask();
120
}
121
122
// Clean shutdown of Firestore
123
async function shutdownFirestore() {
124
const db = firestore();
125
126
// Wait for pending operations
127
await db.waitForPendingWrites();
128
129
// Terminate the instance
130
await db.terminate();
131
132
console.log('Firestore instance terminated');
133
}
134
135
// Handle app lifecycle for proper cleanup
136
class FirestoreManager {
137
async componentWillUnmount() {
138
try {
139
// Ensure data is persisted before app closes
140
await firestore().waitForPendingWrites();
141
} catch (error) {
142
console.error('Error waiting for writes:', error);
143
}
144
}
145
}
146
```
147
148
### Settings Configuration
149
150
Configure Firestore behavior including cache size, persistence, and server settings.
151
152
```typescript { .api }
153
/**
154
* Configure Firestore settings
155
* @param settings - Configuration options
156
* @returns Promise resolving when settings are applied
157
*/
158
settings(settings: FirebaseFirestoreTypes.Settings): Promise<void>;
159
160
interface Settings {
161
/**
162
* Enable or disable offline persistence
163
* @default true
164
*/
165
persistence?: boolean;
166
167
/**
168
* Cache size in bytes (-1 for unlimited)
169
* @default 104857600 (100 MB)
170
*/
171
cacheSizeBytes?: number;
172
173
/**
174
* Firestore host (for custom endpoints)
175
*/
176
host?: string;
177
178
/**
179
* Enable or disable SSL
180
* @default true
181
*/
182
ssl?: boolean;
183
184
/**
185
* Ignore undefined properties in document data
186
* @default false
187
*/
188
ignoreUndefinedProperties?: boolean;
189
190
/**
191
* Behavior for server timestamps that haven't been set yet
192
* @default 'none'
193
*/
194
serverTimestampBehavior?: 'estimate' | 'previous' | 'none';
195
}
196
```
197
198
**Usage Examples:**
199
200
```typescript
201
import firestore from '@react-native-firebase/firestore';
202
203
// Configure basic settings
204
await firestore().settings({
205
persistence: true,
206
cacheSizeBytes: 50 * 1024 * 1024, // 50 MB cache
207
ignoreUndefinedProperties: true
208
});
209
210
// Unlimited cache size
211
await firestore().settings({
212
cacheSizeBytes: firestore.CACHE_SIZE_UNLIMITED
213
});
214
215
// Custom server timestamp behavior
216
await firestore().settings({
217
serverTimestampBehavior: 'estimate' // Use local time estimate
218
});
219
220
// Development settings with custom host
221
await firestore().settings({
222
host: 'localhost:8080',
223
ssl: false,
224
persistence: false // Disable for development
225
});
226
227
// Production optimized settings
228
await firestore().settings({
229
persistence: true,
230
cacheSizeBytes: 100 * 1024 * 1024, // 100 MB
231
ignoreUndefinedProperties: true,
232
serverTimestampBehavior: 'estimate'
233
});
234
235
// Memory-constrained device settings
236
await firestore().settings({
237
persistence: true,
238
cacheSizeBytes: 10 * 1024 * 1024, // 10 MB cache
239
ignoreUndefinedProperties: true
240
});
241
```
242
243
### Emulator Support
244
245
Connect to the Firestore emulator for development and testing.
246
247
```typescript { .api }
248
/**
249
* Connect to Firestore emulator
250
* @param host - Emulator host
251
* @param port - Emulator port
252
* @returns Array containing actual host and port used (for testing)
253
*/
254
useEmulator(host: string, port: number): [string, number];
255
```
256
257
**Usage Examples:**
258
259
```typescript
260
import firestore from '@react-native-firebase/firestore';
261
262
// Connect to local emulator
263
if (__DEV__) {
264
// Connect to emulator running on localhost:8080
265
firestore().useEmulator('localhost', 8080);
266
console.log('Connected to Firestore emulator');
267
}
268
269
// Connect to emulator with custom configuration
270
if (__DEV__) {
271
const db = firestore();
272
273
// Use emulator
274
db.useEmulator('127.0.0.1', 8080);
275
276
// Configure for development
277
await db.settings({
278
persistence: false, // Disable persistence in development
279
cacheSizeBytes: 1024 * 1024 // 1 MB cache for testing
280
});
281
}
282
283
// Environment-based emulator configuration
284
const initializeFirestore = async () => {
285
const db = firestore();
286
287
if (process.env.NODE_ENV === 'development') {
288
// Development: use emulator
289
db.useEmulator('localhost', 8080);
290
291
await db.settings({
292
persistence: false,
293
ignoreUndefinedProperties: true
294
});
295
} else if (process.env.NODE_ENV === 'test') {
296
// Testing: use emulator with clean state
297
db.useEmulator('localhost', 8080);
298
299
await db.clearPersistence();
300
await db.settings({ persistence: false });
301
} else {
302
// Production: use real Firestore
303
await db.settings({
304
persistence: true,
305
cacheSizeBytes: 50 * 1024 * 1024,
306
ignoreUndefinedProperties: true
307
});
308
}
309
};
310
```
311
312
### Bundle Operations
313
314
Load pre-packaged data bundles for efficient initial data loading and execute pre-configured queries.
315
316
```typescript { .api }
317
/**
318
* Load a data bundle containing documents and queries
319
* @param bundle - Bundle data as string
320
* @returns Promise resolving to load bundle task progress
321
*/
322
loadBundle(bundle: string): Promise<FirebaseFirestoreTypes.LoadBundleTaskProgress>;
323
324
/**
325
* Get a pre-configured query from a loaded bundle
326
* @param queryName - Name of the query in the bundle
327
* @returns Query instance or null if not found
328
*/
329
namedQuery(queryName: string): FirebaseFirestoreTypes.Query | null;
330
331
interface LoadBundleTaskProgress {
332
/**
333
* Number of documents loaded so far
334
*/
335
documentsLoaded: number;
336
337
/**
338
* Total number of documents in the bundle
339
*/
340
totalDocuments: number;
341
342
/**
343
* Number of bytes loaded so far
344
*/
345
bytesLoaded: number;
346
347
/**
348
* Total number of bytes in the bundle
349
*/
350
totalBytes: number;
351
352
/**
353
* Current task state
354
*/
355
taskState: FirebaseFirestoreTypes.TaskState;
356
}
357
358
type TaskState = 'Error' | 'Running' | 'Success';
359
```
360
361
**Usage Examples:**
362
363
```typescript
364
import firestore from '@react-native-firebase/firestore';
365
366
// Load a data bundle
367
async function loadInitialData() {
368
const db = firestore();
369
370
try {
371
// Bundle data (typically fetched from server or included in app)
372
const bundleData = await fetch('/api/firestore-bundle').then(r => r.text());
373
374
// Load the bundle
375
const progress = await db.loadBundle(bundleData);
376
377
console.log('Bundle loaded successfully:');
378
console.log(`Documents: ${progress.documentsLoaded}/${progress.totalDocuments}`);
379
console.log(`Bytes: ${progress.bytesLoaded}/${progress.totalBytes}`);
380
console.log(`Status: ${progress.taskState}`);
381
382
// Use named queries from the bundle
383
const featuredProductsQuery = db.namedQuery('featured-products');
384
if (featuredProductsQuery) {
385
const snapshot = await featuredProductsQuery.get();
386
console.log(`Found ${snapshot.size} featured products`);
387
}
388
389
} catch (error) {
390
console.error('Error loading bundle:', error);
391
}
392
}
393
394
// Progressive bundle loading with progress tracking
395
async function loadBundleWithProgress(bundleData: string) {
396
const db = firestore();
397
398
// Note: In practice, you might want to implement progress tracking
399
// This is a simplified example showing the final result
400
const progress = await db.loadBundle(bundleData);
401
402
if (progress.taskState === 'Success') {
403
console.log('Bundle loaded successfully');
404
405
// Access bundled queries
406
const popularQuery = db.namedQuery('popular-items');
407
const recentQuery = db.namedQuery('recent-activity');
408
409
if (popularQuery && recentQuery) {
410
const [popularResults, recentResults] = await Promise.all([
411
popularQuery.get(),
412
recentQuery.get()
413
]);
414
415
console.log('Popular items:', popularResults.size);
416
console.log('Recent activity:', recentResults.size);
417
}
418
} else if (progress.taskState === 'Error') {
419
console.error('Bundle loading failed');
420
}
421
}
422
423
// Offline-first app initialization with bundles
424
async function initializeOfflineApp() {
425
const db = firestore();
426
427
// Load cached bundle for immediate offline access
428
try {
429
const cachedBundle = await getCachedBundle(); // Your caching logic
430
if (cachedBundle) {
431
await db.loadBundle(cachedBundle);
432
console.log('Offline bundle loaded');
433
}
434
} catch (error) {
435
console.log('No cached bundle available');
436
}
437
438
// Try to load fresh bundle when online
439
try {
440
const freshBundle = await fetchFreshBundle(); // Your fetch logic
441
if (freshBundle) {
442
await db.loadBundle(freshBundle);
443
console.log('Fresh bundle loaded');
444
await cacheBundleForOffline(freshBundle);
445
}
446
} catch (error) {
447
console.log('Could not load fresh bundle, using cached version');
448
}
449
}
450
```
451
452
### Cache Management
453
454
Access and manage persistent cache index operations.
455
456
```typescript { .api }
457
/**
458
* Get persistent cache index manager (null if persistence disabled)
459
* @returns PersistentCacheIndexManager instance or null
460
*/
461
persistentCacheIndexManager(): FirebaseFirestoreTypes.PersistentCacheIndexManager | null;
462
463
interface PersistentCacheIndexManager {
464
/**
465
* Enable automatic index creation for queries
466
* @returns Promise resolving when auto-creation is enabled
467
*/
468
enablePersistentCacheIndexAutoCreation(): Promise<void>;
469
470
/**
471
* Disable automatic index creation for queries
472
* @returns Promise resolving when auto-creation is disabled
473
*/
474
disablePersistentCacheIndexAutoCreation(): Promise<void>;
475
476
/**
477
* Delete all persistent cache indexes
478
* @returns Promise resolving when all indexes are deleted
479
*/
480
deleteAllPersistentCacheIndexes(): Promise<void>;
481
}
482
```
483
484
**Usage Examples:**
485
486
```typescript
487
import firestore from '@react-native-firebase/firestore';
488
489
// Manage cache indexes
490
async function manageCacheIndexes() {
491
const db = firestore();
492
const indexManager = db.persistentCacheIndexManager();
493
494
if (indexManager) {
495
// Enable automatic index creation
496
await indexManager.enablePersistentCacheIndexAutoCreation();
497
console.log('Auto index creation enabled');
498
499
// Later, if you need to clean up indexes
500
await indexManager.deleteAllPersistentCacheIndexes();
501
console.log('All cache indexes deleted');
502
503
// Disable auto creation
504
await indexManager.disablePersistentCacheIndexAutoCreation();
505
console.log('Auto index creation disabled');
506
} else {
507
console.log('Cache persistence is disabled - no index manager available');
508
}
509
}
510
511
// Initialize with index management
512
async function initializeWithCacheManagement() {
513
const db = firestore();
514
515
// Configure persistence
516
await db.settings({
517
persistence: true,
518
cacheSizeBytes: 100 * 1024 * 1024
519
});
520
521
// Manage indexes
522
const indexManager = db.persistentCacheIndexManager();
523
if (indexManager) {
524
await indexManager.enablePersistentCacheIndexAutoCreation();
525
}
526
}
527
```
528
529
### Offline Data Patterns
530
531
Best practices for working with offline data and handling connectivity changes.
532
533
**Usage Examples:**
534
535
```typescript
536
import firestore from '@react-native-firebase/firestore';
537
import NetInfo from '@react-native-netinfo/netinfo';
538
539
class OfflineManager {
540
private isOnline = true;
541
private pendingWrites: Array<() => Promise<void>> = [];
542
543
constructor() {
544
this.setupNetworkListener();
545
this.setupFirestoreSettings();
546
}
547
548
private async setupFirestoreSettings() {
549
await firestore().settings({
550
persistence: true,
551
cacheSizeBytes: 50 * 1024 * 1024,
552
ignoreUndefinedProperties: true,
553
serverTimestampBehavior: 'estimate'
554
});
555
}
556
557
private setupNetworkListener() {
558
NetInfo.addEventListener(state => {
559
const wasOnline = this.isOnline;
560
this.isOnline = state.isConnected ?? false;
561
562
if (!wasOnline && this.isOnline) {
563
this.handleConnectionRestored();
564
} else if (wasOnline && !this.isOnline) {
565
this.handleConnectionLost();
566
}
567
});
568
}
569
570
private async handleConnectionRestored() {
571
console.log('Connection restored - syncing data');
572
573
try {
574
// Enable network
575
await firestore().enableNetwork();
576
577
// Wait for pending writes to sync
578
await firestore().waitForPendingWrites();
579
580
console.log('All data synced successfully');
581
} catch (error) {
582
console.error('Error syncing data:', error);
583
}
584
}
585
586
private async handleConnectionLost() {
587
console.log('Connection lost - operating offline');
588
// Firestore automatically handles offline mode
589
// No need to explicitly disable network
590
}
591
592
// Queue writes for when connection is restored
593
async queueWrite(writeOperation: () => Promise<void>) {
594
if (this.isOnline) {
595
await writeOperation();
596
} else {
597
this.pendingWrites.push(writeOperation);
598
console.log('Write queued for when connection is restored');
599
}
600
}
601
602
// Get data with source preference
603
async getData(docRef: firestore.DocumentReference, preferCache = false) {
604
try {
605
if (preferCache || !this.isOnline) {
606
// Try cache first
607
const cacheSnapshot = await docRef.get({ source: 'cache' });
608
if (cacheSnapshot.exists) {
609
return cacheSnapshot;
610
}
611
}
612
613
// Fall back to server
614
return await docRef.get({ source: 'server' });
615
} catch (error) {
616
// If server fails, try cache
617
return await docRef.get({ source: 'cache' });
618
}
619
}
620
}
621
622
// Usage with real-time listeners that handle offline state
623
const setupOfflineAwareListener = (docRef: firestore.DocumentReference) => {
624
return docRef.onSnapshot(
625
{ includeMetadataChanges: true },
626
snapshot => {
627
if (snapshot.exists) {
628
const data = snapshot.data();
629
const isFromCache = snapshot.metadata.fromCache;
630
const hasPendingWrites = snapshot.metadata.hasPendingWrites;
631
632
console.log('Data:', data);
633
console.log('From cache:', isFromCache);
634
console.log('Has pending writes:', hasPendingWrites);
635
636
// Update UI with offline indicators
637
updateUI(data, { offline: isFromCache, pending: hasPendingWrites });
638
}
639
},
640
error => {
641
console.error('Listener error:', error);
642
// Handle offline errors gracefully
643
if (error.code === 'unavailable') {
644
console.log('Service unavailable - using cached data');
645
}
646
}
647
);
648
};
649
```
650
651
## Types
652
653
```typescript { .api }
654
/**
655
* Cache size constant for unlimited cache
656
*/
657
const CACHE_SIZE_UNLIMITED: -1;
658
659
/**
660
* Settings interface for Firestore configuration
661
*/
662
interface Settings {
663
persistence?: boolean;
664
cacheSizeBytes?: number;
665
host?: string;
666
ssl?: boolean;
667
ignoreUndefinedProperties?: boolean;
668
serverTimestampBehavior?: 'estimate' | 'previous' | 'none';
669
}
670
671
/**
672
* Get options for specifying data source
673
*/
674
interface GetOptions {
675
source: 'default' | 'server' | 'cache';
676
}
677
678
/**
679
* Persistent cache index manager for query optimization
680
*/
681
interface PersistentCacheIndexManager {
682
enablePersistentCacheIndexAutoCreation(): Promise<void>;
683
disablePersistentCacheIndexAutoCreation(): Promise<void>;
684
deleteAllPersistentCacheIndexes(): Promise<void>;
685
}
686
```