diff --git a/Example/Database/Tests/Integration/FIRDatabaseTests.m b/Example/Database/Tests/Integration/FIRDatabaseTests.m index 3b20a461995..4bdb773b5a9 100644 --- a/Example/Database/Tests/Integration/FIRDatabaseTests.m +++ b/Example/Database/Tests/Integration/FIRDatabaseTests.m @@ -53,6 +53,56 @@ - (void) testDatabaseForAppWithInvalidURLs { XCTAssertThrows([self databaseForURL:@"http://x.example.com/paths/are/bad"]); } +- (void) testDeleteDatabase { + FIRDatabase *defaultDatabase = [FIRDatabase database]; + FIRApp *defaultApp = [FIRApp defaultApp]; + XCTAssertEqualObjects(defaultDatabase.app, defaultApp); + + // Set up expectation for the default app to be deleted. + XCTestExpectation *defaultAppDeletedExpectation = + [self expectationWithDescription:@"Deleting the default app should invalidate the default " + @"database."]; + [defaultApp deleteApp:^(BOOL success) { + // Deleting the default app should make the default database unavailable. + XCTAssertThrows([FIRDatabase database]); + + [defaultAppDeletedExpectation fulfill]; + }]; + + // Wait for the default app to be deleted. + [self waitForExpectations:@[defaultAppDeletedExpectation] timeout:2]; + + // Set up a custom FIRApp with a custom database based on it. + FIROptions *options = [[FIROptions alloc] initWithGoogleAppID:@"1:123:ios:123abc" + GCMSenderID:@"gcm_sender_id"]; + options.databaseURL = self.databaseURL; + NSString *customAppName = @"MyCustomApp"; + [FIRApp configureWithName:customAppName options:options]; + FIRApp *customApp = [FIRApp appNamed:customAppName]; + FIRDatabase *customDatabase = [FIRDatabase databaseForApp:customApp]; + XCTAssertNotNil(customDatabase); + + // Delete the custom app and wait for it to be done. + XCTestExpectation *customAppDeletedExpectation = + [self expectationWithDescription:@"Deleting the custom app should be successful."]; + [customApp deleteApp:^(BOOL success) { + // The app shouldn't exist anymore, ensure that the databaseForApp throws. + XCTAssertThrows([FIRDatabase databaseForApp:[FIRApp appNamed:customAppName]]); + + [customAppDeletedExpectation fulfill]; + }]; + + // Wait for the custom app to be deleted. + [self waitForExpectations:@[customAppDeletedExpectation] timeout:2]; + + // Configure the app again, then grab a reference to the database. Assert it's different. + [FIRApp configureWithName:customAppName options:options]; + FIRApp *secondCustomApp = [FIRApp appNamed:customAppName]; + FIRDatabase *secondCustomDatabase = [FIRDatabase databaseForApp:secondCustomApp]; + XCTAssertNotNil(secondCustomDatabase); + XCTAssertNotEqualObjects(customDatabase, secondCustomDatabase); +} + - (void) testReferenceWithPath { FIRDatabase *db = [self defaultDatabase]; NSString *expectedURL = [NSString stringWithFormat:@"%@/foo", self.databaseURL]; diff --git a/Firebase/Database/Api/FIRDatabase.m b/Firebase/Database/Api/FIRDatabase.m index 38ccd54e7c1..6ee163fc58a 100644 --- a/Firebase/Database/Api/FIRDatabase.m +++ b/Firebase/Database/Api/FIRDatabase.m @@ -42,6 +42,26 @@ @implementation FIRDatabase #define STR_EXPAND(x) #x static const char *FIREBASE_SEMVER = (const char *)STR(FIRDatabase_VERSION); ++ (void)load { + 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) { + FIRDatabase *deletedApp = instances[appName]; + // Clean up the deleted instance in an effort to remove any resources still in use. + // Note: Any leftover instances of this exact database will be invalid. + [FRepoManager disposeRepos:deletedApp.config]; + [instances removeObjectForKey:appName]; + } + }]; +} + /** * A static NSMutableDictionary of FirebaseApp names to FirebaseDatabase instance. To ensure thread- * safety, it should only be accessed in databaseForApp, which is synchronized.