aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/Source/Model
diff options
context:
space:
mode:
authorGravatar Sebastian Schmidt <mrschmidt@google.com>2017-12-04 16:42:43 -0800
committerGravatar Sebastian Schmidt <mrschmidt@google.com>2017-12-05 23:22:16 -0800
commita8948ced263e7d3ce870dff47cf7c29b12806247 (patch)
treee0720cc0d4e6738e317d4b9f75e73fd344135829 /Firestore/Source/Model
parented3e4982f9f0194851509603e5e649e5152ba897 (diff)
Adding SnapshotOptions to deal with pending ServerTimestamps
Diffstat (limited to 'Firestore/Source/Model')
-rw-r--r--Firestore/Source/Model/FSTFieldValue.h66
-rw-r--r--Firestore/Source/Model/FSTFieldValue.m80
-rw-r--r--Firestore/Source/Model/FSTMutation.h8
-rw-r--r--Firestore/Source/Model/FSTMutation.m29
-rw-r--r--Firestore/Source/Model/FSTMutationBatch.m2
5 files changed, 144 insertions, 41 deletions
diff --git a/Firestore/Source/Model/FSTFieldValue.h b/Firestore/Source/Model/FSTFieldValue.h
index 6de9793..969b3b0 100644
--- a/Firestore/Source/Model/FSTFieldValue.h
+++ b/Firestore/Source/Model/FSTFieldValue.h
@@ -22,6 +22,7 @@
@class FSTDocumentKey;
@class FSTFieldPath;
@class FSTTimestamp;
+@class FSTFieldValueOptions;
@class FIRGeoPoint;
NS_ASSUME_NONNULL_BEGIN
@@ -40,6 +41,30 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
FSTTypeOrderObject,
};
+/** Defines the return value for pending server timestamps. */
+typedef NS_ENUM(NSInteger, FSTServerTimestampBehavior) {
+ FSTServerTimestampBehaviorDefault,
+ FSTServerTimestampBehaviorEstimate,
+ FSTServerTimestampBehaviorPrevious,
+};
+
+/** Holds properties that define field value deserialization options. */
+@interface FSTFieldValueOptions : NSObject
+
+@property(nonatomic, readonly) FSTServerTimestampBehavior serverTimestampBehavior;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+/** Creates a FSTFieldValueOptions instance that specifies deserialization behavior for pending
+ * server timestamps. */
+- (instancetype)initWithServerTimestampBehavior:(FSTServerTimestampBehavior)serverTimestampBehavior
+ NS_DESIGNATED_INITIALIZER;
+
+/** Returns the default deserialization options. */
++ (instancetype)defaultOptions;
+
+@end
+
/**
* Abstract base class representing an immutable data value as stored in Firestore. FSTFieldValue
* represents all the different kinds of values that can be stored in fields in a document.
@@ -71,6 +96,14 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
*/
- (id)value;
+/**
+ * Converts an FSTFieldValue into the value that users will see in document snapshots.
+ *
+ * Options can be provided to configure the deserialization of some field values (such as server
+ * timestamps).
+ */
+- (id)valueWithOptions:(FSTFieldValueOptions *)options;
+
/** Compares against another FSTFieldValue. */
- (NSComparisonResult)compare:(FSTFieldValue *)other;
@@ -81,7 +114,7 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
*/
@interface FSTNullValue : FSTFieldValue
+ (instancetype)nullValue;
-- (id)value;
+- (id)valueWithOptions:(FSTFieldValueOptions *)options;
@end
/**
@@ -91,7 +124,7 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
+ (instancetype)trueValue;
+ (instancetype)falseValue;
+ (instancetype)booleanValue:(BOOL)value;
-- (NSNumber *)value;
+- (NSNumber *)valueWithOptions:(FSTFieldValueOptions *)options;
@end
/**
@@ -106,8 +139,8 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
*/
@interface FSTIntegerValue : FSTNumberValue
+ (instancetype)integerValue:(int64_t)value;
-- (NSNumber *)value;
- (int64_t)internalValue;
+- (NSNumber *)valueWithOptions:(FSTFieldValueOptions *)options;
@end
/**
@@ -116,8 +149,8 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
@interface FSTDoubleValue : FSTNumberValue
+ (instancetype)doubleValue:(double)value;
+ (instancetype)nanValue;
-- (NSNumber *)value;
- (double)internalValue;
+- (NSNumber *)valueWithOptions:(FSTFieldValueOptions *)options;
@end
/**
@@ -125,7 +158,7 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
*/
@interface FSTStringValue : FSTFieldValue
+ (instancetype)stringValue:(NSString *)value;
-- (NSString *)value;
+- (NSString *)valueWithOptions:(FSTFieldValueOptions *)options;
@end
/**
@@ -133,7 +166,7 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
*/
@interface FSTTimestampValue : FSTFieldValue
+ (instancetype)timestampValue:(FSTTimestamp *)value;
-- (NSDate *)value;
+- (NSDate *)valueWithOptions:(FSTFieldValueOptions *)options;
- (FSTTimestamp *)internalValue;
@end
@@ -144,15 +177,18 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
* - FSTServerTimestampValue instances are created as the result of applying an FSTTransformMutation
* (see [FSTTransformMutation applyTo]). They can only exist in the local view of a document.
* Therefore they do not need to be parsed or serialized.
- * - When evaluated locally (e.g. via FSTDocumentSnapshot data), they evaluate to NSNull (at least
- * for now, see b/62064202).
+ * - When evaluated locally (e.g. via FSTDocumentSnapshot data), they by default evaluate to NSNull.
+ * This behavior can be configured by passing custom FSTFieldValueOptions to `valueWithOptions:`.
* - They sort after all FSTTimestampValues. With respect to other FSTServerTimestampValues, they
* sort by their localWriteTime.
*/
@interface FSTServerTimestampValue : FSTFieldValue
-+ (instancetype)serverTimestampValueWithLocalWriteTime:(FSTTimestamp *)localWriteTime;
-- (NSNull *)value;
++ (instancetype)serverTimestampValueWithLocalWriteTime:(FSTTimestamp *)localWriteTime
+ previousValue:(nullable FSTFieldValue *)previousValue;
+
@property(nonatomic, strong, readonly) FSTTimestamp *localWriteTime;
+@property(nonatomic, strong, readonly, nullable) FSTFieldValue *previousValue;
+
@end
/**
@@ -160,7 +196,7 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
*/
@interface FSTGeoPointValue : FSTFieldValue
+ (instancetype)geoPointValue:(FIRGeoPoint *)value;
-- (FIRGeoPoint *)value;
+- (FIRGeoPoint *)valueWithOptions:(FSTFieldValueOptions *)options;
@end
/**
@@ -168,7 +204,7 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
*/
@interface FSTBlobValue : FSTFieldValue
+ (instancetype)blobValue:(NSData *)value;
-- (NSData *)value;
+- (NSData *)valueWithOptions:(FSTFieldValueOptions *)options;
@end
/**
@@ -176,7 +212,7 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
*/
@interface FSTReferenceValue : FSTFieldValue
+ (instancetype)referenceValue:(FSTDocumentKey *)value databaseID:(FSTDatabaseID *)databaseID;
-- (FSTDocumentKey *)value;
+- (FSTDocumentKey *)valueWithOptions:(FSTFieldValueOptions *)options;
@property(nonatomic, strong, readonly) FSTDatabaseID *databaseID;
@end
@@ -200,7 +236,7 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
- (instancetype)init NS_UNAVAILABLE;
-- (NSDictionary<NSString *, id> *)value;
+- (NSDictionary<NSString *, id> *)valueWithOptions:(FSTFieldValueOptions *)options;
- (FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *)internalValue;
/** Returns the value at the given path if it exists. Returns nil otherwise. */
@@ -234,7 +270,7 @@ typedef NS_ENUM(NSInteger, FSTTypeOrder) {
- (instancetype)init NS_UNAVAILABLE;
-- (NSArray<id> *)value;
+- (NSArray<id> *)valueWithOptions:(FSTFieldValueOptions *)options;
- (NSArray<FSTFieldValue *> *)internalValue;
@end
diff --git a/Firestore/Source/Model/FSTFieldValue.m b/Firestore/Source/Model/FSTFieldValue.m
index 95ad306..65478ce 100644
--- a/Firestore/Source/Model/FSTFieldValue.m
+++ b/Firestore/Source/Model/FSTFieldValue.m
@@ -27,6 +27,34 @@
NS_ASSUME_NONNULL_BEGIN
+#pragma mark - FSTFieldValueOptions
+
+@implementation FSTFieldValueOptions
+
+- (instancetype)initWithServerTimestampBehavior:
+ (FSTServerTimestampBehavior)serverTimestampBehavior {
+ self = [super init];
+
+ if (self) {
+ _serverTimestampBehavior = serverTimestampBehavior;
+ }
+ return self;
+}
+
++ (instancetype)defaultOptions {
+ static FSTFieldValueOptions *sharedInstance = nil;
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ sharedInstance = [[FSTFieldValueOptions alloc]
+ initWithServerTimestampBehavior:FSTServerTimestampBehaviorDefault];
+ });
+
+ return sharedInstance;
+}
+
+@end
+
#pragma mark - FSTFieldValue
@interface FSTFieldValue ()
@@ -40,6 +68,10 @@ NS_ASSUME_NONNULL_BEGIN
}
- (id)value {
+ return [self valueWithOptions:[FSTFieldValueOptions defaultOptions]];
+}
+
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
@throw FSTAbstractMethodException(); // NOLINT
}
@@ -89,7 +121,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderNull;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return [NSNull null];
}
@@ -155,7 +187,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderBoolean;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return self.internalValue ? @YES : @NO;
}
@@ -233,7 +265,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return @(self.internalValue);
}
@@ -285,7 +317,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return @(self.internalValue);
}
@@ -332,7 +364,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderString;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return self.internalValue;
}
@@ -379,7 +411,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderTimestamp;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
// For developers, we expose Timestamps as Dates.
return self.internalValue.approximateDateValue;
}
@@ -410,14 +442,18 @@ NS_ASSUME_NONNULL_BEGIN
@implementation FSTServerTimestampValue
-+ (instancetype)serverTimestampValueWithLocalWriteTime:(FSTTimestamp *)localWriteTime {
- return [[FSTServerTimestampValue alloc] initWithLocalWriteTime:localWriteTime];
++ (instancetype)serverTimestampValueWithLocalWriteTime:(FSTTimestamp *)localWriteTime
+ previousValue:(nullable FSTFieldValue *)previousValue {
+ return [[FSTServerTimestampValue alloc] initWithLocalWriteTime:localWriteTime
+ previousValue:previousValue];
}
-- (id)initWithLocalWriteTime:(FSTTimestamp *)localWriteTime {
+- (id)initWithLocalWriteTime:(FSTTimestamp *)localWriteTime
+ previousValue:(nullable FSTFieldValue *)previousValue {
self = [super init];
if (self) {
_localWriteTime = localWriteTime;
+ _previousValue = previousValue;
}
return self;
}
@@ -426,9 +462,17 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderTimestamp;
}
-- (NSNull *)value {
- // For developers, server timestamps always evaluate to NSNull (for now, at least; b/62064202).
- return [NSNull null];
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
+ switch (options.serverTimestampBehavior) {
+ case FSTServerTimestampBehaviorDefault:
+ return [NSNull null];
+ case FSTServerTimestampBehaviorEstimate:
+ return [self.localWriteTime approximateDateValue];
+ case FSTServerTimestampBehaviorPrevious:
+ return self.previousValue ? [self.previousValue valueWithOptions:options] : [NSNull null];
+ default:
+ FSTFail(@"Unexpected server timestamp option: %d", (int)options.serverTimestampBehavior);
+ }
}
- (BOOL)isEqual:(id)other {
@@ -481,7 +525,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderGeoPoint;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return self.internalValue;
}
@@ -529,7 +573,7 @@ NS_ASSUME_NONNULL_BEGIN
return FSTTypeOrderBlob;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return self.internalValue;
}
@@ -573,7 +617,7 @@ NS_ASSUME_NONNULL_BEGIN
return self;
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
return self.key;
}
@@ -648,11 +692,11 @@ NS_ASSUME_NONNULL_BEGIN
return [self initWithImmutableDictionary:dictionary];
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
NSMutableDictionary *result = [NSMutableDictionary dictionary];
[self.internalValue
enumerateKeysAndObjectsUsingBlock:^(NSString *key, FSTFieldValue *obj, BOOL *stop) {
- result[key] = [obj value];
+ result[key] = [obj valueWithOptions:options];
}];
return result;
}
@@ -803,7 +847,7 @@ NS_ASSUME_NONNULL_BEGIN
return [self.internalValue hash];
}
-- (id)value {
+- (id)valueWithOptions:(FSTFieldValueOptions *)options {
NSMutableArray *result = [NSMutableArray arrayWithCapacity:_internalValue.count];
[self.internalValue enumerateObjectsUsingBlock:^(FSTFieldValue *obj, NSUInteger idx, BOOL *stop) {
[result addObject:[obj value]];
diff --git a/Firestore/Source/Model/FSTMutation.h b/Firestore/Source/Model/FSTMutation.h
index ef7f1c8..b2e7f5e 100644
--- a/Firestore/Source/Model/FSTMutation.h
+++ b/Firestore/Source/Model/FSTMutation.h
@@ -158,8 +158,10 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
* Applies this mutation to the given FSTDocument, FSTDeletedDocument or nil, if we don't have
* information about this document. Both the input and returned documents can be nil.
*
- * @param maybeDoc The document to mutate. The input document should nil if it does not currently
- * exist.
+ * @param maybeDoc The current state of the document to mutate. The input document should be nil if
+ * it does not currently exist.
+ * @param baseDoc The state of the document prior to this mutation batch. The input document should
+ * be nil if it the document did not exist.
* @param localWriteTime A timestamp indicating the local write time of the batch this mutation is
* a part of.
* @param mutationResult Optional result info from the backend. If omitted, it's assumed that
@@ -197,6 +199,7 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
* FSTSetMutation, but not necessarily for an FSTPatchMutation).
*/
- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+ baseDoc:(FSTMaybeDocument *_Nullable)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
mutationResult:(FSTMutationResult *_Nullable)mutationResult;
@@ -205,6 +208,7 @@ typedef NS_ENUM(NSUInteger, FSTPreconditionExists) {
* backend).
*/
- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+ baseDoc:(FSTMaybeDocument *_Nullable)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime;
@property(nonatomic, strong, readonly) FSTDocumentKey *key;
diff --git a/Firestore/Source/Model/FSTMutation.m b/Firestore/Source/Model/FSTMutation.m
index 5b47280..2685990 100644
--- a/Firestore/Source/Model/FSTMutation.m
+++ b/Firestore/Source/Model/FSTMutation.m
@@ -237,14 +237,16 @@ NS_ASSUME_NONNULL_BEGIN
}
- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+ baseDoc:(FSTMaybeDocument *_Nullable)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
mutationResult:(FSTMutationResult *_Nullable)mutationResult {
@throw FSTAbstractMethodException(); // NOLINT
}
- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+ baseDoc:(FSTMaybeDocument *_Nullable)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime {
- return [self applyTo:maybeDoc localWriteTime:localWriteTime mutationResult:nil];
+ return [self applyTo:maybeDoc baseDoc:baseDoc localWriteTime:localWriteTime mutationResult:nil];
}
@end
@@ -288,6 +290,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+ baseDoc:(FSTMaybeDocument *_Nullable)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
mutationResult:(FSTMutationResult *_Nullable)mutationResult {
if (mutationResult) {
@@ -363,6 +366,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+ baseDoc:(FSTMaybeDocument *_Nullable)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
mutationResult:(FSTMutationResult *_Nullable)mutationResult {
if (mutationResult) {
@@ -452,6 +456,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+ baseDoc:(FSTMaybeDocument *_Nullable)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
mutationResult:(FSTMutationResult *_Nullable)mutationResult {
if (mutationResult) {
@@ -473,8 +478,9 @@ NS_ASSUME_NONNULL_BEGIN
BOOL hasLocalMutations = (mutationResult == nil);
NSArray<FSTFieldValue *> *transformResults =
- mutationResult ? mutationResult.transformResults
- : [self localTransformResultsWithWriteTime:localWriteTime];
+ mutationResult
+ ? mutationResult.transformResults
+ : [self localTransformResultsWithPreviousDocument:baseDoc writeTime:localWriteTime];
FSTObjectValue *newData = [self transformObject:doc.data transformResults:transformResults];
return [FSTDocument documentWithData:newData
key:doc.key
@@ -486,16 +492,26 @@ NS_ASSUME_NONNULL_BEGIN
* Creates an array of "transform results" (a transform result is a field value representing the
* result of applying a transform) for use when applying an FSTTransformMutation locally.
*
+ * @param previousDocument The document prior to applying this mutation batch.
* @param localWriteTime The local time of the transform mutation (used to generate
* FSTServerTimestampValues).
* @return The transform results array.
*/
-- (NSArray<FSTFieldValue *> *)localTransformResultsWithWriteTime:(FSTTimestamp *)localWriteTime {
+- (NSArray<FSTFieldValue *> *)
+localTransformResultsWithPreviousDocument:(FSTMaybeDocument *_Nullable)previousDocument
+ writeTime:(FSTTimestamp *)localWriteTime {
NSMutableArray<FSTFieldValue *> *transformResults = [NSMutableArray array];
for (FSTFieldTransform *fieldTransform in self.fieldTransforms) {
if ([fieldTransform.transform isKindOfClass:[FSTServerTimestampTransform class]]) {
- [transformResults addObject:[FSTServerTimestampValue
- serverTimestampValueWithLocalWriteTime:localWriteTime]];
+ FSTFieldValue *previousValue = nil;
+
+ if (previousDocument && [previousDocument isMemberOfClass:[FSTDocument class]]) {
+ previousValue = [((FSTDocument *)previousDocument) fieldForPath:fieldTransform.path];
+ }
+
+ [transformResults
+ addObject:[FSTServerTimestampValue serverTimestampValueWithLocalWriteTime:localWriteTime
+ previousValue:previousValue]];
} else {
FSTFail(@"Encountered unknown transform: %@", fieldTransform);
}
@@ -552,6 +568,7 @@ NS_ASSUME_NONNULL_BEGIN
}
- (FSTMaybeDocument *_Nullable)applyTo:(FSTMaybeDocument *_Nullable)maybeDoc
+ baseDoc:(FSTMaybeDocument *_Nullable)baseDoc
localWriteTime:(FSTTimestamp *)localWriteTime
mutationResult:(FSTMutationResult *_Nullable)mutationResult {
if (mutationResult) {
diff --git a/Firestore/Source/Model/FSTMutationBatch.m b/Firestore/Source/Model/FSTMutationBatch.m
index 3677908..1d16de5 100644
--- a/Firestore/Source/Model/FSTMutationBatch.m
+++ b/Firestore/Source/Model/FSTMutationBatch.m
@@ -71,6 +71,7 @@ const FSTBatchID kFSTBatchIDUnknown = -1;
mutationBatchResult:(FSTMutationBatchResult *_Nullable)mutationBatchResult {
FSTAssert(!maybeDoc || [maybeDoc.key isEqualToKey:documentKey],
@"applyTo: key %@ doesn't match maybeDoc key %@", documentKey, maybeDoc.key);
+ FSTMaybeDocument *baseDoc = maybeDoc;
if (mutationBatchResult) {
FSTAssert(mutationBatchResult.mutationResults.count == self.mutations.count,
@"Mismatch between mutations length (%lu) and results length (%lu)",
@@ -83,6 +84,7 @@ const FSTBatchID kFSTBatchIDUnknown = -1;
FSTMutationResult *_Nullable mutationResult = mutationBatchResult.mutationResults[i];
if ([mutation.key isEqualToKey:documentKey]) {
maybeDoc = [mutation applyTo:maybeDoc
+ baseDoc:baseDoc
localWriteTime:self.localWriteTime
mutationResult:mutationResult];
}