aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore
diff options
context:
space:
mode:
authorGravatar Michael Lehenbauer <mikelehen@gmail.com>2017-12-15 14:56:03 -0800
committerGravatar GitHub <noreply@github.com>2017-12-15 14:56:03 -0800
commitd4de7a6e86476991e2363dd09f623b2f0edfbee4 (patch)
tree182dfb0a5143b9465f9f01d7424982da8d0a775d /Firestore
parent98e08bc0b883d24cf2a0e658924ddd14dbf07d65 (diff)
b/68276665: Raise isFromCache=true events when offline (#567)
* Plumbs FSTOnlineState changes through to views. * View sets this.current to false on FSTOnlineStateFailed, triggering isFromCache=true events. It will automatically be returned to true once the listen is reestablished and we get a new CURRENT message. * Updated tests (and added one new one) to verify behavior. * Unifies setOnlineStateToUnknown, setOnlineStateToHealthy, and updateAndBroadcastOnlineState into a single updateOnlineState method. * Split disableNetwork into (public) disableNetwork and (private) disableNetworkWithTargetOnlineState methods.. * Some miscellaneous comment cleanup. * Add missing comment per CR feedback.
Diffstat (limited to 'Firestore')
-rw-r--r--Firestore/CHANGELOG.md7
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRQueryTests.m44
-rw-r--r--Firestore/Example/Tests/SpecTests/FSTSpecTests.m2
-rw-r--r--Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.h3
-rw-r--r--Firestore/Example/Tests/SpecTests/FSTSyncEngineTestDriver.m7
-rw-r--r--Firestore/Example/Tests/SpecTests/json/listen_spec_test.json14
-rw-r--r--Firestore/Example/Tests/SpecTests/json/offline_spec_test.json165
-rw-r--r--Firestore/Source/Core/FSTFirestoreClient.h2
-rw-r--r--Firestore/Source/Core/FSTFirestoreClient.m7
-rw-r--r--Firestore/Source/Core/FSTSyncEngine.h3
-rw-r--r--Firestore/Source/Core/FSTSyncEngine.m15
-rw-r--r--Firestore/Source/Core/FSTTypes.h9
-rw-r--r--Firestore/Source/Core/FSTView.h7
-rw-r--r--Firestore/Source/Core/FSTView.m18
-rw-r--r--Firestore/Source/Remote/FSTRemoteStore.m77
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];
}
}