aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firebase/Database
diff options
context:
space:
mode:
authorGravatar Sebastian Schmidt <mrschmidt@google.com>2017-09-15 16:45:46 -0700
committerGravatar GitHub <noreply@github.com>2017-09-15 16:45:46 -0700
commitc6bde890ce2b352f86aa699b28f829d4cd85424c (patch)
tree4bb647eacb640c75d8447d4d2edc83d304ec2adb /Firebase/Database
parent06a7c4f330fd7cdc01d16a15a277df538a49b25d (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')
-rw-r--r--Firebase/Database/Api/FIRDatabase.m95
-rw-r--r--Firebase/Database/Core/FRepoInfo.h6
-rw-r--r--Firebase/Database/Core/FRepoInfo.m19
-rw-r--r--Firebase/Database/Core/FRepoManager.m55
-rw-r--r--Firebase/Database/Public/FIRDatabase.h21
5 files changed, 142 insertions, 54 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;
diff --git a/Firebase/Database/Core/FRepoInfo.h b/Firebase/Database/Core/FRepoInfo.h
index dace937..433bf35 100644
--- a/Firebase/Database/Core/FRepoInfo.h
+++ b/Firebase/Database/Core/FRepoInfo.h
@@ -16,7 +16,7 @@
#import <Foundation/Foundation.h>
-@interface FRepoInfo : NSObject
+@interface FRepoInfo : NSObject <NSCopying>
@property (nonatomic, readonly, strong) NSString* host;
@property (nonatomic, readonly, strong) NSString* namespace;
@@ -31,4 +31,8 @@
- (BOOL) isDemoHost;
- (BOOL) isCustomHost;
+- (id)copyWithZone:(NSZone *)zone;
+- (NSUInteger)hash;
+- (BOOL)isEqual:(id)anObject;
+
@end
diff --git a/Firebase/Database/Core/FRepoInfo.m b/Firebase/Database/Core/FRepoInfo.m
index 6b15fe5..925163e 100644
--- a/Firebase/Database/Core/FRepoInfo.m
+++ b/Firebase/Database/Core/FRepoInfo.m
@@ -112,4 +112,23 @@
return url;
}
+- (id)copyWithZone:(NSZone *)zone; {
+ return self; // Immutable
+}
+
+- (NSUInteger)hash {
+ NSUInteger result = host.hash;
+ result = 31 * result + (secure ? 1 : 0);
+ result = 31 * result + namespace.hash;
+ result = 31 * result + host.hash;
+ return result;
+}
+
+- (BOOL)isEqual:(id)anObject {
+ if (![anObject isKindOfClass:[FRepoInfo class]]) return NO;
+ FRepoInfo *other = (FRepoInfo *)anObject;
+ return secure == other.secure && [host isEqualToString:other.host] &&
+ [namespace isEqualToString:other.namespace];
+}
+
@end
diff --git a/Firebase/Database/Core/FRepoManager.m b/Firebase/Database/Core/FRepoManager.m
index 6a134d2..c5194d5 100644
--- a/Firebase/Database/Core/FRepoManager.m
+++ b/Firebase/Database/Core/FRepoManager.m
@@ -24,9 +24,13 @@
@implementation FRepoManager
-+ (NSMutableDictionary *)configs {
+typedef NSMutableDictionary<NSString *,
+ NSMutableDictionary<FRepoInfo *, FRepo *> *>
+ FRepoDictionary;
+
++ (FRepoDictionary *)configs {
static dispatch_once_t pred = 0;
- static NSMutableDictionary *configs;
+ static FRepoDictionary *configs;
dispatch_once(&pred, ^{
configs = [NSMutableDictionary dictionary];
});
@@ -39,33 +43,32 @@
*/
+ (FRepo *) getRepo:(FRepoInfo *)repoInfo config:(FIRDatabaseConfig *)config {
[config freeze];
- NSString* repoHashString = [NSString stringWithFormat:@"%@_%@", repoInfo.host, repoInfo.namespace];
- NSMutableDictionary *configs = [FRepoManager configs];
+ FRepoDictionary *configs = [FRepoManager configs];
@synchronized(configs) {
- NSMutableDictionary *repos = configs[config.sessionIdentifier];
- if (!repos || repos[repoHashString] == nil) {
+ NSMutableDictionary<FRepoInfo *, FRepo *> *repos = configs[config.sessionIdentifier];
+ if (!repos || repos[repoInfo] == nil) {
// Calling this should create the repo.
[FIRDatabase createDatabaseForTests:repoInfo config:config];
}
- return configs[config.sessionIdentifier][repoHashString];
+ return configs[config.sessionIdentifier][repoInfo];
}
}
+ (FRepo *) createRepo:(FRepoInfo *)repoInfo config:(FIRDatabaseConfig *)config database:(FIRDatabase *)database {
[config freeze];
- NSString* repoHashString = [NSString stringWithFormat:@"%@_%@", repoInfo.host, repoInfo.namespace];
- NSMutableDictionary *configs = [FRepoManager configs];
+ FRepoDictionary *configs = [FRepoManager configs];
@synchronized(configs) {
- NSMutableDictionary *repos = configs[config.sessionIdentifier];
+ NSMutableDictionary<FRepoInfo *, FRepo *> *repos =
+ configs[config.sessionIdentifier];
if (!repos) {
repos = [NSMutableDictionary dictionary];
configs[config.sessionIdentifier] = repos;
}
- FRepo *repo = repos[repoHashString];
+ FRepo *repo = repos[repoInfo];
if (repo == nil) {
repo = [[FRepo alloc] initWithRepoInfo:repoInfo config:config database:database];
- repos[repoHashString] = repo;
+ repos[repoInfo] = repo;
return repo;
} else {
[NSException raise:@"RepoExists" format:@"createRepo called for Repo that already exists."];
@@ -76,9 +79,9 @@
+ (void) interrupt:(FIRDatabaseConfig *)config {
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
- NSMutableDictionary *configs = [FRepoManager configs];
- NSMutableDictionary *repos = configs[config.sessionIdentifier];
- for (FRepo* repo in [repos allValues]) {
+ FRepoDictionary *configs = [FRepoManager configs];
+ NSMutableDictionary<FRepoInfo *, FRepo *> *repos = configs[config.sessionIdentifier];
+ for (FRepo *repo in [repos allValues]) {
[repo interrupt];
}
});
@@ -86,20 +89,20 @@
+ (void) interruptAll {
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
- NSMutableDictionary *configs = [FRepoManager configs];
- for (NSMutableDictionary *repos in [configs allValues]) {
- for (FRepo* repo in [repos allValues]) {
- [repo interrupt];
- }
+ FRepoDictionary *configs = [FRepoManager configs];
+ for (NSMutableDictionary<FRepoInfo *, FRepo *> *repos in [configs allValues]) {
+ for (FRepo *repo in [repos allValues]) {
+ [repo interrupt];
+ }
}
});
}
+ (void) resume:(FIRDatabaseConfig *)config {
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
- NSMutableDictionary *configs = [FRepoManager configs];
- NSMutableDictionary *repos = configs[config.sessionIdentifier];
- for (FRepo* repo in [repos allValues]) {
+ FRepoDictionary *configs = [FRepoManager configs];
+ NSMutableDictionary<FRepoInfo *, FRepo *> *repos = configs[config.sessionIdentifier];
+ for (FRepo *repo in [repos allValues]) {
[repo resume];
}
});
@@ -107,9 +110,9 @@
+ (void) resumeAll {
dispatch_async([FIRDatabaseQuery sharedQueue], ^{
- NSMutableDictionary *configs = [FRepoManager configs];
- for (NSMutableDictionary *repos in [configs allValues]) {
- for (FRepo* repo in [repos allValues]) {
+ FRepoDictionary *configs = [FRepoManager configs];
+ for (NSMutableDictionary<FRepoInfo *, FRepo *> *repos in [configs allValues]) {
+ for (FRepo *repo in [repos allValues]) {
[repo resume];
}
}
diff --git a/Firebase/Database/Public/FIRDatabase.h b/Firebase/Database/Public/FIRDatabase.h
index a67f96d..606f164 100644
--- a/Firebase/Database/Public/FIRDatabase.h
+++ b/Firebase/Database/Public/FIRDatabase.h
@@ -46,6 +46,27 @@ FIR_SWIFT_NAME(Database)
+ (FIRDatabase *) database FIR_SWIFT_NAME(database());
/**
+ * Gets a FirebaseDatabase instance for the specified URL.
+ *
+ * @param url The URL to the Firebase Database instance you want to access.
+ * @return A FIRDatabase instance.
+ */
++ (FIRDatabase *)databaseWithURL:(NSString *)url NS_SWIFT_NAME(database(url:));
+
+/**
+ * Gets a FirebaseDatabase instance for the specified URL, using the specified
+ * FirebaseApp.
+ *
+ * @param app The FIRApp to get a FIRDatabase for.
+ * @param url The URL to the Firebase Database instance you want to access.
+ * @return A FIRDatabase instance.
+ */
+// clang-format off
++ (FIRDatabase *)databaseForApp:(FIRApp *)app
+ URL:(NSString *)url NS_SWIFT_NAME(database(app:url:));
+// clang-format on
+
+/**
* Gets an instance of FIRDatabase for a specific FIRApp.
*
* @param app The FIRApp to get a FIRDatabase for.