FIRDatabase.m 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <Foundation/Foundation.h>
  17. #import "FIRAppInternal.h"
  18. #import "FIRLogger.h"
  19. #import "FIRDatabase.h"
  20. #import "FIRDatabase_Private.h"
  21. #import "FIRDatabaseQuery_Private.h"
  22. #import "FRepoManager.h"
  23. #import "FValidation.h"
  24. #import "FIRDatabaseConfig_Private.h"
  25. #import "FRepoInfo.h"
  26. #import "FIRDatabaseConfig.h"
  27. #import "FIRDatabaseReference_Private.h"
  28. #import "FIROptions.h"
  29. @interface FIRDatabase ()
  30. @property (nonatomic, strong) FRepoInfo *repoInfo;
  31. @property (nonatomic, strong) FIRDatabaseConfig *config;
  32. @property (nonatomic, strong) FRepo *repo;
  33. @end
  34. @implementation FIRDatabase
  35. // The STR and STR_EXPAND macro allow a numeric version passed to he compiler driver
  36. // with a -D to be treated as a string instead of an invalid floating point value.
  37. #define STR(x) STR_EXPAND(x)
  38. #define STR_EXPAND(x) #x
  39. static const char *FIREBASE_SEMVER = (const char *)STR(FIRDatabase_VERSION);
  40. + (void)load {
  41. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  42. [center addObserverForName:kFIRAppDeleteNotification
  43. object:nil
  44. queue:nil
  45. usingBlock:^(NSNotification * _Nonnull note) {
  46. NSString *appName = note.userInfo[kFIRAppNameKey];
  47. if (appName == nil) { return; }
  48. NSMutableDictionary *instances = [self instances];
  49. @synchronized (instances) {
  50. FIRDatabase *deletedApp = instances[appName];
  51. if (deletedApp) {
  52. // Clean up the deleted instance in an effort to remove any resources still in use.
  53. // Note: Any leftover instances of this exact database will be invalid.
  54. [FRepoManager disposeRepos:deletedApp.config];
  55. [instances removeObjectForKey:appName];
  56. }
  57. }
  58. }];
  59. }
  60. /**
  61. * A static NSMutableDictionary of FirebaseApp names to FirebaseDatabase instance. To ensure thread-
  62. * safety, it should only be accessed in databaseForApp, which is synchronized.
  63. *
  64. * TODO: This serves a duplicate purpose as RepoManager. We should clean up.
  65. * TODO: We should maybe be conscious of leaks and make this a weak map or similar
  66. * but we have a lot of work to do to allow FirebaseDatabase/Repo etc. to be GC'd.
  67. */
  68. + (NSMutableDictionary *)instances {
  69. static dispatch_once_t pred = 0;
  70. static NSMutableDictionary *instances;
  71. dispatch_once(&pred, ^{
  72. instances = [NSMutableDictionary dictionary];
  73. });
  74. return instances;
  75. }
  76. + (FIRDatabase *)database {
  77. if (![FIRApp isDefaultAppConfigured]) {
  78. [NSException raise:@"FIRAppNotConfigured"
  79. format:@"Failed to get default Firebase Database instance. Must call `[FIRApp "
  80. @"configure]` (`FirebaseApp.configure()` in Swift) before using "
  81. @"Firebase Database."];
  82. }
  83. FIRApp *app = [FIRApp defaultApp];
  84. return [FIRDatabase databaseForApp:app];
  85. }
  86. + (FIRDatabase *)databaseForApp:(FIRApp *)app {
  87. if (app == nil) {
  88. [NSException raise:@"InvalidFIRApp" format:@"nil FIRApp instance passed to databaseForApp."];
  89. }
  90. NSMutableDictionary *instances = [self instances];
  91. @synchronized (instances) {
  92. FIRDatabase *database = instances[app.name];
  93. if (!database) {
  94. NSString *databaseUrl = app.options.databaseURL;
  95. if (databaseUrl == nil) {
  96. [NSException raise:@"MissingDatabaseURL" format:@"Failed to get FIRDatabase instance: FIRApp object has no "
  97. "databaseURL in its FirebaseOptions object."];
  98. }
  99. FParsedUrl *parsedUrl = [FUtilities parseUrl:databaseUrl];
  100. if (![parsedUrl.path isEmpty]) {
  101. [NSException raise:@"InvalidDatabaseURL" format:@"Configured Database URL '%@' is invalid. It should "
  102. "point to the root of a Firebase Database but it includes a path: %@",
  103. databaseUrl, [parsedUrl.path toString]];
  104. }
  105. id<FAuthTokenProvider> authTokenProvider = [FAuthTokenProvider authTokenProviderForApp:app];
  106. // If this is the default app, don't set the session persistence key so that we use our
  107. // default ("default") instead of the FIRApp default ("[DEFAULT]") so that we
  108. // preserve the default location used by the legacy Firebase SDK.
  109. NSString *sessionIdentifier = @"default";
  110. if (![FIRApp isDefaultAppConfigured] || app != [FIRApp defaultApp]) {
  111. sessionIdentifier = app.name;
  112. }
  113. FIRDatabaseConfig *config = [[FIRDatabaseConfig alloc] initWithSessionIdentifier:sessionIdentifier
  114. authTokenProvider:authTokenProvider];
  115. database = [[FIRDatabase alloc] initWithApp:app repoInfo:parsedUrl.repoInfo config:config];
  116. instances[app.name] = database;
  117. }
  118. return database;
  119. }
  120. }
  121. + (NSString *) buildVersion {
  122. // TODO: Restore git hash when build moves back to git
  123. return [NSString stringWithFormat:@"%s_%s", FIREBASE_SEMVER, __DATE__];
  124. }
  125. + (FIRDatabase *)createDatabaseForTests:(FRepoInfo *)repoInfo config:(FIRDatabaseConfig *)config {
  126. FIRDatabase *db = [[FIRDatabase alloc] initWithApp:nil repoInfo:repoInfo config:config];
  127. [db ensureRepo];
  128. return db;
  129. }
  130. + (NSString *) sdkVersion {
  131. return [NSString stringWithUTF8String:FIREBASE_SEMVER];
  132. }
  133. + (void) setLoggingEnabled:(BOOL)enabled {
  134. [FUtilities setLoggingEnabled:enabled];
  135. FFLog(@"I-RDB024001", @"BUILD Version: %@", [FIRDatabase buildVersion]);
  136. }
  137. - (id)initWithApp:(FIRApp *)app repoInfo:(FRepoInfo *)info config:(FIRDatabaseConfig *)config {
  138. self = [super init];
  139. if (self != nil) {
  140. self->_repoInfo = info;
  141. self->_config = config;
  142. self->_app = app;
  143. }
  144. return self;
  145. }
  146. - (FIRDatabaseReference *)reference {
  147. [self ensureRepo];
  148. return [[FIRDatabaseReference alloc] initWithRepo:self.repo path:[FPath empty]];
  149. }
  150. - (FIRDatabaseReference *)referenceWithPath:(NSString *)path {
  151. [self ensureRepo];
  152. [FValidation validateFrom:@"referenceWithPath" validRootPathString:path];
  153. FPath *childPath = [[FPath alloc] initWith:path];
  154. return [[FIRDatabaseReference alloc] initWithRepo:self.repo path:childPath];
  155. }
  156. - (FIRDatabaseReference *)referenceFromURL:(NSString *)databaseUrl {
  157. [self ensureRepo];
  158. if (databaseUrl == nil) {
  159. [NSException raise:@"InvalidDatabaseURL" format:@"Invalid nil url passed to referenceFromURL:"];
  160. }
  161. FParsedUrl *parsedUrl = [FUtilities parseUrl:databaseUrl];
  162. [FValidation validateFrom:@"referenceFromURL:" validURL:parsedUrl];
  163. if (![parsedUrl.repoInfo.host isEqualToString:_repoInfo.host]) {
  164. [NSException raise:@"InvalidDatabaseURL" format:@"Invalid URL (%@) passed to getReference(). URL was expected "
  165. "to match configured Database URL: %@", databaseUrl, [self reference].URL];
  166. }
  167. return [[FIRDatabaseReference alloc] initWithRepo:self.repo path:parsedUrl.path];
  168. }
  169. - (void)purgeOutstandingWrites {
  170. [self ensureRepo];
  171. dispatch_async([FIRDatabaseQuery sharedQueue], ^{
  172. [self.repo purgeOutstandingWrites];
  173. });
  174. }
  175. - (void)goOnline {
  176. [self ensureRepo];
  177. dispatch_async([FIRDatabaseQuery sharedQueue], ^{
  178. [self.repo resume];
  179. });
  180. }
  181. - (void)goOffline {
  182. [self ensureRepo];
  183. dispatch_async([FIRDatabaseQuery sharedQueue], ^{
  184. [self.repo interrupt];
  185. });
  186. }
  187. - (void)setPersistenceEnabled:(BOOL)persistenceEnabled {
  188. [self assertUnfrozen:@"setPersistenceEnabled"];
  189. self->_config.persistenceEnabled = persistenceEnabled;
  190. }
  191. - (BOOL)persistenceEnabled {
  192. return self->_config.persistenceEnabled;
  193. }
  194. - (void)setPersistenceCacheSizeBytes:(NSUInteger)persistenceCacheSizeBytes {
  195. [self assertUnfrozen:@"setPersistenceCacheSizeBytes"];
  196. self->_config.persistenceCacheSizeBytes = persistenceCacheSizeBytes;
  197. }
  198. - (NSUInteger)persistenceCacheSizeBytes {
  199. return self->_config.persistenceCacheSizeBytes;
  200. }
  201. - (void)setCallbackQueue:(dispatch_queue_t)callbackQueue {
  202. [self assertUnfrozen:@"setCallbackQueue"];
  203. self->_config.callbackQueue = callbackQueue;
  204. }
  205. - (dispatch_queue_t)callbackQueue {
  206. return self->_config.callbackQueue;
  207. }
  208. - (void) assertUnfrozen:(NSString*)methodName {
  209. if (self.repo != nil) {
  210. [NSException raise:@"FIRDatabaseAlreadyInUse" format:@"Calls to %@ must be made before any other usage of "
  211. "FIRDatabase instance.", methodName];
  212. }
  213. }
  214. - (void) ensureRepo {
  215. if (self.repo == nil) {
  216. self.repo = [FRepoManager createRepo:self.repoInfo config:self.config database:self];
  217. }
  218. }
  219. @end