FSTFirestoreClient.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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 "Firestore/Source/Core/FSTFirestoreClient.h"
  17. #import "Firestore/Source/Auth/FSTCredentialsProvider.h"
  18. #import "Firestore/Source/Core/FSTEventManager.h"
  19. #import "Firestore/Source/Core/FSTSyncEngine.h"
  20. #import "Firestore/Source/Core/FSTTransaction.h"
  21. #import "Firestore/Source/Local/FSTEagerGarbageCollector.h"
  22. #import "Firestore/Source/Local/FSTLevelDB.h"
  23. #import "Firestore/Source/Local/FSTLocalSerializer.h"
  24. #import "Firestore/Source/Local/FSTLocalStore.h"
  25. #import "Firestore/Source/Local/FSTMemoryPersistence.h"
  26. #import "Firestore/Source/Local/FSTNoOpGarbageCollector.h"
  27. #import "Firestore/Source/Remote/FSTDatastore.h"
  28. #import "Firestore/Source/Remote/FSTRemoteStore.h"
  29. #import "Firestore/Source/Remote/FSTSerializerBeta.h"
  30. #import "Firestore/Source/Util/FSTAssert.h"
  31. #import "Firestore/Source/Util/FSTClasses.h"
  32. #import "Firestore/Source/Util/FSTDispatchQueue.h"
  33. #import "Firestore/Source/Util/FSTLogger.h"
  34. #include "Firestore/core/src/firebase/firestore/core/database_info.h"
  35. #include "Firestore/core/src/firebase/firestore/model/database_id.h"
  36. #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
  37. namespace util = firebase::firestore::util;
  38. using firebase::firestore::auth::User;
  39. using firebase::firestore::core::DatabaseInfo;
  40. using firebase::firestore::model::DatabaseId;
  41. NS_ASSUME_NONNULL_BEGIN
  42. @interface FSTFirestoreClient () {
  43. DatabaseInfo _databaseInfo;
  44. }
  45. - (instancetype)initWithDatabaseInfo:(const DatabaseInfo &)databaseInfo
  46. usePersistence:(BOOL)usePersistence
  47. credentialsProvider:(id<FSTCredentialsProvider>)credentialsProvider
  48. userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue
  49. workerDispatchQueue:(FSTDispatchQueue *)queue NS_DESIGNATED_INITIALIZER;
  50. @property(nonatomic, assign, readonly) const DatabaseInfo *databaseInfo;
  51. @property(nonatomic, strong, readonly) FSTEventManager *eventManager;
  52. @property(nonatomic, strong, readonly) id<FSTPersistence> persistence;
  53. @property(nonatomic, strong, readonly) FSTSyncEngine *syncEngine;
  54. @property(nonatomic, strong, readonly) FSTRemoteStore *remoteStore;
  55. @property(nonatomic, strong, readonly) FSTLocalStore *localStore;
  56. /**
  57. * Dispatch queue responsible for all of our internal processing. When we get incoming work from
  58. * the user (via public API) or the network (incoming GRPC messages), we should always dispatch
  59. * onto this queue. This ensures our internal data structures are never accessed from multiple
  60. * threads simultaneously.
  61. */
  62. @property(nonatomic, strong, readonly) FSTDispatchQueue *workerDispatchQueue;
  63. @property(nonatomic, strong, readonly) id<FSTCredentialsProvider> credentialsProvider;
  64. @end
  65. @implementation FSTFirestoreClient
  66. + (instancetype)clientWithDatabaseInfo:(const DatabaseInfo &)databaseInfo
  67. usePersistence:(BOOL)usePersistence
  68. credentialsProvider:(id<FSTCredentialsProvider>)credentialsProvider
  69. userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue
  70. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue {
  71. return [[FSTFirestoreClient alloc] initWithDatabaseInfo:databaseInfo
  72. usePersistence:usePersistence
  73. credentialsProvider:credentialsProvider
  74. userDispatchQueue:userDispatchQueue
  75. workerDispatchQueue:workerDispatchQueue];
  76. }
  77. - (instancetype)initWithDatabaseInfo:(const DatabaseInfo &)databaseInfo
  78. usePersistence:(BOOL)usePersistence
  79. credentialsProvider:(id<FSTCredentialsProvider>)credentialsProvider
  80. userDispatchQueue:(FSTDispatchQueue *)userDispatchQueue
  81. workerDispatchQueue:(FSTDispatchQueue *)workerDispatchQueue {
  82. if (self = [super init]) {
  83. _databaseInfo = databaseInfo;
  84. _credentialsProvider = credentialsProvider;
  85. _userDispatchQueue = userDispatchQueue;
  86. _workerDispatchQueue = workerDispatchQueue;
  87. dispatch_semaphore_t initialUserAvailable = dispatch_semaphore_create(0);
  88. __block bool initialized = false;
  89. __block User initialUser;
  90. FSTWeakify(self);
  91. _credentialsProvider.userChangeListener = ^(User user) {
  92. FSTStrongify(self);
  93. if (self) {
  94. if (!initialized) {
  95. initialUser = user;
  96. initialized = true;
  97. dispatch_semaphore_signal(initialUserAvailable);
  98. } else {
  99. [workerDispatchQueue dispatchAsync:^{
  100. [self userDidChange:user];
  101. }];
  102. }
  103. }
  104. };
  105. // Defer initialization until we get the current user from the userChangeListener. This is
  106. // guaranteed to be synchronously dispatched onto our worker queue, so we will be initialized
  107. // before any subsequently queued work runs.
  108. [_workerDispatchQueue dispatchAsync:^{
  109. dispatch_semaphore_wait(initialUserAvailable, DISPATCH_TIME_FOREVER);
  110. [self initializeWithUser:initialUser usePersistence:usePersistence];
  111. }];
  112. }
  113. return self;
  114. }
  115. - (void)initializeWithUser:(const User &)user usePersistence:(BOOL)usePersistence {
  116. // Do all of our initialization on our own dispatch queue.
  117. [self.workerDispatchQueue verifyIsCurrentQueue];
  118. // Note: The initialization work must all be synchronous (we can't dispatch more work) since
  119. // external write/listen operations could get queued to run before that subsequent work
  120. // completes.
  121. id<FSTGarbageCollector> garbageCollector;
  122. if (usePersistence) {
  123. // TODO(http://b/33384523): For now we just disable garbage collection when persistence is
  124. // enabled.
  125. garbageCollector = [[FSTNoOpGarbageCollector alloc] init];
  126. NSString *dir = [FSTLevelDB storageDirectoryForDatabaseInfo:*self.databaseInfo
  127. documentsDirectory:[FSTLevelDB documentsDirectory]];
  128. FSTSerializerBeta *remoteSerializer =
  129. [[FSTSerializerBeta alloc] initWithDatabaseID:&self.databaseInfo->database_id()];
  130. FSTLocalSerializer *serializer =
  131. [[FSTLocalSerializer alloc] initWithRemoteSerializer:remoteSerializer];
  132. _persistence = [[FSTLevelDB alloc] initWithDirectory:dir serializer:serializer];
  133. } else {
  134. garbageCollector = [[FSTEagerGarbageCollector alloc] init];
  135. _persistence = [FSTMemoryPersistence persistence];
  136. }
  137. NSError *error;
  138. if (![_persistence start:&error]) {
  139. // If local storage fails to start then just throw up our hands: the error is unrecoverable.
  140. // There's nothing an end-user can do and nearly all failures indicate the developer is doing
  141. // something grossly wrong so we should stop them cold in their tracks with a failure they
  142. // can't ignore.
  143. [NSException raise:NSInternalInconsistencyException format:@"Failed to open DB: %@", error];
  144. }
  145. _localStore = [[FSTLocalStore alloc] initWithPersistence:_persistence
  146. garbageCollector:garbageCollector
  147. initialUser:user];
  148. FSTDatastore *datastore = [FSTDatastore datastoreWithDatabase:self.databaseInfo
  149. workerDispatchQueue:self.workerDispatchQueue
  150. credentials:self.credentialsProvider];
  151. _remoteStore = [FSTRemoteStore remoteStoreWithLocalStore:_localStore datastore:datastore];
  152. _syncEngine = [[FSTSyncEngine alloc] initWithLocalStore:_localStore
  153. remoteStore:_remoteStore
  154. initialUser:user];
  155. _eventManager = [FSTEventManager eventManagerWithSyncEngine:_syncEngine];
  156. // Setup wiring for remote store.
  157. _remoteStore.syncEngine = _syncEngine;
  158. _remoteStore.onlineStateDelegate = self;
  159. // NOTE: RemoteStore depends on LocalStore (for persisting stream tokens, refilling mutation
  160. // queue, etc.) so must be started after LocalStore.
  161. [_localStore start];
  162. [_remoteStore start];
  163. }
  164. - (void)userDidChange:(const User &)user {
  165. [self.workerDispatchQueue verifyIsCurrentQueue];
  166. FSTLog(@"User Changed: %@", util::WrapNSStringNoCopy(user.uid()));
  167. [self.syncEngine userDidChange:user];
  168. }
  169. - (void)applyChangedOnlineState:(FSTOnlineState)onlineState {
  170. [self.syncEngine applyChangedOnlineState:onlineState];
  171. [self.eventManager applyChangedOnlineState:onlineState];
  172. }
  173. - (void)disableNetworkWithCompletion:(nullable FSTVoidErrorBlock)completion {
  174. [self.workerDispatchQueue dispatchAsync:^{
  175. [self.remoteStore disableNetwork];
  176. if (completion) {
  177. [self.userDispatchQueue dispatchAsync:^{
  178. completion(nil);
  179. }];
  180. }
  181. }];
  182. }
  183. - (void)enableNetworkWithCompletion:(nullable FSTVoidErrorBlock)completion {
  184. [self.workerDispatchQueue dispatchAsync:^{
  185. [self.remoteStore enableNetwork];
  186. if (completion) {
  187. [self.userDispatchQueue dispatchAsync:^{
  188. completion(nil);
  189. }];
  190. }
  191. }];
  192. }
  193. - (void)shutdownWithCompletion:(nullable FSTVoidErrorBlock)completion {
  194. [self.workerDispatchQueue dispatchAsync:^{
  195. self.credentialsProvider.userChangeListener = nil;
  196. [self.remoteStore shutdown];
  197. [self.localStore shutdown];
  198. [self.persistence shutdown];
  199. if (completion) {
  200. [self.userDispatchQueue dispatchAsync:^{
  201. completion(nil);
  202. }];
  203. }
  204. }];
  205. }
  206. - (FSTQueryListener *)listenToQuery:(FSTQuery *)query
  207. options:(FSTListenOptions *)options
  208. viewSnapshotHandler:(FSTViewSnapshotHandler)viewSnapshotHandler {
  209. FSTQueryListener *listener = [[FSTQueryListener alloc] initWithQuery:query
  210. options:options
  211. viewSnapshotHandler:viewSnapshotHandler];
  212. [self.workerDispatchQueue dispatchAsync:^{
  213. [self.eventManager addListener:listener];
  214. }];
  215. return listener;
  216. }
  217. - (void)removeListener:(FSTQueryListener *)listener {
  218. [self.workerDispatchQueue dispatchAsync:^{
  219. [self.eventManager removeListener:listener];
  220. }];
  221. }
  222. - (void)writeMutations:(NSArray<FSTMutation *> *)mutations
  223. completion:(nullable FSTVoidErrorBlock)completion {
  224. [self.workerDispatchQueue dispatchAsync:^{
  225. if (mutations.count == 0) {
  226. if (completion) {
  227. [self.userDispatchQueue dispatchAsync:^{
  228. completion(nil);
  229. }];
  230. }
  231. } else {
  232. [self.syncEngine writeMutations:mutations
  233. completion:^(NSError *error) {
  234. // Dispatch the result back onto the user dispatch queue.
  235. if (completion) {
  236. [self.userDispatchQueue dispatchAsync:^{
  237. completion(error);
  238. }];
  239. }
  240. }];
  241. }
  242. }];
  243. };
  244. - (void)transactionWithRetries:(int)retries
  245. updateBlock:(FSTTransactionBlock)updateBlock
  246. completion:(FSTVoidIDErrorBlock)completion {
  247. [self.workerDispatchQueue dispatchAsync:^{
  248. [self.syncEngine transactionWithRetries:retries
  249. workerDispatchQueue:self.workerDispatchQueue
  250. updateBlock:updateBlock
  251. completion:^(id _Nullable result, NSError *_Nullable error) {
  252. // Dispatch the result back onto the user dispatch queue.
  253. if (completion) {
  254. [self.userDispatchQueue dispatchAsync:^{
  255. completion(result, error);
  256. }];
  257. }
  258. }];
  259. }];
  260. }
  261. - (const DatabaseInfo *)databaseInfo {
  262. return &_databaseInfo;
  263. }
  264. - (const DatabaseId *)databaseID {
  265. return &_databaseInfo.database_id();
  266. }
  267. @end
  268. NS_ASSUME_NONNULL_END