diff options
author | Ryan Wilson <wilsonryan@google.com> | 2018-02-16 17:03:14 -0500 |
---|---|---|
committer | Gil <mcg@google.com> | 2018-02-16 14:03:14 -0800 |
commit | a9f3f35d483f1031ef2e2860aeda921f56e1bf08 (patch) | |
tree | 6df1a5ae7487f28af10b783935e914b70e5197b3 /Firestore | |
parent | aa6f1ae0993ed948fb0b39283561bb3321eea5e9 (diff) |
Delete stale Firestore instances after FIRApp is deleted. (#809)
Diffstat (limited to 'Firestore')
-rw-r--r-- | Firestore/Example/Tests/API/FIRFirestoreTests.mm | 62 | ||||
-rw-r--r-- | Firestore/Source/API/FIRFirestore.mm | 35 |
2 files changed, 96 insertions, 1 deletions
diff --git a/Firestore/Example/Tests/API/FIRFirestoreTests.mm b/Firestore/Example/Tests/API/FIRFirestoreTests.mm new file mode 100644 index 0000000..4daf35a --- /dev/null +++ b/Firestore/Example/Tests/API/FIRFirestoreTests.mm @@ -0,0 +1,62 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import <FirebaseCore/FIRAppInternal.h> +#import <FirebaseCore/FIROptionsInternal.h> +#import <FirebaseFirestore/FIRFirestore.h> + +#import <XCTest/XCTest.h> + +@interface FIRFirestoreTests : XCTestCase +@end + +@implementation FIRFirestoreTests + +- (void)testDeleteApp { + // Create a FIRApp for testing. + NSString *appName = @"custom_app_name"; + FIROptions *options = + [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123ab" GCMSenderID:@"gcm_sender_id"]; + options.projectID = @"project_id"; + [FIRApp configureWithName:appName options:options]; + + // Ensure the app is set appropriately. + FIRApp *app = [FIRApp appNamed:appName]; + FIRFirestore *firestore = [FIRFirestore firestoreForApp:app]; + XCTAssertEqualObjects(firestore.app, app); + + // Ensure that firestoreForApp returns the same instance. + XCTAssertEqualObjects(firestore, [FIRFirestore firestoreForApp:app]); + + XCTestExpectation *defaultAppDeletedExpectation = + [self expectationWithDescription: + @"Deleting the default app should invalidate the default " + @"Firestore instance."]; + [app deleteApp:^(BOOL success) { + // Recreate the FIRApp with the same name, fetch a new Firestore instance and make sure it's + // different than the other one. + [FIRApp configureWithName:appName options:options]; + FIRApp *newApp = [FIRApp appNamed:appName]; + FIRFirestore *newInstance = [FIRFirestore firestoreForApp:newApp]; + XCTAssertNotEqualObjects(newInstance, firestore); + + [defaultAppDeletedExpectation fulfill]; + }]; + + [self waitForExpectations:@[ defaultAppDeletedExpectation ] timeout:2]; +} + +@end diff --git a/Firestore/Source/API/FIRFirestore.mm b/Firestore/Source/API/FIRFirestore.mm index 6d2d27b..5a50710 100644 --- a/Firestore/Source/API/FIRFirestore.mm +++ b/Firestore/Source/API/FIRFirestore.mm @@ -16,7 +16,7 @@ #import "FIRFirestore.h" -#import <FirebaseCore/FIRApp.h> +#import <FirebaseCore/FIRAppInternal.h> #import <FirebaseCore/FIRLogger.h> #import <FirebaseCore/FIROptions.h> @@ -80,6 +80,36 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; return instances; } ++ (void)initialize { + NSNotificationCenter *center = [NSNotificationCenter defaultCenter]; + [center addObserverForName:kFIRAppDeleteNotification + object:nil + queue:nil + usingBlock:^(NSNotification *_Nonnull note) { + NSString *appName = note.userInfo[kFIRAppNameKey]; + if (appName == nil) return; + + NSMutableDictionary *instances = [self instances]; + @synchronized(instances) { + // Since the key for instances isn't just the app name, iterate over all the + // keys to get the one(s) we have to delete. There could be multiple in case + // the user calls firestoreForApp:database:. + NSMutableArray *keysToDelete = [[NSMutableArray alloc] init]; + NSString *keyPrefix = [NSString stringWithFormat:@"%@|", appName]; + for (NSString *key in instances.allKeys) { + if ([key hasPrefix:keyPrefix]) { + [keysToDelete addObject:key]; + } + } + + // Loop through the keys found and delete them from the stored instances. + for (NSString *key in keysToDelete) { + [instances removeObjectForKey:key]; + } + } + }]; +} + + (instancetype)firestore { FIRApp *app = [FIRApp defaultApp]; if (!app) { @@ -109,6 +139,9 @@ extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain"; "database", util::WrapNSStringNoCopy(DatabaseId::kDefaultDatabaseId)); } + + // Note: If the key format changes, please change the code that detects FIRApps being deleted + // contained in +initialize. It checks for the app's name followed by a | character. NSString *key = [NSString stringWithFormat:@"%@|%@", app.name, database]; NSMutableDictionary<NSString *, FIRFirestore *> *instances = self.instances; |