Skip to content

Commit 806c15d

Browse files
feat(firestore): support for PersistentCacheIndexManager for firestore instances for managing cache indexes. (#13070)
* feat(firestore): initial setup for `PersistentCacheIndexManager` * chore: fix melos format script * fix(firestore): update pigeon API for new cache manager methods * feat: update method handlers for cache manager * feat(firestore): persistence cache manager implementation for ios + android * feat(firestore): cache manager index implementation for web * refactor: use only one method for native to save duplication * chore: format * refactor(firestore): method channel use single method for persistence manager * chore: update method channel name * fix(web: remove firestore instance from invocation * fix: add break statements to ios switch statement * chore: add license header * chore: fix analyser issues * fix(apple): revert back to `index` to fix * tests(firestore): `PersistentCacheIndexManager` * chore: make delegate private * chore: remove obsolete code * refactor: return null if persistence is not enabled * chore: fix test scoping * fix: settings on firestore web was always creating new Settings object * tests: test every scenario for `PersistenceCacheIndexManager` * chore: fix analyse
1 parent 81844f4 commit 806c15d

24 files changed

+712
-25
lines changed

‎melos.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ scripts:
229229
melos exec -- "flutter pub run pigeon --input ./pigeons/messages.dart" && \
230230
melos run generate:pigeon:macos --no-select && \
231231
melos run generate:pigeon:android --no-select && \
232-
melos run format --no-select
232+
melos run format-ci --no-select
233233
packageFilters:
234234
fileExists: 'pigeons/messages.dart'
235235
description: Generate the pigeon messages for all the supported packages.

‎packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static com.google.firebase.firestore.AggregateField.sum;
1010

1111
import android.app.Activity;
12+
import android.util.Log;
1213
import androidx.annotation.NonNull;
1314
import androidx.annotation.Nullable;
1415
import com.google.android.gms.tasks.Task;
@@ -24,6 +25,7 @@
2425
import com.google.firebase.firestore.FirebaseFirestore;
2526
import com.google.firebase.firestore.FirebaseFirestoreSettings;
2627
import com.google.firebase.firestore.MemoryCacheSettings;
28+
import com.google.firebase.firestore.PersistentCacheIndexManager;
2729
import com.google.firebase.firestore.PersistentCacheSettings;
2830
import com.google.firebase.firestore.Query;
2931
import com.google.firebase.firestore.QuerySnapshot;
@@ -64,7 +66,7 @@ public class FlutterFirebaseFirestorePlugin
6466
GeneratedAndroidFirebaseFirestore.FirebaseFirestoreHostApi {
6567
protected static final HashMap<FirebaseFirestore, FlutterFirebaseFirestoreExtension>
6668
firestoreInstanceCache = new HashMap<>();
67-
69+
public static final String TAG = "FlutterFirestorePlugin";
6870
public static final String DEFAULT_ERROR_CODE = "firebase_firestore";
6971

7072
private static final String METHOD_CHANNEL_NAME = "plugins.flutter.io/firebase_firestore";
@@ -473,6 +475,35 @@ public void setIndexConfiguration(
473475
});
474476
}
475477

478+
@Override
479+
public void persistenceCacheIndexManagerRequest(
480+
@NonNull GeneratedAndroidFirebaseFirestore.FirestorePigeonFirebaseApp app,
481+
@NonNull GeneratedAndroidFirebaseFirestore.PersistenceCacheIndexManagerRequest request,
482+
@NonNull GeneratedAndroidFirebaseFirestore.Result<Void> result) {
483+
cachedThreadPool.execute(
484+
() -> {
485+
PersistentCacheIndexManager indexManager =
486+
getFirestoreFromPigeon(app).getPersistentCacheIndexManager();
487+
if (indexManager != null) {
488+
switch (request) {
489+
case ENABLE_INDEX_AUTO_CREATION:
490+
indexManager.enableIndexAutoCreation();
491+
break;
492+
case DISABLE_INDEX_AUTO_CREATION:
493+
indexManager.disableIndexAutoCreation();
494+
break;
495+
case DELETE_ALL_INDEXES:
496+
indexManager.deleteAllIndexes();
497+
break;
498+
}
499+
} else {
500+
Log.d(TAG, "`PersistentCacheIndexManager` is not available.");
501+
}
502+
503+
result.success(null);
504+
});
505+
}
506+
476507
@Override
477508
public void setLoggingEnabled(
478509
@NonNull Boolean loggingEnabled,

‎packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/GeneratedAndroidFirebaseFirestore.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,22 @@ private AggregateSource(final int index) {
166166
}
167167
}
168168

169+
/**
170+
* [PersistenceCacheIndexManagerRequest] represents the request types for the persistence cache
171+
* index manager.
172+
*/
173+
public enum PersistenceCacheIndexManagerRequest {
174+
ENABLE_INDEX_AUTO_CREATION(0),
175+
DISABLE_INDEX_AUTO_CREATION(1),
176+
DELETE_ALL_INDEXES(2);
177+
178+
final int index;
179+
180+
private PersistenceCacheIndexManagerRequest(final int index) {
181+
this.index = index;
182+
}
183+
}
184+
169185
public enum PigeonTransactionResult {
170186
SUCCESS(0),
171187
FAILURE(1);
@@ -1815,6 +1831,11 @@ void documentReferenceSnapshot(
18151831
@NonNull ListenSource source,
18161832
@NonNull Result<String> result);
18171833

1834+
void persistenceCacheIndexManagerRequest(
1835+
@NonNull FirestorePigeonFirebaseApp app,
1836+
@NonNull PersistenceCacheIndexManagerRequest request,
1837+
@NonNull Result<Void> result);
1838+
18181839
/** The codec used by FirebaseFirestoreHostApi. */
18191840
static @NonNull MessageCodec<Object> getCodec() {
18201841
return FirebaseFirestoreHostApiCodec.INSTANCE;
@@ -2570,6 +2591,39 @@ public void error(Throwable error) {
25702591
channel.setMessageHandler(null);
25712592
}
25722593
}
2594+
{
2595+
BasicMessageChannel<Object> channel =
2596+
new BasicMessageChannel<>(
2597+
binaryMessenger,
2598+
"dev.flutter.pigeon.cloud_firestore_platform_interface.FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest",
2599+
getCodec());
2600+
if (api != null) {
2601+
channel.setMessageHandler(
2602+
(message, reply) -> {
2603+
ArrayList<Object> wrapped = new ArrayList<Object>();
2604+
ArrayList<Object> args = (ArrayList<Object>) message;
2605+
FirestorePigeonFirebaseApp appArg = (FirestorePigeonFirebaseApp) args.get(0);
2606+
PersistenceCacheIndexManagerRequest requestArg =
2607+
PersistenceCacheIndexManagerRequest.values()[(int) args.get(1)];
2608+
Result<Void> resultCallback =
2609+
new Result<Void>() {
2610+
public void success(Void result) {
2611+
wrapped.add(0, null);
2612+
reply.reply(wrapped);
2613+
}
2614+
2615+
public void error(Throwable error) {
2616+
ArrayList<Object> wrappedError = wrapError(error);
2617+
reply.reply(wrappedError);
2618+
}
2619+
};
2620+
2621+
api.persistenceCacheIndexManagerRequest(appArg, requestArg, resultCallback);
2622+
});
2623+
} else {
2624+
channel.setMessageHandler(null);
2625+
}
2626+
}
25732627
}
25742628
}
25752629
}

‎packages/cloud_firestore/cloud_firestore/example/integration_test/instance_e2e.dart

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:async';
66
import 'dart:convert';
77

88
import 'package:cloud_firestore/cloud_firestore.dart';
9+
import 'package:firebase_core/firebase_core.dart';
910
import 'package:flutter/foundation.dart';
1011
import 'package:flutter_test/flutter_test.dart';
1112

@@ -312,6 +313,121 @@ void runInstanceTests() {
312313
{'some': 'data'},
313314
);
314315
});
316+
testWidgets(
317+
'`PersistenceCacheIndexManager` with default persistence settings for each platform',
318+
(widgetTester) async {
319+
if (defaultTargetPlatform == TargetPlatform.windows) {
320+
try {
321+
// Windows does not have `PersistenceCacheIndexManager` support
322+
FirebaseFirestore.instance.persistentCacheIndexManager();
323+
} catch (e) {
324+
expect(e, isInstanceOf<UnimplementedError>());
325+
}
326+
} else {
327+
if (kIsWeb) {
328+
// persistence is disabled by default on web
329+
final firestore = FirebaseFirestore.instanceFor(
330+
app: Firebase.app(),
331+
// Use different firestore instance to test behavior
332+
databaseId: 'default-web',
333+
);
334+
PersistentCacheIndexManager? indexManager =
335+
firestore.persistentCacheIndexManager();
336+
expect(indexManager, isNull);
337+
} else {
338+
final firestore = FirebaseFirestore.instanceFor(
339+
app: Firebase.app(),
340+
// Use different firestore instance to test behavior
341+
databaseId: 'default-other-platform-test',
342+
);
343+
// macOS, android, iOS have persistence enabled by default
344+
PersistentCacheIndexManager? indexManager =
345+
firestore.persistentCacheIndexManager();
346+
await indexManager!.enableIndexAutoCreation();
347+
await indexManager.disableIndexAutoCreation();
348+
await indexManager.deleteAllIndexes();
349+
}
350+
}
351+
},
352+
);
353+
354+
testWidgets(
355+
'`PersistenceCacheIndexManager` with persistence enabled for each platform',
356+
(widgetTester) async {
357+
if (kIsWeb) {
358+
final firestore = FirebaseFirestore.instanceFor(
359+
app: Firebase.app(),
360+
databaseId: 'web-enabled',
361+
);
362+
// persistence is disabled by default so we enable it
363+
firestore.settings = const Settings(persistenceEnabled: true);
364+
365+
PersistentCacheIndexManager? indexManager =
366+
firestore.persistentCacheIndexManager();
367+
368+
await indexManager!.enableIndexAutoCreation();
369+
await indexManager.disableIndexAutoCreation();
370+
await indexManager.deleteAllIndexes();
371+
372+
final firestore2 = FirebaseFirestore.instanceFor(
373+
app: Firebase.app(),
374+
databaseId: 'web-disabled-2',
375+
);
376+
377+
// Now try using `enablePersistence()`, web only API
378+
await firestore2.enablePersistence();
379+
380+
PersistentCacheIndexManager? indexManager2 =
381+
firestore2.persistentCacheIndexManager();
382+
383+
await indexManager2!.enableIndexAutoCreation();
384+
await indexManager2.disableIndexAutoCreation();
385+
await indexManager2.deleteAllIndexes();
386+
} else {
387+
final firestore = FirebaseFirestore.instanceFor(
388+
app: Firebase.app(),
389+
databaseId: 'other-platform-enabled',
390+
);
391+
firestore.settings = const Settings(persistenceEnabled: true);
392+
PersistentCacheIndexManager? indexManager =
393+
firestore.persistentCacheIndexManager();
394+
await indexManager!.enableIndexAutoCreation();
395+
await indexManager.disableIndexAutoCreation();
396+
await indexManager.deleteAllIndexes();
397+
}
398+
},
399+
skip: defaultTargetPlatform == TargetPlatform.windows,
400+
);
401+
402+
testWidgets(
403+
'`PersistenceCacheIndexManager` with persistence disabled for each platform',
404+
(widgetTester) async {
405+
if (kIsWeb) {
406+
final firestore = FirebaseFirestore.instanceFor(
407+
app: Firebase.app(),
408+
databaseId: 'web-disabled-1',
409+
);
410+
// persistence is disabled by default so we enable it
411+
firestore.settings = const Settings(persistenceEnabled: false);
412+
413+
PersistentCacheIndexManager? indexManager =
414+
firestore.persistentCacheIndexManager();
415+
416+
expect(indexManager, isNull);
417+
} else {
418+
final firestore = FirebaseFirestore.instanceFor(
419+
app: Firebase.app(),
420+
databaseId: 'other-platform-disabled',
421+
);
422+
// macOS, android, iOS have persistence enabled by default so we disable it
423+
firestore.settings = const Settings(persistenceEnabled: false);
424+
PersistentCacheIndexManager? indexManager =
425+
firestore.persistentCacheIndexManager();
426+
expect(indexManager, isNull);
427+
}
428+
},
429+
skip: defaultTargetPlatform == TargetPlatform.windows,
430+
);
315431
},
316432
);
317433
}

‎packages/cloud_firestore/cloud_firestore/ios/Classes/FLTFirebaseFirestorePlugin.m

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#import <firebase_core/FLTFirebasePluginRegistry.h>
77

88
#import <TargetConditionals.h>
9+
#import "FirebaseFirestoreInternal/FIRPersistentCacheIndexManager.h"
910
#import "Private/FLTDocumentSnapshotStreamHandler.h"
1011
#import "Private/FLTFirebaseFirestoreReader.h"
1112
#import "Private/FLTFirebaseFirestoreUtils.h"
@@ -566,6 +567,31 @@ - (void)setIndexConfigurationApp:(nonnull FirestorePigeonFirebaseApp *)app
566567
}];
567568
}
568569

570+
- (void)persistenceCacheIndexManagerRequestApp:(FirestorePigeonFirebaseApp *)app
571+
request:(PersistenceCacheIndexManagerRequest)request
572+
completion:(void (^)(FlutterError *_Nullable))completion {
573+
FIRPersistentCacheIndexManager *persistentCacheIndexManager =
574+
[self getFIRFirestoreFromAppNameFromPigeon:app].persistentCacheIndexManager;
575+
576+
if (persistentCacheIndexManager) {
577+
switch (request) {
578+
case PersistenceCacheIndexManagerRequestEnableIndexAutoCreation:
579+
[persistentCacheIndexManager enableIndexAutoCreation];
580+
break;
581+
case PersistenceCacheIndexManagerRequestDisableIndexAutoCreation:
582+
[persistentCacheIndexManager disableIndexAutoCreation];
583+
break;
584+
case PersistenceCacheIndexManagerRequestDeleteAllIndexes:
585+
[persistentCacheIndexManager deleteAllIndexes];
586+
break;
587+
}
588+
} else {
589+
// Put because `persistentCacheIndexManager` is a nullable property
590+
NSLog(@"FLTFirebaseFirestore: `PersistentCacheIndexManager` is not available.");
591+
}
592+
completion(nil);
593+
}
594+
569595
- (void)setLoggingEnabledLoggingEnabled:(nonnull NSNumber *)loggingEnabled
570596
completion:(nonnull void (^)(FlutterError *_Nullable))completion {
571597
[FIRFirestore enableLogging:[loggingEnabled boolValue]];

‎packages/cloud_firestore/cloud_firestore/ios/Classes/FirestoreMessages.g.m

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ - (instancetype)initWithValue:(AggregateSource)value {
7777
}
7878
@end
7979

80+
/// [PersistenceCacheIndexManagerRequest] represents the request types for the persistence cache
81+
/// index manager.
82+
@implementation PersistenceCacheIndexManagerRequestBox
83+
- (instancetype)initWithValue:(PersistenceCacheIndexManagerRequest)value {
84+
self = [super init];
85+
if (self) {
86+
_value = value;
87+
}
88+
return self;
89+
}
90+
@end
91+
8092
@implementation PigeonTransactionResultBox
8193
- (instancetype)initWithValue:(PigeonTransactionResult)value {
8294
self = [super init];
@@ -1340,4 +1352,31 @@ void FirebaseFirestoreHostApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
13401352
[channel setMessageHandler:nil];
13411353
}
13421354
}
1355+
{
1356+
FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
1357+
initWithName:@"dev.flutter.pigeon.cloud_firestore_platform_interface."
1358+
@"FirebaseFirestoreHostApi.persistenceCacheIndexManagerRequest"
1359+
binaryMessenger:binaryMessenger
1360+
codec:FirebaseFirestoreHostApiGetCodec()];
1361+
if (api) {
1362+
NSCAssert([api respondsToSelector:@selector
1363+
(persistenceCacheIndexManagerRequestApp:request:completion:)],
1364+
@"FirebaseFirestoreHostApi api (%@) doesn't respond to "
1365+
@"@selector(persistenceCacheIndexManagerRequestApp:request:completion:)",
1366+
api);
1367+
[channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
1368+
NSArray *args = message;
1369+
FirestorePigeonFirebaseApp *arg_app = GetNullableObjectAtIndex(args, 0);
1370+
PersistenceCacheIndexManagerRequest arg_request =
1371+
[GetNullableObjectAtIndex(args, 1) integerValue];
1372+
[api persistenceCacheIndexManagerRequestApp:arg_app
1373+
request:arg_request
1374+
completion:^(FlutterError *_Nullable error) {
1375+
callback(wrapResult(nil, error));
1376+
}];
1377+
}];
1378+
} else {
1379+
[channel setMessageHandler:nil];
1380+
}
1381+
}
13431382
}

‎packages/cloud_firestore/cloud_firestore/ios/Classes/Public/FirestoreMessages.g.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,20 @@ typedef NS_ENUM(NSUInteger, AggregateSource) {
109109
- (instancetype)initWithValue:(AggregateSource)value;
110110
@end
111111

112+
/// [PersistenceCacheIndexManagerRequest] represents the request types for the persistence cache
113+
/// index manager.
114+
typedef NS_ENUM(NSUInteger, PersistenceCacheIndexManagerRequest) {
115+
PersistenceCacheIndexManagerRequestEnableIndexAutoCreation = 0,
116+
PersistenceCacheIndexManagerRequestDisableIndexAutoCreation = 1,
117+
PersistenceCacheIndexManagerRequestDeleteAllIndexes = 2,
118+
};
119+
120+
/// Wrapper for PersistenceCacheIndexManagerRequest to allow for nullability.
121+
@interface PersistenceCacheIndexManagerRequestBox : NSObject
122+
@property(nonatomic, assign) PersistenceCacheIndexManagerRequest value;
123+
- (instancetype)initWithValue:(PersistenceCacheIndexManagerRequest)value;
124+
@end
125+
112126
typedef NS_ENUM(NSUInteger, PigeonTransactionResult) {
113127
PigeonTransactionResultSuccess = 0,
114128
PigeonTransactionResultFailure = 1,
@@ -399,6 +413,9 @@ NSObject<FlutterMessageCodec> *FirebaseFirestoreHostApiGetCodec(void);
399413
source:(ListenSource)source
400414
completion:
401415
(void (^)(NSString *_Nullable, FlutterError *_Nullable))completion;
416+
- (void)persistenceCacheIndexManagerRequestApp:(FirestorePigeonFirebaseApp *)app
417+
request:(PersistenceCacheIndexManagerRequest)request
418+
completion:(void (^)(FlutterError *_Nullable))completion;
402419
@end
403420

404421
extern void FirebaseFirestoreHostApiSetup(id<FlutterBinaryMessenger> binaryMessenger,

0 commit comments

Comments
 (0)