aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/Source
diff options
context:
space:
mode:
Diffstat (limited to 'Firestore/Source')
-rw-r--r--Firestore/Source/API/FIRDocumentReference.mm25
-rw-r--r--Firestore/Source/API/FIRQuery.mm38
-rw-r--r--Firestore/Source/Core/FSTFirestoreClient.h22
-rw-r--r--Firestore/Source/Core/FSTFirestoreClient.mm63
-rw-r--r--Firestore/Source/Public/FIRDocumentReference.h19
-rw-r--r--Firestore/Source/Public/FIRFirestoreSource.h48
-rw-r--r--Firestore/Source/Public/FIRQuery.h20
7 files changed, 229 insertions, 6 deletions
diff --git a/Firestore/Source/API/FIRDocumentReference.mm b/Firestore/Source/API/FIRDocumentReference.mm
index 2423472..c2fc546 100644
--- a/Firestore/Source/API/FIRDocumentReference.mm
+++ b/Firestore/Source/API/FIRDocumentReference.mm
@@ -22,6 +22,7 @@
#include <utility>
#import "FIRFirestoreErrors.h"
+#import "FIRFirestoreSource.h"
#import "FIRSnapshotMetadata.h"
#import "Firestore/Source/API/FIRCollectionReference+Internal.h"
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
@@ -162,6 +163,17 @@ NS_ASSUME_NONNULL_BEGIN
- (void)getDocumentWithCompletion:(void (^)(FIRDocumentSnapshot *_Nullable document,
NSError *_Nullable error))completion {
+ return [self getDocumentWithSource:FIRFirestoreSourceDefault completion:completion];
+}
+
+- (void)getDocumentWithSource:(FIRFirestoreSource)source
+ completion:(void (^)(FIRDocumentSnapshot *_Nullable document,
+ NSError *_Nullable error))completion {
+ if (source == FIRFirestoreSourceCache) {
+ [self.firestore.client getDocumentFromLocalCache:self completion:completion];
+ return;
+ }
+
FSTListenOptions *options = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES
includeDocumentMetadataChanges:YES
waitForSyncWhenOnline:YES];
@@ -188,7 +200,6 @@ NS_ASSUME_NONNULL_BEGIN
// offline.
// 2) Actually call the completion handler with an error if the document doesn't exist when
// you are offline.
- // TODO(dimond): Use proper error domain
completion(nil,
[NSError errorWithDomain:FIRFirestoreErrorDomain
code:FIRFirestoreErrorCodeUnavailable
@@ -196,6 +207,18 @@ NS_ASSUME_NONNULL_BEGIN
NSLocalizedDescriptionKey :
@"Failed to get document because the client is offline.",
}]);
+ } else if (snapshot.exists && snapshot.metadata.fromCache &&
+ source == FIRFirestoreSourceServer) {
+ completion(nil,
+ [NSError errorWithDomain:FIRFirestoreErrorDomain
+ code:FIRFirestoreErrorCodeUnavailable
+ userInfo:@{
+ NSLocalizedDescriptionKey :
+ @"Failed to get document from server. (However, this "
+ @"document does exist in the local cache. Run again "
+ @"without setting source to FIRFirestoreSourceServer to "
+ @"retrieve the cached document.)"
+ }]);
} else {
completion(snapshot, nil);
}
diff --git a/Firestore/Source/API/FIRQuery.mm b/Firestore/Source/API/FIRQuery.mm
index 14dcaef..2d78ac0 100644
--- a/Firestore/Source/API/FIRQuery.mm
+++ b/Firestore/Source/API/FIRQuery.mm
@@ -17,6 +17,8 @@
#import "FIRQuery.h"
#import "FIRDocumentReference.h"
+#import "FIRFirestoreErrors.h"
+#import "FIRFirestoreSource.h"
#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h"
#import "Firestore/Source/API/FIRFieldPath+Internal.h"
@@ -96,9 +98,21 @@ NS_ASSUME_NONNULL_BEGIN
- (void)getDocumentsWithCompletion:(void (^)(FIRQuerySnapshot *_Nullable snapshot,
NSError *_Nullable error))completion {
- FSTListenOptions *options = [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES
- includeDocumentMetadataChanges:YES
- waitForSyncWhenOnline:YES];
+ [self getDocumentsWithSource:FIRFirestoreSourceDefault completion:completion];
+}
+
+- (void)getDocumentsWithSource:(FIRFirestoreSource)source
+ completion:(void (^)(FIRQuerySnapshot *_Nullable snapshot,
+ NSError *_Nullable error))completion {
+ if (source == FIRFirestoreSourceCache) {
+ [self.firestore.client getDocumentsFromLocalCache:self completion:completion];
+ return;
+ }
+
+ FSTListenOptions *listenOptions =
+ [[FSTListenOptions alloc] initWithIncludeQueryMetadataChanges:YES
+ includeDocumentMetadataChanges:YES
+ waitForSyncWhenOnline:YES];
dispatch_semaphore_t registered = dispatch_semaphore_create(0);
__block id<FIRListenerRegistration> listenerRegistration;
@@ -113,10 +127,24 @@ NS_ASSUME_NONNULL_BEGIN
dispatch_semaphore_wait(registered, DISPATCH_TIME_FOREVER);
[listenerRegistration remove];
- completion(snapshot, nil);
+ if (snapshot.metadata.fromCache && source == FIRFirestoreSourceServer) {
+ completion(nil,
+ [NSError errorWithDomain:FIRFirestoreErrorDomain
+ code:FIRFirestoreErrorCodeUnavailable
+ userInfo:@{
+ NSLocalizedDescriptionKey :
+ @"Failed to get documents from server. (However, these "
+ @"documents may exist in the local cache. Run again "
+ @"without setting source to FIRFirestoreSourceServer to "
+ @"retrieve the cached documents.)"
+ }]);
+ } else {
+ completion(snapshot, nil);
+ }
};
- listenerRegistration = [self addSnapshotListenerInternalWithOptions:options listener:listener];
+ listenerRegistration =
+ [self addSnapshotListenerInternalWithOptions:listenOptions listener:listener];
dispatch_semaphore_signal(registered);
}
diff --git a/Firestore/Source/Core/FSTFirestoreClient.h b/Firestore/Source/Core/FSTFirestoreClient.h
index 6da5ed3..7285e65 100644
--- a/Firestore/Source/Core/FSTFirestoreClient.h
+++ b/Firestore/Source/Core/FSTFirestoreClient.h
@@ -24,6 +24,12 @@
#include "Firestore/core/src/firebase/firestore/core/database_info.h"
#include "Firestore/core/src/firebase/firestore/model/database_id.h"
+@class FIRDocumentReference;
+@class FIRDocumentSnapshot;
+@class FIRQuery;
+@class FIRQuerySnapshot;
+@class FSTDatabaseID;
+@class FSTDatabaseInfo;
@class FSTDispatchQueue;
@class FSTDocument;
@class FSTListenOptions;
@@ -72,6 +78,22 @@ NS_ASSUME_NONNULL_BEGIN
/** Stops listening to a query previously listened to. */
- (void)removeListener:(FSTQueryListener *)listener;
+/**
+ * Retrieves a document from the cache via the indicated completion. If the doc
+ * doesn't exist, an error will be sent to the completion.
+ */
+- (void)getDocumentFromLocalCache:(FIRDocumentReference *)doc
+ completion:(void (^)(FIRDocumentSnapshot *_Nullable document,
+ NSError *_Nullable error))completion;
+
+/**
+ * Retrieves a (possibly empty) set of documents from the cache via the
+ * indicated completion.
+ */
+- (void)getDocumentsFromLocalCache:(FIRQuery *)query
+ completion:(void (^)(FIRQuerySnapshot *_Nullable query,
+ NSError *_Nullable error))completion;
+
/** Write mutations. completion will be notified when it's written to the backend. */
- (void)writeMutations:(NSArray<FSTMutation *> *)mutations
completion:(nullable FSTVoidErrorBlock)completion;
diff --git a/Firestore/Source/Core/FSTFirestoreClient.mm b/Firestore/Source/Core/FSTFirestoreClient.mm
index 33d1903..4f1a20b 100644
--- a/Firestore/Source/Core/FSTFirestoreClient.mm
+++ b/Firestore/Source/Core/FSTFirestoreClient.mm
@@ -19,15 +19,25 @@
#include <future> // NOLINT(build/c++11)
#include <memory>
+#import "FIRFirestoreErrors.h"
+#import "Firestore/Source/API/FIRDocumentReference+Internal.h"
+#import "Firestore/Source/API/FIRDocumentSnapshot+Internal.h"
+#import "Firestore/Source/API/FIRQuery+Internal.h"
+#import "Firestore/Source/API/FIRQuerySnapshot+Internal.h"
+#import "Firestore/Source/API/FIRSnapshotMetadata+Internal.h"
#import "Firestore/Source/Core/FSTEventManager.h"
+#import "Firestore/Source/Core/FSTQuery.h"
#import "Firestore/Source/Core/FSTSyncEngine.h"
#import "Firestore/Source/Core/FSTTransaction.h"
+#import "Firestore/Source/Core/FSTView.h"
#import "Firestore/Source/Local/FSTEagerGarbageCollector.h"
#import "Firestore/Source/Local/FSTLevelDB.h"
#import "Firestore/Source/Local/FSTLocalSerializer.h"
#import "Firestore/Source/Local/FSTLocalStore.h"
#import "Firestore/Source/Local/FSTMemoryPersistence.h"
#import "Firestore/Source/Local/FSTNoOpGarbageCollector.h"
+#import "Firestore/Source/Model/FSTDocument.h"
+#import "Firestore/Source/Model/FSTDocumentSet.h"
#import "Firestore/Source/Remote/FSTDatastore.h"
#import "Firestore/Source/Remote/FSTRemoteStore.h"
#import "Firestore/Source/Remote/FSTSerializerBeta.h"
@@ -271,6 +281,59 @@ NS_ASSUME_NONNULL_BEGIN
}];
}
+- (void)getDocumentFromLocalCache:(FIRDocumentReference *)doc
+ completion:(void (^)(FIRDocumentSnapshot *_Nullable document,
+ NSError *_Nullable error))completion {
+ [self.workerDispatchQueue dispatchAsync:^{
+ FSTMaybeDocument *maybeDoc = [self.localStore readDocument:doc.key];
+ if (maybeDoc) {
+ completion([FIRDocumentSnapshot snapshotWithFirestore:doc.firestore
+ documentKey:doc.key
+ document:(FSTDocument *)maybeDoc
+ fromCache:YES],
+ nil);
+ } else {
+ completion(nil,
+ [NSError errorWithDomain:FIRFirestoreErrorDomain
+ code:FIRFirestoreErrorCodeUnavailable
+ userInfo:@{
+ NSLocalizedDescriptionKey :
+ @"Failed to get document from cache. (However, this "
+ @"document may exist on the server. Run again without "
+ @"setting source to FIRFirestoreSourceCache to attempt to "
+ @"retrieve the document from the server.)",
+ }]);
+ }
+ }];
+}
+
+- (void)getDocumentsFromLocalCache:(FIRQuery *)query
+ completion:(void (^)(FIRQuerySnapshot *_Nullable query,
+ NSError *_Nullable error))completion {
+ [self.workerDispatchQueue dispatchAsync:^{
+
+ FSTDocumentDictionary *docs = [self.localStore executeQuery:query.query];
+ FSTDocumentKeySet *remoteKeys = [FSTDocumentKeySet keySet];
+
+ FSTView *view = [[FSTView alloc] initWithQuery:query.query remoteDocuments:remoteKeys];
+ FSTViewDocumentChanges *viewDocChanges = [view computeChangesWithDocuments:docs];
+ FSTViewChange *viewChange = [view applyChangesToDocuments:viewDocChanges];
+ FSTAssert(viewChange.limboChanges.count == 0,
+ @"View returned limbo documents during local-only query execution.");
+
+ FSTViewSnapshot *snapshot = viewChange.snapshot;
+ FIRSnapshotMetadata *metadata =
+ [FIRSnapshotMetadata snapshotMetadataWithPendingWrites:snapshot.hasPendingWrites
+ fromCache:snapshot.fromCache];
+
+ completion([FIRQuerySnapshot snapshotWithFirestore:query.firestore
+ originalQuery:query.query
+ snapshot:snapshot
+ metadata:metadata],
+ nil);
+ }];
+}
+
- (void)writeMutations:(NSArray<FSTMutation *> *)mutations
completion:(nullable FSTVoidErrorBlock)completion {
[self.workerDispatchQueue dispatchAsync:^{
diff --git a/Firestore/Source/Public/FIRDocumentReference.h b/Firestore/Source/Public/FIRDocumentReference.h
index e7ba6eb..4aa8c45 100644
--- a/Firestore/Source/Public/FIRDocumentReference.h
+++ b/Firestore/Source/Public/FIRDocumentReference.h
@@ -16,6 +16,7 @@
#import <Foundation/Foundation.h>
+#import "FIRFirestoreSource.h"
#import "FIRListenerRegistration.h"
@class FIRCollectionReference;
@@ -166,12 +167,30 @@ NS_SWIFT_NAME(DocumentReference)
/**
* Reads the document referenced by this `FIRDocumentReference`.
*
+ * This method attempts to provide up-to-date data when possible by waiting for
+ * data from the server, but it may return cached data or fail if you are
+ * offline and the server cannot be reached. See the
+ * `getDocument(source:completion:)` method to change this behavior.
+ *
* @param completion a block to execute once the document has been successfully read.
*/
- (void)getDocumentWithCompletion:(FIRDocumentSnapshotBlock)completion
NS_SWIFT_NAME(getDocument(completion:));
/**
+ * Reads the document referenced by this `FIRDocumentReference`.
+ *
+ * @param source indicates whether the results should be fetched from the cache
+ * only (`Source.cache`), the server only (`Source.server`), or to attempt
+ * the server and fall back to the cache (`Source.default`).
+ * @param completion a block to execute once the document has been successfully read.
+ */
+// clang-format off
+- (void)getDocumentWithSource:(FIRFirestoreSource)source completion:(FIRDocumentSnapshotBlock)completion
+ NS_SWIFT_NAME(getDocument(source:completion:));
+// clang-format on
+
+/**
* Attaches a listener for DocumentSnapshot events.
*
* @param listener The listener to attach.
diff --git a/Firestore/Source/Public/FIRFirestoreSource.h b/Firestore/Source/Public/FIRFirestoreSource.h
new file mode 100644
index 0000000..c133747
--- /dev/null
+++ b/Firestore/Source/Public/FIRFirestoreSource.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2018 Google
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+/**
+ * An enum that configures the behavior of `DocumentReference.getDocument()` and
+ * `Query.getDocuments()`. By providing a source enum the `getDocument[s]`
+ * methods can be configured to fetch results only from the server, only from
+ * the local cache, or attempt to fetch results from the server and fall back to
+ * the cache (which is the default).
+ *
+ * Setting the source to `Source.default` causes Firestore to try to retrieve an
+ * up-to-date (server-retrieved) snapshot, but fall back to returning cached
+ * data if the server can't be reached.
+ *
+ * Setting the source to `Source.server` causes Firestore to avoid the cache,
+ * generating an error if the server cannot be reached. Note that the cache will
+ * still be updated if the server request succeeds. Also note that
+ * latency-compensation still takes effect, so any pending write operations will
+ * be visible in the returned data (merged into the server-provided data).
+ *
+ * Setting the source to `Source.cache` causes Firestore to immediately return a
+ * value from the cache, ignoring the server completely (implying that the
+ * returned value may be stale with respect to the value on the server). If
+ * there is no data in the cache to satisfy the `getDocument[s]` call,
+ * `DocumentReference.getDocument()` will return an error and
+ * `QuerySnapshot.getDocuments()` will return an empty `QuerySnapshot` with no
+ * documents.
+ */
+typedef NS_ENUM(NSUInteger, FIRFirestoreSource) {
+ FIRFirestoreSourceDefault,
+ FIRFirestoreSourceServer,
+ FIRFirestoreSourceCache
+} NS_SWIFT_NAME(FirestoreSource);
diff --git a/Firestore/Source/Public/FIRQuery.h b/Firestore/Source/Public/FIRQuery.h
index a28af39..946cf06 100644
--- a/Firestore/Source/Public/FIRQuery.h
+++ b/Firestore/Source/Public/FIRQuery.h
@@ -16,6 +16,7 @@
#import <Foundation/Foundation.h>
+#import "FIRFirestoreSource.h"
#import "FIRListenerRegistration.h"
@class FIRFieldPath;
@@ -44,6 +45,11 @@ NS_SWIFT_NAME(Query)
/**
* Reads the documents matching this query.
*
+ * This method attempts to provide up-to-date data when possible by waiting for
+ * data from the server, but it may return cached data or fail if you are
+ * offline and the server cannot be reached. See the
+ * `getDocuments(source:completion:)` method to change this behavior.
+ *
* @param completion a block to execute once the documents have been successfully read.
* documentSet will be `nil` only if error is `non-nil`.
*/
@@ -51,6 +57,20 @@ NS_SWIFT_NAME(Query)
NS_SWIFT_NAME(getDocuments(completion:));
/**
+ * Reads the documents matching this query.
+ *
+ * @param source indicates whether the results should be fetched from the cache
+ * only (`Source.cache`), the server only (`Source.server`), or to attempt
+ * the server and fall back to the cache (`Source.default`).
+ * @param completion a block to execute once the documents have been successfully read.
+ * documentSet will be `nil` only if error is `non-nil`.
+ */
+// clang-format off
+- (void)getDocumentsWithSource:(FIRFirestoreSource)source completion:(FIRQuerySnapshotBlock)completion
+ NS_SWIFT_NAME(getDocuments(source:completion:));
+// clang-format on
+
+/**
* Attaches a listener for QuerySnapshot events.
*
* @param listener The listener to attach.