diff options
author | Sebastian Schmidt <mrschmidt@google.com> | 2017-09-15 16:45:46 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-15 16:45:46 -0700 |
commit | c6bde890ce2b352f86aa699b28f829d4cd85424c (patch) | |
tree | 4bb647eacb640c75d8447d4d2edc83d304ec2adb /Firebase/Database/Api | |
parent | 06a7c4f330fd7cdc01d16a15a277df538a49b25d (diff) |
Adding Multi-Resource support to the Firebase iOS SDK (#278)
* Adding Multi-Resource support to the Firebase iOS SDK.
This CL also makes RepoInfo hashable and simplifies RepoManager based on this.
Diffstat (limited to 'Firebase/Database/Api')
-rw-r--r-- | Firebase/Database/Api/FIRDatabase.m | 95 |
1 files changed, 68 insertions, 27 deletions
diff --git a/Firebase/Database/Api/FIRDatabase.m b/Firebase/Database/Api/FIRDatabase.m index 2dd77f6..4a8f5b8 100644 --- a/Firebase/Database/Api/FIRDatabase.m +++ b/Firebase/Database/Api/FIRDatabase.m @@ -36,6 +36,9 @@ @implementation FIRDatabase +/** A NSMutableDictionary of FirebaseApp name and FRepoInfo to FirebaseDatabase instance. */ +typedef NSMutableDictionary<NSString *, NSMutableDictionary<FRepoInfo *, FIRDatabase *> *> FIRDatabaseDictionary; + // The STR and STR_EXPAND macro allow a numeric version passed to he compiler driver // with a -D to be treated as a string instead of an invalid floating point value. #define STR(x) STR_EXPAND(x) @@ -51,13 +54,15 @@ static const char *FIREBASE_SEMVER = (const char *)STR(FIRDatabase_VERSION); NSString *appName = note.userInfo[kFIRAppNameKey]; if (appName == nil) { return; } - NSMutableDictionary *instances = [self instances]; + FIRDatabaseDictionary* instances = [self instances]; @synchronized (instances) { - FIRDatabase *deletedApp = instances[appName]; - if (deletedApp) { + NSMutableDictionary<FRepoInfo *, FIRDatabase *> *databaseInstances = instances[appName]; + if (databaseInstances) { // 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]; + for (FIRDatabase * database in [databaseInstances allValues]) { + [FRepoManager disposeRepos:database.config]; + } [instances removeObjectForKey:appName]; } } @@ -65,16 +70,18 @@ static const char *FIREBASE_SEMVER = (const char *)STR(FIRDatabase_VERSION); } /** - * A static NSMutableDictionary of FirebaseApp names to FirebaseDatabase instance. To ensure thread- - * safety, it should only be accessed in databaseForApp, which is synchronized. + * A static NSMutableDictionary of FirebaseApp name and FRepoInfo to + * FirebaseDatabase instance. To ensure thread-safety, it should only be + * accessed in databaseForApp:URL:, which is synchronized. * * TODO: This serves a duplicate purpose as RepoManager. We should clean up. - * TODO: We should maybe be conscious of leaks and make this a weak map or similar - * but we have a lot of work to do to allow FirebaseDatabase/Repo etc. to be GC'd. + * TODO: We should maybe be conscious of leaks and make this a weak map or + * similar but we have a lot of work to do to allow FirebaseDatabase/Repo etc. + * to be GC'd. */ -+ (NSMutableDictionary *)instances { ++ (FIRDatabaseDictionary *)instances { static dispatch_once_t pred = 0; - static NSMutableDictionary *instances; + static FIRDatabaseDictionary *instances; dispatch_once(&pred, ^{ instances = [NSMutableDictionary dictionary]; }); @@ -92,27 +99,59 @@ static const char *FIREBASE_SEMVER = (const char *)STR(FIRDatabase_VERSION); return [FIRDatabase databaseForApp:app]; } ++ (FIRDatabase *)databaseWithURL:(NSString *)url { + FIRApp *app = [FIRApp defaultApp]; + if (app == nil) { + [NSException raise:@"FIRAppNotConfigured" + format:@"Failed to get default Firebase Database instance. " + @"Must call `[FIRApp configure]` (`FirebaseApp.configure()` in Swift) " + @"before using Firebase Database."]; + } + return [FIRDatabase databaseForApp:app URL:url]; +} + + (FIRDatabase *)databaseForApp:(FIRApp *)app { if (app == nil) { [NSException raise:@"InvalidFIRApp" format:@"nil FIRApp instance passed to databaseForApp."]; } - NSMutableDictionary *instances = [self instances]; - @synchronized (instances) { - FIRDatabase *database = instances[app.name]; - if (!database) { - NSString *databaseUrl = app.options.databaseURL; - if (databaseUrl == nil) { - [NSException raise:@"MissingDatabaseURL" format:@"Failed to get FIRDatabase instance: FIRApp object has no " - "databaseURL in its FirebaseOptions object."]; - } - FParsedUrl *parsedUrl = [FUtilities parseUrl:databaseUrl]; - if (![parsedUrl.path isEmpty]) { - [NSException raise:@"InvalidDatabaseURL" format:@"Configured Database URL '%@' is invalid. It should " - "point to the root of a Firebase Database but it includes a path: %@", - databaseUrl, [parsedUrl.path toString]]; - } + return [FIRDatabase databaseForApp:app URL:app.options.databaseURL]; +} ++ (FIRDatabase *)databaseForApp:(FIRApp *)app URL:(NSString *)url { + if (app == nil) { + [NSException raise:@"InvalidFIRApp" + format:@"nil FIRApp instance passed to databaseForApp."]; + } + + if (url == nil) { + [NSException raise:@"MissingDatabaseURL" + format:@"Failed to get FirebaseDatabase instance: " + "Specify DatabaseURL within FIRApp or from your databaseForApp:URL: call."]; + } + + NSURL *databaseUrl = [NSURL URLWithString:url]; + + if (databaseUrl == nil) { + [NSException raise:@"InvalidDatabaseURL" format:@"The Database URL '%@' cannot be parsed. " + "Specify a valid DatabaseURL within FIRApp or from your databaseForApp:URL: call.", databaseUrl]; + } else if (![databaseUrl.path isEqualToString:@""] && ![databaseUrl.path isEqualToString:@"/"]) { + [NSException raise:@"InvalidDatabaseURL" format:@"Configured Database URL '%@' is invalid. It should point " + "to the root of a Firebase Database but it includes a path: %@",databaseUrl, databaseUrl.path]; + } + + FIRDatabaseDictionary *instances = [self instances]; + @synchronized (instances) { + NSMutableDictionary<FRepoInfo *, FIRDatabase *> *urlInstanceMap = + instances[app.name]; + if (!urlInstanceMap) { + urlInstanceMap = [NSMutableDictionary dictionary]; + instances[app.name] = urlInstanceMap; + } + + FParsedUrl *parsedUrl = [FUtilities parseUrl:databaseUrl.absoluteString]; + FIRDatabase *database = urlInstanceMap[parsedUrl.repoInfo]; + if (!database) { id<FAuthTokenProvider> authTokenProvider = [FAuthTokenProvider authTokenProviderForApp:app]; // If this is the default app, don't set the session persistence key so that we use our @@ -125,8 +164,10 @@ static const char *FIREBASE_SEMVER = (const char *)STR(FIRDatabase_VERSION); FIRDatabaseConfig *config = [[FIRDatabaseConfig alloc] initWithSessionIdentifier:sessionIdentifier authTokenProvider:authTokenProvider]; - database = [[FIRDatabase alloc] initWithApp:app repoInfo:parsedUrl.repoInfo config:config]; - instances[app.name] = database; + database = [[FIRDatabase alloc] initWithApp:app + repoInfo:parsedUrl.repoInfo + config:config]; + urlInstanceMap[parsedUrl.repoInfo] = database; } return database; |