FSTEventManager.mm 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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/FSTEventManager.h"
  17. #include <utility>
  18. #include <vector>
  19. #import "Firestore/Source/Core/FSTQuery.h"
  20. #import "Firestore/Source/Core/FSTSyncEngine.h"
  21. #import "Firestore/Source/Model/FSTDocumentSet.h"
  22. #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
  23. using firebase::firestore::core::DocumentViewChange;
  24. using firebase::firestore::model::OnlineState;
  25. using firebase::firestore::model::TargetId;
  26. NS_ASSUME_NONNULL_BEGIN
  27. #pragma mark - FSTListenOptions
  28. @implementation FSTListenOptions
  29. + (instancetype)defaultOptions {
  30. static FSTListenOptions *defaultOptions;
  31. static dispatch_once_t onceToken;
  32. dispatch_once(&onceToken, ^{
  33. defaultOptions = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:NO
  34. includeDocumentMetadataChanges:NO
  35. waitForSyncWhenOnline:NO];
  36. });
  37. return defaultOptions;
  38. }
  39. - (instancetype)initWithIncludeQueryMetadataChanges:(BOOL)includeQueryMetadataChanges
  40. includeDocumentMetadataChanges:(BOOL)includeDocumentMetadataChanges
  41. waitForSyncWhenOnline:(BOOL)waitForSyncWhenOnline {
  42. if (self = [super init]) {
  43. _includeQueryMetadataChanges = includeQueryMetadataChanges;
  44. _includeDocumentMetadataChanges = includeDocumentMetadataChanges;
  45. _waitForSyncWhenOnline = waitForSyncWhenOnline;
  46. }
  47. return self;
  48. }
  49. - (instancetype)init {
  50. HARD_FAIL("FSTListenOptions init not supported");
  51. return nil;
  52. }
  53. @end
  54. #pragma mark - FSTQueryListenersInfo
  55. /**
  56. * Holds the listeners and the last received ViewSnapshot for a query being tracked by
  57. * EventManager.
  58. */
  59. @interface FSTQueryListenersInfo : NSObject
  60. @property(nonatomic, strong, nullable, readwrite) FSTViewSnapshot *viewSnapshot;
  61. @property(nonatomic, assign, readwrite) TargetId targetID;
  62. @property(nonatomic, strong, readonly) NSMutableArray<FSTQueryListener *> *listeners;
  63. @end
  64. @implementation FSTQueryListenersInfo
  65. - (instancetype)init {
  66. if (self = [super init]) {
  67. _listeners = [NSMutableArray array];
  68. }
  69. return self;
  70. }
  71. @end
  72. #pragma mark - FSTQueryListener
  73. @interface FSTQueryListener ()
  74. /** The last received view snapshot. */
  75. @property(nonatomic, strong, nullable) FSTViewSnapshot *snapshot;
  76. @property(nonatomic, strong, readonly) FSTListenOptions *options;
  77. /**
  78. * Initial snapshots (e.g. from cache) may not be propagated to the FSTViewSnapshotHandler.
  79. * This flag is set to YES once we've actually raised an event.
  80. */
  81. @property(nonatomic, assign, readwrite) BOOL raisedInitialEvent;
  82. /** The last online state this query listener got. */
  83. @property(nonatomic, assign, readwrite) OnlineState onlineState;
  84. /** The FSTViewSnapshotHandler associated with this query listener. */
  85. @property(nonatomic, copy, nullable) FSTViewSnapshotHandler viewSnapshotHandler;
  86. @end
  87. @implementation FSTQueryListener
  88. - (instancetype)initWithQuery:(FSTQuery *)query
  89. options:(FSTListenOptions *)options
  90. viewSnapshotHandler:(FSTViewSnapshotHandler)viewSnapshotHandler {
  91. if (self = [super init]) {
  92. _query = query;
  93. _options = options;
  94. _viewSnapshotHandler = viewSnapshotHandler;
  95. _raisedInitialEvent = NO;
  96. }
  97. return self;
  98. }
  99. - (void)queryDidChangeViewSnapshot:(FSTViewSnapshot *)snapshot {
  100. HARD_ASSERT(!snapshot.documentChanges.empty() || snapshot.syncStateChanged,
  101. "We got a new snapshot with no changes?");
  102. if (!self.options.includeDocumentMetadataChanges) {
  103. // Remove the metadata-only changes.
  104. std::vector<DocumentViewChange> changes;
  105. for (const DocumentViewChange &change : snapshot.documentChanges) {
  106. if (change.type() != DocumentViewChange::Type::kMetadata) {
  107. changes.push_back(change);
  108. }
  109. }
  110. snapshot = [[FSTViewSnapshot alloc] initWithQuery:snapshot.query
  111. documents:snapshot.documents
  112. oldDocuments:snapshot.oldDocuments
  113. documentChanges:std::move(changes)
  114. fromCache:snapshot.fromCache
  115. mutatedKeys:snapshot.mutatedKeys
  116. syncStateChanged:snapshot.syncStateChanged
  117. excludesMetadataChanges:YES];
  118. }
  119. if (!self.raisedInitialEvent) {
  120. if ([self shouldRaiseInitialEventForSnapshot:snapshot onlineState:self.onlineState]) {
  121. [self raiseInitialEventForSnapshot:snapshot];
  122. }
  123. } else if ([self shouldRaiseEventForSnapshot:snapshot]) {
  124. self.viewSnapshotHandler(snapshot, nil);
  125. }
  126. self.snapshot = snapshot;
  127. }
  128. - (void)queryDidError:(NSError *)error {
  129. self.viewSnapshotHandler(nil, error);
  130. }
  131. - (void)applyChangedOnlineState:(OnlineState)onlineState {
  132. self.onlineState = onlineState;
  133. if (self.snapshot && !self.raisedInitialEvent &&
  134. [self shouldRaiseInitialEventForSnapshot:self.snapshot onlineState:onlineState]) {
  135. [self raiseInitialEventForSnapshot:self.snapshot];
  136. }
  137. }
  138. - (BOOL)shouldRaiseInitialEventForSnapshot:(FSTViewSnapshot *)snapshot
  139. onlineState:(OnlineState)onlineState {
  140. HARD_ASSERT(!self.raisedInitialEvent,
  141. "Determining whether to raise initial event, but already had first event.");
  142. // Always raise the first event when we're synced
  143. if (!snapshot.fromCache) {
  144. return YES;
  145. }
  146. // NOTE: We consider OnlineState.Unknown as online (it should become Offline or Online if we
  147. // wait long enough).
  148. BOOL maybeOnline = onlineState != OnlineState::Offline;
  149. // Don't raise the event if we're online, aren't synced yet (checked
  150. // above) and are waiting for a sync.
  151. if (self.options.waitForSyncWhenOnline && maybeOnline) {
  152. HARD_ASSERT(snapshot.fromCache, "Waiting for sync, but snapshot is not from cache.");
  153. return NO;
  154. }
  155. // Raise data from cache if we have any documents or we are offline
  156. return !snapshot.documents.isEmpty || onlineState == OnlineState::Offline;
  157. }
  158. - (BOOL)shouldRaiseEventForSnapshot:(FSTViewSnapshot *)snapshot {
  159. // We don't need to handle includeDocumentMetadataChanges here because the Metadata only changes
  160. // have already been stripped out if needed. At this point the only changes we will see are the
  161. // ones we should propagate.
  162. if (!snapshot.documentChanges.empty()) {
  163. return YES;
  164. }
  165. BOOL hasPendingWritesChanged =
  166. self.snapshot && self.snapshot.hasPendingWrites != snapshot.hasPendingWrites;
  167. if (snapshot.syncStateChanged || hasPendingWritesChanged) {
  168. return self.options.includeQueryMetadataChanges;
  169. }
  170. // Generally we should have hit one of the cases above, but it's possible to get here if there
  171. // were only metadata docChanges and they got stripped out.
  172. return NO;
  173. }
  174. - (void)raiseInitialEventForSnapshot:(FSTViewSnapshot *)snapshot {
  175. HARD_ASSERT(!self.raisedInitialEvent, "Trying to raise initial events for second time");
  176. snapshot = [FSTViewSnapshot snapshotForInitialDocuments:snapshot.documents
  177. query:snapshot.query
  178. mutatedKeys:snapshot.mutatedKeys
  179. fromCache:snapshot.fromCache
  180. excludesMetadataChanges:snapshot.excludesMetadataChanges];
  181. self.raisedInitialEvent = YES;
  182. self.viewSnapshotHandler(snapshot, nil);
  183. }
  184. @end
  185. #pragma mark - FSTEventManager
  186. @interface FSTEventManager () <FSTSyncEngineDelegate>
  187. - (instancetype)initWithSyncEngine:(FSTSyncEngine *)syncEngine NS_DESIGNATED_INITIALIZER;
  188. @property(nonatomic, strong, readonly) FSTSyncEngine *syncEngine;
  189. @property(nonatomic, strong, readonly)
  190. NSMutableDictionary<FSTQuery *, FSTQueryListenersInfo *> *queries;
  191. @property(nonatomic, assign) OnlineState onlineState;
  192. @end
  193. @implementation FSTEventManager
  194. + (instancetype)eventManagerWithSyncEngine:(FSTSyncEngine *)syncEngine {
  195. return [[FSTEventManager alloc] initWithSyncEngine:syncEngine];
  196. }
  197. - (instancetype)initWithSyncEngine:(FSTSyncEngine *)syncEngine {
  198. if (self = [super init]) {
  199. _syncEngine = syncEngine;
  200. _queries = [NSMutableDictionary dictionary];
  201. _syncEngine.syncEngineDelegate = self;
  202. }
  203. return self;
  204. }
  205. - (TargetId)addListener:(FSTQueryListener *)listener {
  206. FSTQuery *query = listener.query;
  207. BOOL firstListen = NO;
  208. FSTQueryListenersInfo *queryInfo = self.queries[query];
  209. if (!queryInfo) {
  210. firstListen = YES;
  211. queryInfo = [[FSTQueryListenersInfo alloc] init];
  212. self.queries[query] = queryInfo;
  213. }
  214. [queryInfo.listeners addObject:listener];
  215. [listener applyChangedOnlineState:self.onlineState];
  216. if (queryInfo.viewSnapshot) {
  217. [listener queryDidChangeViewSnapshot:queryInfo.viewSnapshot];
  218. }
  219. if (firstListen) {
  220. queryInfo.targetID = [self.syncEngine listenToQuery:query];
  221. }
  222. return queryInfo.targetID;
  223. }
  224. - (void)removeListener:(FSTQueryListener *)listener {
  225. FSTQuery *query = listener.query;
  226. BOOL lastListen = NO;
  227. FSTQueryListenersInfo *queryInfo = self.queries[query];
  228. if (queryInfo) {
  229. [queryInfo.listeners removeObject:listener];
  230. lastListen = (queryInfo.listeners.count == 0);
  231. }
  232. if (lastListen) {
  233. [self.queries removeObjectForKey:query];
  234. [self.syncEngine stopListeningToQuery:query];
  235. }
  236. }
  237. - (void)handleViewSnapshots:(NSArray<FSTViewSnapshot *> *)viewSnapshots {
  238. for (FSTViewSnapshot *viewSnapshot in viewSnapshots) {
  239. FSTQuery *query = viewSnapshot.query;
  240. FSTQueryListenersInfo *queryInfo = self.queries[query];
  241. if (queryInfo) {
  242. for (FSTQueryListener *listener in queryInfo.listeners) {
  243. [listener queryDidChangeViewSnapshot:viewSnapshot];
  244. }
  245. queryInfo.viewSnapshot = viewSnapshot;
  246. }
  247. }
  248. }
  249. - (void)handleError:(NSError *)error forQuery:(FSTQuery *)query {
  250. FSTQueryListenersInfo *queryInfo = self.queries[query];
  251. if (queryInfo) {
  252. for (FSTQueryListener *listener in queryInfo.listeners) {
  253. [listener queryDidError:error];
  254. }
  255. }
  256. // Remove all listeners. NOTE: We don't need to call [FSTSyncEngine stopListening] after an error.
  257. [self.queries removeObjectForKey:query];
  258. }
  259. - (void)applyChangedOnlineState:(OnlineState)onlineState {
  260. self.onlineState = onlineState;
  261. for (FSTQueryListenersInfo *info in self.queries.objectEnumerator) {
  262. for (FSTQueryListener *listener in info.listeners) {
  263. [listener applyChangedOnlineState:onlineState];
  264. }
  265. }
  266. }
  267. @end
  268. NS_ASSUME_NONNULL_END