diff options
-rw-r--r-- | Firestore/CHANGELOG.md | 7 | ||||
-rw-r--r-- | Firestore/Example/Tests/Integration/API/FIRQueryTests.m | 44 | ||||
-rw-r--r-- | Firestore/Example/Tests/SpecTests/FSTSpecTests.m | 2 | ||||
-rw-r--r-- | Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h | 3 | ||||
-rw-r--r-- | Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m | 7 | ||||
-rw-r--r-- | Firestore/Example/Tests/SpecTests/json/listen_spec_test.json | 14 | ||||
-rw-r--r-- | Firestore/Example/Tests/SpecTests/json/offline_spec_test.json | 165 | ||||
-rw-r--r-- | Firestore/Source/Core/FSTFirestoreClient.h | 2 | ||||
-rw-r--r-- | Firestore/Source/Core/FSTFirestoreClient.m | 7 | ||||
-rw-r--r-- | Firestore/Source/Core/FSTSyncEngine.h | 3 | ||||
-rw-r--r-- | Firestore/Source/Core/FSTSyncEngine.m | 15 | ||||
-rw-r--r-- | Firestore/Source/Core/FSTTypes.h | 9 | ||||
-rw-r--r-- | Firestore/Source/Core/FSTView.h | 7 | ||||
-rw-r--r-- | Firestore/Source/Core/FSTView.m | 18 | ||||
-rw-r--r-- | Firestore/Source/Remote/FSTRemoteStore.m | 77 |
15 files changed, 332 insertions, 48 deletions
diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index 8daae91..59858e6 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -1,4 +1,4 @@ -# Unreleased +# Unreleased (firestore-api-changes) - [changed] Removed the includeMetadataChanges property in FIRDocumentListenOptions to avoid confusion with the factory method of the same name. - [changed] Added a commit method that takes no completion handler to FIRWriteBatch. @@ -8,6 +8,11 @@ - [changed] For non-existing documents, DocumentSnapshot.data() now returns `nil` instead of throwing an exception. A non-nullable QueryDocumentSnapshot is introduced for Queries to reduce the number of nil-checks in your code. +- [changed] Snapshot listeners (with the `includeMetadataChanges` option + enabled) now receive an event with `snapshot.metadata.isFromCache` set to + `true` if the SDK loses its connection to the backend. A new event with + `snapshot.metadata.isFromCache` set to false will be raised once the + connection is restored and the query is in sync with the backend again. # v0.9.4 - [changed] Firestore no longer has a direct dependency on FirebaseAuth. diff --git a/Firestore/Example/Tests/Integration/API/FIRQueryTests.m b/Firestore/Example/Tests/Integration/API/FIRQueryTests.m index ccc635e..14351a8 100644 --- a/Firestore/Example/Tests/Integration/API/FIRQueryTests.m +++ b/Firestore/Example/Tests/Integration/API/FIRQueryTests.m @@ -18,9 +18,10 @@ #import <XCTest/XCTest.h> -#import "Firestore/Source/Core/FSTFirestoreClient.h" - +#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h" #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" +#import "Firestore/Source/API/FIRFirestore+Internal.h" +#import "Firestore/Source/Core/FSTFirestoreClient.h" @interface FIRQueryTests : FSTIntegrationTestCase @end @@ -211,4 +212,43 @@ XCTAssertEqualObjects(FIRQuerySnapshotGetData(docs), (@[ testDocs[@"ab"], testDocs[@"ba"] ])); } +- (void)testQueriesFireFromCacheWhenOffline { + NSDictionary *testDocs = @{ + @"a" : @{@"foo" : @1}, + }; + FIRCollectionReference *collection = [self collectionRefWithDocuments:testDocs]; + + FIRQueryListenOptions *options = [[[FIRQueryListenOptions options] + includeDocumentMetadataChanges:YES] includeQueryMetadataChanges:YES]; + id<FIRListenerRegistration> registration = + [collection addSnapshotListenerWithOptions:options + listener:self.eventAccumulator.valueEventHandler]; + + FIRQuerySnapshot *querySnap = [self.eventAccumulator awaitEventWithName:@"initial event"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(querySnap), @[ @{ @"foo" : @1 } ]); + XCTAssertEqual(querySnap.metadata.isFromCache, NO); + XCTestExpectation *networkDisabled = [self expectationWithDescription:@"disable network"]; + [collection.firestore.client disableNetworkWithCompletion:^(NSError *error) { + [networkDisabled fulfill]; + }]; + [self awaitExpectations]; + + querySnap = [self.eventAccumulator awaitEventWithName:@"offline event with isFromCache=YES"]; + XCTAssertEqual(querySnap.metadata.isFromCache, YES); + + // TODO(b/70631617): There's currently a backend bug that prevents us from using a resume token + // right away (against hexa at least). So we sleep. :-( :-( Anything over ~10ms seems to be + // sufficient. + [NSThread sleepForTimeInterval:0.2f]; + + XCTestExpectation *networkEnabled = [self expectationWithDescription:@"enable network"]; + [collection.firestore.client enableNetworkWithCompletion:^(NSError *error) { + [networkEnabled fulfill]; + }]; + [self awaitExpectations]; + + querySnap = [self.eventAccumulator awaitEventWithName:@"back online event with isFromCache=NO"]; + XCTAssertEqual(querySnap.metadata.isFromCache, NO); +} + @end diff --git a/Firestore/Example/Tests/SpecTests/FSTSpecTests.m b/Firestore/Example/Tests/SpecTests/FSTSpecTests.m index 2c1b8db..0bd7b43 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSpecTests.m +++ b/Firestore/Example/Tests/SpecTests/FSTSpecTests.m @@ -156,7 +156,7 @@ static NSString *const kNoIOSTag = @"no-ios"; FSTTargetID actualID = [self.driver addUserListenerWithQuery:query]; FSTTargetID expectedID = [listenSpec[0] intValue]; - XCTAssertEqual(actualID, expectedID); + XCTAssertEqual(actualID, expectedID, @"targetID assigned to listen"); } - (void)doUnlisten:(NSArray *)unlistenSpec { diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h index 3d031bd..7cb2726 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h @@ -14,6 +14,7 @@ * limitations under the License. */ +#import <Firestore/Source/Remote/FSTRemoteStore.h> #import <Foundation/Foundation.h> #import "Firestore/Source/Core/FSTTypes.h" @@ -76,7 +77,7 @@ typedef NSDictionary<FSTUser *, NSArray<FSTOutstandingWrite *> *> FSTOutstanding * * Each method on the driver injects a different event into the system. */ -@interface FSTSyncEngineTestDriver : NSObject +@interface FSTSyncEngineTestDriver : NSObject <FSTOnlineStateDelegate> /** * Initializes the underlying FSTSyncEngine with the given local persistence implementation and diff --git a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m index 896a292..d618aee 100644 --- a/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m +++ b/Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m @@ -119,7 +119,7 @@ NS_ASSUME_NONNULL_BEGIN _remoteStore.syncEngine = _syncEngine; _eventManager = [FSTEventManager eventManagerWithSyncEngine:_syncEngine]; - _remoteStore.onlineStateDelegate = _eventManager; + _remoteStore.onlineStateDelegate = self; // Set up internal event tracking for the spec tests. NSMutableArray<FSTQueryEvent *> *events = [NSMutableArray array]; @@ -139,6 +139,11 @@ NS_ASSUME_NONNULL_BEGIN return self; } +- (void)watchStreamDidChangeOnlineState:(FSTOnlineState)onlineState { + [self.syncEngine applyOnlineStateChange:onlineState]; + [self.eventManager watchStreamDidChangeOnlineState:onlineState]; +} + - (void)start { [self.localStore start]; [self.remoteStore start]; diff --git a/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json b/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json index e607710..7bfe557 100644 --- a/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json +++ b/Firestore/Example/Tests/SpecTests/json/listen_spec_test.json @@ -1608,7 +1608,19 @@ "stateExpect": { "activeTargets": {}, "limboDocs": [] - } + }, + "expect": [ + { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false + } + ] }, { "enableNetwork": true, diff --git a/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json b/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json index f542a6e..3981cec 100644 --- a/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json +++ b/Firestore/Example/Tests/SpecTests/json/offline_spec_test.json @@ -294,5 +294,170 @@ ] } ] + }, + "Queries revert to fromCache=true when offline.": { + "describeName": "Offline:", + "itName": "Queries revert to fromCache=true when offline.", + "tags": [], + "config": { + "useGarbageCollection": true + }, + "steps": [ + { + "userListen": [ + 2, + { + "path": "collection", + "filters": [], + "orderBys": [] + } + ], + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "resumeToken": "" + } + } + } + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [ + [ + "collection/a", + 1000, + { + "key": "a" + } + ] + ], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ], + "watchSnapshot": 1000, + "expect": [ + { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "added": [ + [ + "collection/a", + 1000, + { + "key": "a" + } + ] + ], + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false + } + ] + }, + { + "watchStreamClose": { + "error": { + "code": 14, + "message": "Simulated Backend Error" + } + }, + "stateExpect": { + "activeTargets": { + "2": { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "resumeToken": "resume-token-1000" + } + } + } + }, + { + "watchStreamClose": { + "error": { + "code": 14, + "message": "Simulated Backend Error" + } + } + }, + { + "watchStreamClose": { + "error": { + "code": 14, + "message": "Simulated Backend Error" + } + }, + "expect": [ + { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "errorCode": 0, + "fromCache": true, + "hasPendingWrites": false + } + ] + }, + { + "watchAck": [ + 2 + ] + }, + { + "watchEntity": { + "docs": [], + "targets": [ + 2 + ] + } + }, + { + "watchCurrent": [ + [ + 2 + ], + "resume-token-1000" + ], + "watchSnapshot": 1000, + "expect": [ + { + "query": { + "path": "collection", + "filters": [], + "orderBys": [] + }, + "errorCode": 0, + "fromCache": false, + "hasPendingWrites": false + } + ] + } + ] } } diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h index 6a1e11b..0ecf2f6 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.h +++ b/Firestore/Source/Core/FSTFirestoreClient.h @@ -38,7 +38,7 @@ NS_ASSUME_NONNULL_BEGIN * SDK architecture. It is responsible for creating the worker queue that is shared by all of the * other components in the system. */ -@interface FSTFirestoreClient : NSObject +@interface FSTFirestoreClient : NSObject <FSTOnlineStateDelegate> /** * Creates and returns a FSTFirestoreClient with the given parameters. diff --git a/Firestore/Source/Core/FSTFirestoreClient.m b/Firestore/Source/Core/FSTFirestoreClient.m index 2e0e407..d8bdde7 100644 --- a/Firestore/Source/Core/FSTFirestoreClient.m +++ b/Firestore/Source/Core/FSTFirestoreClient.m @@ -172,7 +172,7 @@ NS_ASSUME_NONNULL_BEGIN // Setup wiring for remote store. _remoteStore.syncEngine = _syncEngine; - _remoteStore.onlineStateDelegate = _eventManager; + _remoteStore.onlineStateDelegate = self; // NOTE: RemoteStore depends on LocalStore (for persisting stream tokens, refilling mutation // queue, etc.) so must be started after LocalStore. @@ -187,6 +187,11 @@ NS_ASSUME_NONNULL_BEGIN [self.syncEngine userDidChange:user]; } +- (void)watchStreamDidChangeOnlineState:(FSTOnlineState)onlineState { + [self.syncEngine applyOnlineStateChange:onlineState]; + [self.eventManager watchStreamDidChangeOnlineState:onlineState]; +} + - (void)disableNetworkWithCompletion:(nullable FSTVoidErrorBlock)completion { [self.workerDispatchQueue dispatchAsync:^{ [self.remoteStore disableNetwork]; diff --git a/Firestore/Source/Core/FSTSyncEngine.h b/Firestore/Source/Core/FSTSyncEngine.h index bb45196..316ac05 100644 --- a/Firestore/Source/Core/FSTSyncEngine.h +++ b/Firestore/Source/Core/FSTSyncEngine.h @@ -100,6 +100,9 @@ NS_ASSUME_NONNULL_BEGIN - (void)userDidChange:(FSTUser *)user; +/** Applies an FSTOnlineState change to the sync engine and notifies any views of the change. */ +- (void)applyOnlineStateChange:(FSTOnlineState)onlineState; + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTSyncEngine.m b/Firestore/Source/Core/FSTSyncEngine.m index 98658e4..aa1ccae 100644 --- a/Firestore/Source/Core/FSTSyncEngine.m +++ b/Firestore/Source/Core/FSTSyncEngine.m @@ -318,6 +318,21 @@ NS_ASSUME_NONNULL_BEGIN [self emitNewSnapshotsWithChanges:changes remoteEvent:remoteEvent]; } +- (void)applyOnlineStateChange:(FSTOnlineState)onlineState { + NSMutableArray<FSTViewSnapshot *> *newViewSnapshots = [NSMutableArray array]; + [self.queryViewsByQuery + enumerateKeysAndObjectsUsingBlock:^(FSTQuery *query, FSTQueryView *queryView, BOOL *stop) { + FSTViewChange *viewChange = [queryView.view applyOnlineStateChange:onlineState]; + FSTAssert(viewChange.limboChanges.count == 0, + @"OnlineState should not affect limbo documents."); + if (viewChange.snapshot) { + [newViewSnapshots addObject:viewChange.snapshot]; + } + }]; + + [self.delegate handleViewSnapshots:newViewSnapshots]; +} + - (void)rejectListenWithTargetID:(FSTBoxedTargetID *)targetID error:(NSError *)error { [self assertDelegateExistsForSelector:_cmd]; diff --git a/Firestore/Source/Core/FSTTypes.h b/Firestore/Source/Core/FSTTypes.h index c10f1bf..b47bd0b 100644 --- a/Firestore/Source/Core/FSTTypes.h +++ b/Firestore/Source/Core/FSTTypes.h @@ -67,8 +67,8 @@ typedef void (^FSTTransactionBlock)(FSTTransaction *transaction, typedef NS_ENUM(NSUInteger, FSTOnlineState) { /** * The Firestore client is in an unknown online state. This means the client is either not - * actively trying to establish a connection or it was previously in an unknown state and is - * trying to establish a connection. + * actively trying to establish a connection or it is currently trying to establish a connection, + * but it has not succeeded or failed yet. */ FSTOnlineStateUnknown, @@ -80,9 +80,8 @@ typedef NS_ENUM(NSUInteger, FSTOnlineState) { FSTOnlineStateHealthy, /** - * The client has tried to establish a connection but has failed. - * This state is reached after either a connection attempt failed or a healthy stream was closed - * for unexpected reasons. + * The client considers itself offline. It is either trying to establish a connection but + * failing, or it has been explicitly marked offline via a call to `disableNetwork`. */ FSTOnlineStateFailed }; diff --git a/Firestore/Source/Core/FSTView.h b/Firestore/Source/Core/FSTView.h index ed230a3..beadf46 100644 --- a/Firestore/Source/Core/FSTView.h +++ b/Firestore/Source/Core/FSTView.h @@ -16,6 +16,7 @@ #import <Foundation/Foundation.h> +#import "Firestore/Source/Core/FSTTypes.h" #import "Firestore/Source/Model/FSTDocumentDictionary.h" #import "Firestore/Source/Model/FSTDocumentKeySet.h" @@ -138,6 +139,12 @@ typedef NS_ENUM(NSInteger, FSTLimboDocumentChangeType) { - (FSTViewChange *)applyChangesToDocuments:(FSTViewDocumentChanges *)docChanges targetChange:(nullable FSTTargetChange *)targetChange; +/** + * Applies an FSTOnlineState change to the view, potentially generating an FSTViewChange if the + * view's syncState changes as a result. + */ +- (FSTViewChange *)applyOnlineStateChange:(FSTOnlineState)onlineState; + @end NS_ASSUME_NONNULL_END diff --git a/Firestore/Source/Core/FSTView.m b/Firestore/Source/Core/FSTView.m index 9b44bf4..d539bb6 100644 --- a/Firestore/Source/Core/FSTView.m +++ b/Firestore/Source/Core/FSTView.m @@ -330,6 +330,24 @@ static NSComparisonResult FSTCompareDocumentViewChangeTypes(FSTDocumentViewChang } } +- (FSTViewChange *)applyOnlineStateChange:(FSTOnlineState)onlineState { + if (self.isCurrent && onlineState == FSTOnlineStateFailed) { + // If we're offline, set `current` to NO and then call applyChanges to refresh our syncState + // and generate an FSTViewChange as appropriate. We are guaranteed to get a new FSTTargetChange + // that sets `current` back to YES once the client is back online. + self.current = NO; + return + [self applyChangesToDocuments:[[FSTViewDocumentChanges alloc] + initWithDocumentSet:self.documentSet + changeSet:[FSTDocumentViewChangeSet changeSet] + needsRefill:NO + mutatedKeys:self.mutatedKeys]]; + } else { + // No effect, just return a no-op FSTViewChange. + return [[FSTViewChange alloc] initWithSnapshot:nil limboChanges:@[]]; + } +} + #pragma mark - Private methods /** Returns whether the doc for the given key should be in limbo. */ diff --git a/Firestore/Source/Remote/FSTRemoteStore.m b/Firestore/Source/Remote/FSTRemoteStore.m index 063e487..12cffd4 100644 --- a/Firestore/Source/Remote/FSTRemoteStore.m +++ b/Firestore/Source/Remote/FSTRemoteStore.m @@ -160,27 +160,39 @@ static const int kOnlineAttemptsBeforeFailure = 2; [self enableNetwork]; } -- (void)setOnlineStateToHealthy { - self.shouldWarnOffline = NO; - [self updateAndNotifyAboutOnlineState:FSTOnlineStateHealthy]; -} +/** + * Updates our OnlineState to the new state, updating local state and notifying the + * onlineStateHandler as appropriate. + */ +- (void)updateOnlineState:(FSTOnlineState)newState { + if (newState == FSTOnlineStateHealthy) { + // We've connected to watch at least once. Don't warn the developer about being offline going + // forward. + self.shouldWarnOffline = NO; + } else if (newState == FSTOnlineStateUnknown) { + // The state is set to unknown when a healthy stream is closed (e.g. due to a token timeout) or + // when we have no active listens and therefore there's no need to start the stream. Assuming + // there is (possibly in the future) an active listen, then we will eventually move to state + // Online or Failed, but we always want to make at least kOnlineAttemptsBeforeFailure attempts + // before failing, so we reset the count here. + self.watchStreamFailures = 0; + } -- (void)setOnlineStateToUnknown { - // The state is set to unknown when a healthy stream is closed (e.g. due to a token timeout) or - // when we have no active listens and therefore there's no need to start the stream. Assuming - // there is (possibly in the future) an active listen, then we will eventually move to state - // Online or Failed, but we always want to make at least kOnlineAttemptsBeforeFailure attempts - // before failing, so we reset the count here. - self.watchStreamFailures = 0; - [self updateAndNotifyAboutOnlineState:FSTOnlineStateUnknown]; + // Update and broadcast the new state. + if (newState != self.watchStreamOnlineState) { + self.watchStreamOnlineState = newState; + [self.onlineStateDelegate watchStreamDidChangeOnlineState:newState]; + } } +/** + * Updates our FSTOnlineState as appropriate after the watch stream reports a failure. The first + * failure moves us to the 'Unknown' state. We then may allow multiple failures (based on + * kOnlineAttemptsBeforeFailure) before we actually transition to FSTOnlineStateFailed. + */ - (void)updateOnlineStateAfterFailure { - // The first failure after we are successfully connected moves us to the 'Unknown' state. We - // then may make multiple attempts (based on kOnlineAttemptsBeforeFailure) before we actually - // report failure. if (self.watchStreamOnlineState == FSTOnlineStateHealthy) { - [self setOnlineStateToUnknown]; + [self updateOnlineState:FSTOnlineStateUnknown]; } else { self.watchStreamFailures++; if (self.watchStreamFailures >= kOnlineAttemptsBeforeFailure) { @@ -188,19 +200,11 @@ static const int kOnlineAttemptsBeforeFailure = 2; FSTWarn(@"Could not reach Firestore backend."); self.shouldWarnOffline = NO; } - [self updateAndNotifyAboutOnlineState:FSTOnlineStateFailed]; + [self updateOnlineState:FSTOnlineStateFailed]; } } } -- (void)updateAndNotifyAboutOnlineState:(FSTOnlineState)watchStreamOnlineState { - BOOL didChange = (watchStreamOnlineState != self.watchStreamOnlineState); - self.watchStreamOnlineState = watchStreamOnlineState; - if (didChange) { - [self.onlineStateDelegate watchStreamDidChangeOnlineState:watchStreamOnlineState]; - } -} - #pragma mark Online/Offline state - (BOOL)isNetworkEnabled { @@ -227,12 +231,16 @@ static const int kOnlineAttemptsBeforeFailure = 2; [self fillWritePipeline]; // This may start the writeStream. // We move back to the unknown state because we might not want to re-open the stream - [self setOnlineStateToUnknown]; + [self updateOnlineState:FSTOnlineStateUnknown]; } - (void)disableNetwork { - [self updateAndNotifyAboutOnlineState:FSTOnlineStateFailed]; + // Set the FSTOnlineState to failed so get()'s return from cache, etc. + [self disableNetworkWithTargetOnlineState:FSTOnlineStateFailed]; +} +/** Disables the network, setting the FSTOnlineState to the specified targetOnlineState. */ +- (void)disableNetworkWithTargetOnlineState:(FSTOnlineState)targetOnlineState { // NOTE: We're guaranteed not to get any further events from these streams (not even a close // event). [self.watchStream stop]; @@ -243,6 +251,8 @@ static const int kOnlineAttemptsBeforeFailure = 2; self.writeStream = nil; self.watchStream = nil; + + [self updateOnlineState:targetOnlineState]; } #pragma mark Shutdown @@ -250,13 +260,12 @@ static const int kOnlineAttemptsBeforeFailure = 2; - (void)shutdown { FSTLog(@"FSTRemoteStore %p shutting down", (__bridge void *)self); - // Don't fire initial listener callbacks on shutdown. - self.onlineStateDelegate = nil; - // For now, all shutdown logic is handled by disableNetwork(). We might expand on this in the // future. if ([self isNetworkEnabled]) { - [self disableNetwork]; + // Set the FSTOnlineState to Unknown (rather than Failed) to avoid potentially triggering + // spurious listener events with cached data, etc. + [self disableNetworkWithTargetOnlineState:FSTOnlineStateUnknown]; } } @@ -266,7 +275,7 @@ static const int kOnlineAttemptsBeforeFailure = 2; // Tear down and re-create our network streams. This will ensure we get a fresh auth token // for the new user and re-fill the write pipeline with new mutations from the LocalStore // (since mutations are per-user). - [self disableNetwork]; + [self disableNetworkWithTargetOnlineState:FSTOnlineStateUnknown]; [self enableNetwork]; } @@ -348,7 +357,7 @@ static const int kOnlineAttemptsBeforeFailure = 2; - (void)watchStreamDidChange:(FSTWatchChange *)change snapshotVersion:(FSTSnapshotVersion *)snapshotVersion { // Mark the connection as healthy because we got a message from the server. - [self setOnlineStateToHealthy]; + [self updateOnlineState:FSTOnlineStateHealthy]; FSTWatchTargetChange *watchTargetChange = [change isKindOfClass:[FSTWatchTargetChange class]] ? (FSTWatchTargetChange *)change : nil; @@ -391,7 +400,7 @@ static const int kOnlineAttemptsBeforeFailure = 2; } else { // We don't need to restart the watch stream because there are no active targets. The online // state is set to unknown because there is no active attempt at establishing a connection. - [self setOnlineStateToUnknown]; + [self updateOnlineState:FSTOnlineStateUnknown]; } } |