FIRFirestore.mm 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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 "FIRFirestore.h"
  17. #import <FirebaseCore/FIRApp.h>
  18. #import <FirebaseCore/FIRAppInternal.h>
  19. #import <FirebaseCore/FIRComponentContainer.h>
  20. #import <FirebaseCore/FIRLogger.h>
  21. #import <FirebaseCore/FIROptions.h>
  22. #include <memory>
  23. #include <string>
  24. #include <utility>
  25. #import "FIRFirestore.h"
  26. #import "Firestore/Source/API/FIRDocumentReference+Internal.h"
  27. #import "Firestore/Source/API/FIRFirestore+Internal.h"
  28. #import "Firestore/Source/API/FSTFirestoreComponent.h"
  29. #import "Firestore/Source/API/FSTUserDataConverter.h"
  30. #include "Firestore/core/src/firebase/firestore/api/firestore.h"
  31. #include "Firestore/core/src/firebase/firestore/api/input_validation.h"
  32. #include "Firestore/core/src/firebase/firestore/auth/credentials_provider.h"
  33. #include "Firestore/core/src/firebase/firestore/core/database_info.h"
  34. #include "Firestore/core/src/firebase/firestore/model/database_id.h"
  35. #include "Firestore/core/src/firebase/firestore/util/async_queue.h"
  36. #include "Firestore/core/src/firebase/firestore/util/delayed_constructor.h"
  37. #include "Firestore/core/src/firebase/firestore/util/log.h"
  38. #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
  39. namespace util = firebase::firestore::util;
  40. using firebase::firestore::api::DocumentReference;
  41. using firebase::firestore::api::Firestore;
  42. using firebase::firestore::api::ThrowIllegalState;
  43. using firebase::firestore::api::ThrowInvalidArgument;
  44. using firebase::firestore::auth::CredentialsProvider;
  45. using firebase::firestore::model::DatabaseId;
  46. using firebase::firestore::util::AsyncQueue;
  47. using firebase::firestore::util::DelayedConstructor;
  48. NS_ASSUME_NONNULL_BEGIN
  49. extern "C" NSString *const FIRFirestoreErrorDomain = @"FIRFirestoreErrorDomain";
  50. #pragma mark - FIRFirestore
  51. @interface FIRFirestore ()
  52. @property(nonatomic, strong, readonly) FSTUserDataConverter *dataConverter;
  53. @end
  54. @implementation FIRFirestore {
  55. DelayedConstructor<Firestore> _firestore;
  56. }
  57. + (NSMutableDictionary<NSString *, FIRFirestore *> *)instances {
  58. static dispatch_once_t token = 0;
  59. static NSMutableDictionary<NSString *, FIRFirestore *> *instances;
  60. dispatch_once(&token, ^{
  61. instances = [NSMutableDictionary dictionary];
  62. });
  63. return instances;
  64. }
  65. + (void)initialize {
  66. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  67. [center addObserverForName:kFIRAppDeleteNotification
  68. object:nil
  69. queue:nil
  70. usingBlock:^(NSNotification *_Nonnull note) {
  71. NSString *appName = note.userInfo[kFIRAppNameKey];
  72. if (appName == nil) return;
  73. NSMutableDictionary *instances = [self instances];
  74. @synchronized(instances) {
  75. // Since the key for instances isn't just the app name, iterate over all the
  76. // keys to get the one(s) we have to delete. There could be multiple in case
  77. // the user calls firestoreForApp:database:.
  78. NSMutableArray *keysToDelete = [[NSMutableArray alloc] init];
  79. NSString *keyPrefix = [NSString stringWithFormat:@"%@|", appName];
  80. for (NSString *key in instances.allKeys) {
  81. if ([key hasPrefix:keyPrefix]) {
  82. [keysToDelete addObject:key];
  83. }
  84. }
  85. // Loop through the keys found and delete them from the stored instances.
  86. for (NSString *key in keysToDelete) {
  87. [instances removeObjectForKey:key];
  88. }
  89. }
  90. }];
  91. }
  92. + (instancetype)firestore {
  93. FIRApp *app = [FIRApp defaultApp];
  94. if (!app) {
  95. ThrowIllegalState("Failed to get FirebaseApp instance. Please call FirebaseApp.configure() "
  96. "before using Firestore");
  97. }
  98. return [self firestoreForApp:app database:util::WrapNSString(DatabaseId::kDefault)];
  99. }
  100. + (instancetype)firestoreForApp:(FIRApp *)app {
  101. return [self firestoreForApp:app database:util::WrapNSString(DatabaseId::kDefault)];
  102. }
  103. // TODO(b/62410906): make this public
  104. + (instancetype)firestoreForApp:(FIRApp *)app database:(NSString *)database {
  105. if (!app) {
  106. ThrowInvalidArgument("FirebaseApp instance may not be nil. Use FirebaseApp.app() if you'd like "
  107. "to use the default FirebaseApp instance.");
  108. }
  109. if (!database) {
  110. ThrowInvalidArgument("Database identifier may not be nil. Use '%s' if you want the default "
  111. "database",
  112. DatabaseId::kDefault);
  113. }
  114. id<FSTFirestoreMultiDBProvider> provider =
  115. FIR_COMPONENT(FSTFirestoreMultiDBProvider, app.container);
  116. return [provider firestoreForDatabase:database];
  117. }
  118. - (instancetype)initWithProjectID:(std::string)projectID
  119. database:(std::string)database
  120. persistenceKey:(std::string)persistenceKey
  121. credentialsProvider:(std::unique_ptr<CredentialsProvider>)credentialsProvider
  122. workerQueue:(std::unique_ptr<AsyncQueue>)workerQueue
  123. firebaseApp:(FIRApp *)app {
  124. if (self = [super init]) {
  125. _firestore.Init(std::move(projectID), std::move(database), std::move(persistenceKey),
  126. std::move(credentialsProvider), std::move(workerQueue), (__bridge void *)self);
  127. _app = app;
  128. FSTPreConverterBlock block = ^id _Nullable(id _Nullable input) {
  129. if ([input isKindOfClass:[FIRDocumentReference class]]) {
  130. FIRDocumentReference *documentReference = (FIRDocumentReference *)input;
  131. return [[FSTDocumentKeyReference alloc] initWithKey:documentReference.key
  132. databaseID:documentReference.firestore.databaseID];
  133. } else {
  134. return input;
  135. }
  136. };
  137. _dataConverter = [[FSTUserDataConverter alloc] initWithDatabaseID:&_firestore->database_id()
  138. preConverter:block];
  139. }
  140. return self;
  141. }
  142. - (FIRFirestoreSettings *)settings {
  143. return _firestore->settings();
  144. }
  145. - (void)setSettings:(FIRFirestoreSettings *)settings {
  146. _firestore->set_settings(settings);
  147. }
  148. /**
  149. * Ensures that the FirestoreClient is configured and returns it.
  150. */
  151. - (FSTFirestoreClient *)client {
  152. return _firestore->client();
  153. }
  154. - (FIRCollectionReference *)collectionWithPath:(NSString *)collectionPath {
  155. if (!collectionPath) {
  156. ThrowInvalidArgument("Collection path cannot be nil.");
  157. }
  158. if ([collectionPath containsString:@"//"]) {
  159. ThrowInvalidArgument("Invalid path (%s). Paths must not contain // in them.", collectionPath);
  160. }
  161. return _firestore->GetCollection(util::MakeString(collectionPath));
  162. }
  163. - (FIRDocumentReference *)documentWithPath:(NSString *)documentPath {
  164. if (!documentPath) {
  165. ThrowInvalidArgument("Document path cannot be nil.");
  166. }
  167. if ([documentPath containsString:@"//"]) {
  168. ThrowInvalidArgument("Invalid path (%s). Paths must not contain // in them.", documentPath);
  169. }
  170. DocumentReference documentReference = _firestore->GetDocument(util::MakeString(documentPath));
  171. return [[FIRDocumentReference alloc] initWithReference:std::move(documentReference)];
  172. }
  173. - (FIRQuery *)collectionGroupWithID:(NSString *)collectionID {
  174. if (!collectionID) {
  175. ThrowInvalidArgument("Collection ID cannot be nil.");
  176. }
  177. if ([collectionID containsString:@"/"]) {
  178. ThrowInvalidArgument("Invalid collection ID (%s). Collection IDs must not contain / in them.",
  179. collectionID);
  180. }
  181. return _firestore->GetCollectionGroup(collectionID);
  182. }
  183. - (FIRWriteBatch *)batch {
  184. return _firestore->GetBatch();
  185. }
  186. - (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **))updateBlock
  187. dispatchQueue:(dispatch_queue_t)queue
  188. completion:
  189. (void (^)(id _Nullable result, NSError *_Nullable error))completion {
  190. // We wrap the function they provide in order to use internal implementation classes for
  191. // transaction, and to run the user callback block on the proper queue.
  192. if (!updateBlock) {
  193. ThrowInvalidArgument("Transaction block cannot be nil.");
  194. } else if (!completion) {
  195. ThrowInvalidArgument("Transaction completion block cannot be nil.");
  196. }
  197. _firestore->RunTransaction(updateBlock, queue, completion);
  198. }
  199. - (void)runTransactionWithBlock:(id _Nullable (^)(FIRTransaction *, NSError **error))updateBlock
  200. completion:
  201. (void (^)(id _Nullable result, NSError *_Nullable error))completion {
  202. static dispatch_queue_t transactionDispatchQueue;
  203. static dispatch_once_t onceToken;
  204. dispatch_once(&onceToken, ^{
  205. transactionDispatchQueue = dispatch_queue_create("com.google.firebase.firestore.transaction",
  206. DISPATCH_QUEUE_CONCURRENT);
  207. });
  208. [self runTransactionWithBlock:updateBlock
  209. dispatchQueue:transactionDispatchQueue
  210. completion:completion];
  211. }
  212. + (void)enableLogging:(BOOL)logging {
  213. FIRSetLoggerLevel(logging ? FIRLoggerLevelDebug : FIRLoggerLevelNotice);
  214. }
  215. - (void)enableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
  216. _firestore->EnableNetwork(completion);
  217. }
  218. - (void)disableNetworkWithCompletion:(nullable void (^)(NSError *_Nullable))completion {
  219. _firestore->DisableNetwork(completion);
  220. }
  221. @end
  222. @implementation FIRFirestore (Internal)
  223. - (Firestore *)wrapped {
  224. return _firestore.get();
  225. }
  226. - (AsyncQueue *)workerQueue {
  227. return _firestore->worker_queue();
  228. }
  229. - (const DatabaseId *)databaseID {
  230. return &_firestore->database_id();
  231. }
  232. + (BOOL)isLoggingEnabled {
  233. return FIRIsLoggableLevel(FIRLoggerLevelDebug, NO);
  234. }
  235. + (FIRFirestore *)recoverFromFirestore:(Firestore *)firestore {
  236. return (__bridge FIRFirestore *)firestore->extension();
  237. }
  238. - (void)shutdownWithCompletion:(nullable void (^)(NSError *_Nullable error))completion {
  239. _firestore->Shutdown(completion);
  240. }
  241. @end
  242. NS_ASSUME_NONNULL_END