| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084 |
- /*
- * Copyright 2017 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 "Firestore/Source/Remote/FSTSerializerBeta.h"
- #import <GRPCClient/GRPCCall.h>
- #import "Firestore/Protos/objc/google/firestore/v1beta1/Common.pbobjc.h"
- #import "Firestore/Protos/objc/google/firestore/v1beta1/Document.pbobjc.h"
- #import "Firestore/Protos/objc/google/firestore/v1beta1/Firestore.pbobjc.h"
- #import "Firestore/Protos/objc/google/firestore/v1beta1/Query.pbobjc.h"
- #import "Firestore/Protos/objc/google/firestore/v1beta1/Write.pbobjc.h"
- #import "Firestore/Protos/objc/google/rpc/Status.pbobjc.h"
- #import "Firestore/Protos/objc/google/type/Latlng.pbobjc.h"
- #import "FIRFirestoreErrors.h"
- #import "FIRGeoPoint.h"
- #import "Firestore/Source/Core/FSTQuery.h"
- #import "Firestore/Source/Core/FSTSnapshotVersion.h"
- #import "Firestore/Source/Core/FSTTimestamp.h"
- #import "Firestore/Source/Local/FSTQueryData.h"
- #import "Firestore/Source/Model/FSTDatabaseID.h"
- #import "Firestore/Source/Model/FSTDocument.h"
- #import "Firestore/Source/Model/FSTDocumentKey.h"
- #import "Firestore/Source/Model/FSTFieldValue.h"
- #import "Firestore/Source/Model/FSTMutation.h"
- #import "Firestore/Source/Model/FSTMutationBatch.h"
- #import "Firestore/Source/Model/FSTPath.h"
- #import "Firestore/Source/Remote/FSTExistenceFilter.h"
- #import "Firestore/Source/Remote/FSTWatchChange.h"
- #import "Firestore/Source/Util/FSTAssert.h"
- NS_ASSUME_NONNULL_BEGIN
- @interface FSTSerializerBeta ()
- @property(nonatomic, strong, readonly) FSTDatabaseID *databaseID;
- @end
- @implementation FSTSerializerBeta
- - (instancetype)initWithDatabaseID:(FSTDatabaseID *)databaseID {
- self = [super init];
- if (self) {
- _databaseID = databaseID;
- }
- return self;
- }
- #pragma mark - FSTSnapshotVersion <=> GPBTimestamp
- - (GPBTimestamp *)encodedTimestamp:(FSTTimestamp *)timestamp {
- GPBTimestamp *result = [GPBTimestamp message];
- result.seconds = timestamp.seconds;
- result.nanos = timestamp.nanos;
- return result;
- }
- - (FSTTimestamp *)decodedTimestamp:(GPBTimestamp *)timestamp {
- return [[FSTTimestamp alloc] initWithSeconds:timestamp.seconds nanos:timestamp.nanos];
- }
- - (GPBTimestamp *)encodedVersion:(FSTSnapshotVersion *)version {
- return [self encodedTimestamp:version.timestamp];
- }
- - (FSTSnapshotVersion *)decodedVersion:(GPBTimestamp *)version {
- return [FSTSnapshotVersion versionWithTimestamp:[self decodedTimestamp:version]];
- }
- #pragma mark - FIRGeoPoint <=> GTPLatLng
- - (GTPLatLng *)encodedGeoPoint:(FIRGeoPoint *)geoPoint {
- GTPLatLng *latLng = [GTPLatLng message];
- latLng.latitude = geoPoint.latitude;
- latLng.longitude = geoPoint.longitude;
- return latLng;
- }
- - (FIRGeoPoint *)decodedGeoPoint:(GTPLatLng *)latLng {
- return [[FIRGeoPoint alloc] initWithLatitude:latLng.latitude longitude:latLng.longitude];
- }
- #pragma mark - FSTDocumentKey <=> Key proto
- - (NSString *)encodedDocumentKey:(FSTDocumentKey *)key {
- return [self encodedResourcePathForDatabaseID:self.databaseID path:key.path];
- }
- - (FSTDocumentKey *)decodedDocumentKey:(NSString *)name {
- FSTResourcePath *path = [self decodedResourcePathWithDatabaseID:name];
- FSTAssert([[path segmentAtIndex:1] isEqualToString:self.databaseID.projectID],
- @"Tried to deserialize key from different project.");
- FSTAssert([[path segmentAtIndex:3] isEqualToString:self.databaseID.databaseID],
- @"Tried to deserialize key from different datbase.");
- return [FSTDocumentKey keyWithPath:[self localResourcePathForQualifiedResourcePath:path]];
- }
- - (NSString *)encodedResourcePathForDatabaseID:(FSTDatabaseID *)databaseID
- path:(FSTResourcePath *)path {
- return [[[[self encodedResourcePathForDatabaseID:databaseID] pathByAppendingSegment:@"documents"]
- pathByAppendingPath:path] canonicalString];
- }
- - (FSTResourcePath *)decodedResourcePathWithDatabaseID:(NSString *)name {
- FSTResourcePath *path = [FSTResourcePath pathWithString:name];
- FSTAssert([self validQualifiedResourcePath:path], @"Tried to deserialize invalid key %@", path);
- return path;
- }
- - (NSString *)encodedQueryPath:(FSTResourcePath *)path {
- if (path.length == 0) {
- // If the path is empty, the backend requires we leave off the /documents at the end.
- return [self encodedDatabaseID];
- }
- return [self encodedResourcePathForDatabaseID:self.databaseID path:path];
- }
- - (FSTResourcePath *)decodedQueryPath:(NSString *)name {
- FSTResourcePath *resource = [self decodedResourcePathWithDatabaseID:name];
- if (resource.length == 4) {
- return [FSTResourcePath pathWithSegments:@[]];
- } else {
- return [self localResourcePathForQualifiedResourcePath:resource];
- }
- }
- - (FSTResourcePath *)encodedResourcePathForDatabaseID:(FSTDatabaseID *)databaseID {
- return [FSTResourcePath
- pathWithSegments:@[ @"projects", databaseID.projectID, @"databases", databaseID.databaseID ]];
- }
- - (FSTResourcePath *)localResourcePathForQualifiedResourcePath:(FSTResourcePath *)resourceName {
- FSTAssert(
- resourceName.length > 4 && [[resourceName segmentAtIndex:4] isEqualToString:@"documents"],
- @"Tried to deserialize invalid key %@", resourceName);
- return [resourceName pathByRemovingFirstSegments:5];
- }
- - (BOOL)validQualifiedResourcePath:(FSTResourcePath *)path {
- return path.length >= 4 && [[path segmentAtIndex:0] isEqualToString:@"projects"] &&
- [[path segmentAtIndex:2] isEqualToString:@"databases"];
- }
- - (NSString *)encodedDatabaseID {
- return [[self encodedResourcePathForDatabaseID:self.databaseID] canonicalString];
- }
- #pragma mark - FSTFieldValue <=> Value proto
- - (GCFSValue *)encodedFieldValue:(FSTFieldValue *)fieldValue {
- Class class = [fieldValue class];
- if (class == [FSTNullValue class]) {
- return [self encodedNull];
- } else if (class == [FSTBooleanValue class]) {
- return [self encodedBool:[[fieldValue value] boolValue]];
- } else if (class == [FSTIntegerValue class]) {
- return [self encodedInteger:[[fieldValue value] longLongValue]];
- } else if (class == [FSTDoubleValue class]) {
- return [self encodedDouble:[[fieldValue value] doubleValue]];
- } else if (class == [FSTStringValue class]) {
- return [self encodedString:[fieldValue value]];
- } else if (class == [FSTTimestampValue class]) {
- return [self encodedTimestampValue:((FSTTimestampValue *)fieldValue).internalValue];
- } else if (class == [FSTGeoPointValue class]) {
- return [self encodedGeoPointValue:[fieldValue value]];
- } else if (class == [FSTBlobValue class]) {
- return [self encodedBlobValue:[fieldValue value]];
- } else if (class == [FSTReferenceValue class]) {
- FSTReferenceValue *ref = (FSTReferenceValue *)fieldValue;
- return [self encodedReferenceValueForDatabaseID:[ref databaseID] key:[ref value]];
- } else if (class == [FSTObjectValue class]) {
- GCFSValue *result = [GCFSValue message];
- result.mapValue = [self encodedMapValue:(FSTObjectValue *)fieldValue];
- return result;
- } else if (class == [FSTArrayValue class]) {
- GCFSValue *result = [GCFSValue message];
- result.arrayValue = [self encodedArrayValue:(FSTArrayValue *)fieldValue];
- return result;
- } else {
- FSTFail(@"Unhandled type %@ on %@", NSStringFromClass([fieldValue class]), fieldValue);
- }
- }
- - (FSTFieldValue *)decodedFieldValue:(GCFSValue *)valueProto {
- switch (valueProto.valueTypeOneOfCase) {
- case GCFSValue_ValueType_OneOfCase_NullValue:
- return [FSTNullValue nullValue];
- case GCFSValue_ValueType_OneOfCase_BooleanValue:
- return [FSTBooleanValue booleanValue:valueProto.booleanValue];
- case GCFSValue_ValueType_OneOfCase_IntegerValue:
- return [FSTIntegerValue integerValue:valueProto.integerValue];
- case GCFSValue_ValueType_OneOfCase_DoubleValue:
- return [FSTDoubleValue doubleValue:valueProto.doubleValue];
- case GCFSValue_ValueType_OneOfCase_StringValue:
- return [FSTStringValue stringValue:valueProto.stringValue];
- case GCFSValue_ValueType_OneOfCase_TimestampValue:
- return [FSTTimestampValue timestampValue:[self decodedTimestamp:valueProto.timestampValue]];
- case GCFSValue_ValueType_OneOfCase_GeoPointValue:
- return [FSTGeoPointValue geoPointValue:[self decodedGeoPoint:valueProto.geoPointValue]];
- case GCFSValue_ValueType_OneOfCase_BytesValue:
- return [FSTBlobValue blobValue:valueProto.bytesValue];
- case GCFSValue_ValueType_OneOfCase_ReferenceValue:
- return [self decodedReferenceValue:valueProto.referenceValue];
- case GCFSValue_ValueType_OneOfCase_ArrayValue:
- return [self decodedArrayValue:valueProto.arrayValue];
- case GCFSValue_ValueType_OneOfCase_MapValue:
- return [self decodedMapValue:valueProto.mapValue];
- default:
- FSTFail(@"Unhandled type %d on %@", valueProto.valueTypeOneOfCase, valueProto);
- }
- }
- - (GCFSValue *)encodedNull {
- GCFSValue *result = [GCFSValue message];
- result.nullValue = GPBNullValue_NullValue;
- return result;
- }
- - (GCFSValue *)encodedBool:(BOOL)value {
- GCFSValue *result = [GCFSValue message];
- result.booleanValue = value;
- return result;
- }
- - (GCFSValue *)encodedDouble:(double)value {
- GCFSValue *result = [GCFSValue message];
- result.doubleValue = value;
- return result;
- }
- - (GCFSValue *)encodedInteger:(int64_t)value {
- GCFSValue *result = [GCFSValue message];
- result.integerValue = value;
- return result;
- }
- - (GCFSValue *)encodedString:(NSString *)value {
- GCFSValue *result = [GCFSValue message];
- result.stringValue = value;
- return result;
- }
- - (GCFSValue *)encodedTimestampValue:(FSTTimestamp *)value {
- GCFSValue *result = [GCFSValue message];
- result.timestampValue = [self encodedTimestamp:value];
- return result;
- }
- - (GCFSValue *)encodedGeoPointValue:(FIRGeoPoint *)value {
- GCFSValue *result = [GCFSValue message];
- result.geoPointValue = [self encodedGeoPoint:value];
- return result;
- }
- - (GCFSValue *)encodedBlobValue:(NSData *)value {
- GCFSValue *result = [GCFSValue message];
- result.bytesValue = value;
- return result;
- }
- - (GCFSValue *)encodedReferenceValueForDatabaseID:(FSTDatabaseID *)databaseID
- key:(FSTDocumentKey *)key {
- GCFSValue *result = [GCFSValue message];
- result.referenceValue = [self encodedResourcePathForDatabaseID:databaseID path:key.path];
- return result;
- }
- - (FSTReferenceValue *)decodedReferenceValue:(NSString *)resourceName {
- FSTResourcePath *path = [self decodedResourcePathWithDatabaseID:resourceName];
- NSString *project = [path segmentAtIndex:1];
- NSString *database = [path segmentAtIndex:3];
- FSTDatabaseID *databaseID = [FSTDatabaseID databaseIDWithProject:project database:database];
- FSTDocumentKey *key =
- [FSTDocumentKey keyWithPath:[self localResourcePathForQualifiedResourcePath:path]];
- return [FSTReferenceValue referenceValue:key databaseID:databaseID];
- }
- - (GCFSArrayValue *)encodedArrayValue:(FSTArrayValue *)arrayValue {
- GCFSArrayValue *proto = [GCFSArrayValue message];
- NSMutableArray<GCFSValue *> *protoContents = [proto valuesArray];
- [[arrayValue internalValue]
- enumerateObjectsUsingBlock:^(FSTFieldValue *value, NSUInteger idx, BOOL *stop) {
- GCFSValue *converted = [self encodedFieldValue:value];
- [protoContents addObject:converted];
- }];
- return proto;
- }
- - (FSTArrayValue *)decodedArrayValue:(GCFSArrayValue *)arrayValue {
- NSMutableArray<FSTFieldValue *> *contents =
- [NSMutableArray arrayWithCapacity:arrayValue.valuesArray_Count];
- [arrayValue.valuesArray
- enumerateObjectsUsingBlock:^(GCFSValue *value, NSUInteger idx, BOOL *stop) {
- [contents addObject:[self decodedFieldValue:value]];
- }];
- return [[FSTArrayValue alloc] initWithValueNoCopy:contents];
- }
- - (GCFSMapValue *)encodedMapValue:(FSTObjectValue *)value {
- GCFSMapValue *result = [GCFSMapValue message];
- result.fields = [self encodedFields:value];
- return result;
- }
- - (FSTObjectValue *)decodedMapValue:(GCFSMapValue *)map {
- return [self decodedFields:map.fields];
- }
- /**
- * Encodes an FSTObjectValue into a dictionary.
- * @return a new dictionary that can be assigned to a field in another proto.
- */
- - (NSMutableDictionary<NSString *, GCFSValue *> *)encodedFields:(FSTObjectValue *)value {
- FSTImmutableSortedDictionary<NSString *, FSTFieldValue *> *fields = value.internalValue;
- NSMutableDictionary<NSString *, GCFSValue *> *result = [NSMutableDictionary dictionary];
- [fields enumerateKeysAndObjectsUsingBlock:^(NSString *key, FSTFieldValue *obj, BOOL *stop) {
- GCFSValue *converted = [self encodedFieldValue:obj];
- result[key] = converted;
- }];
- return result;
- }
- - (FSTObjectValue *)decodedFields:(NSDictionary<NSString *, GCFSValue *> *)fields {
- __block FSTObjectValue *result = [FSTObjectValue objectValue];
- [fields enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, GCFSValue *_Nonnull obj,
- BOOL *_Nonnull stop) {
- FSTFieldPath *path = [FSTFieldPath pathWithSegments:@[ key ]];
- FSTFieldValue *value = [self decodedFieldValue:obj];
- result = [result objectBySettingValue:value forPath:path];
- }];
- return result;
- }
- #pragma mark - FSTObjectValue <=> Document proto
- - (GCFSDocument *)encodedDocumentWithFields:(FSTObjectValue *)objectValue
- key:(FSTDocumentKey *)key {
- GCFSDocument *proto = [GCFSDocument message];
- proto.name = [self encodedDocumentKey:key];
- proto.fields = [self encodedFields:objectValue];
- return proto;
- }
- #pragma mark - FSTMaybeDocument <= BatchGetDocumentsResponse proto
- - (FSTMaybeDocument *)decodedMaybeDocumentFromBatch:(GCFSBatchGetDocumentsResponse *)response {
- switch (response.resultOneOfCase) {
- case GCFSBatchGetDocumentsResponse_Result_OneOfCase_Found:
- return [self decodedFoundDocument:response];
- case GCFSBatchGetDocumentsResponse_Result_OneOfCase_Missing:
- return [self decodedDeletedDocument:response];
- default:
- FSTFail(@"Unknown document type: %@", response);
- }
- }
- - (FSTDocument *)decodedFoundDocument:(GCFSBatchGetDocumentsResponse *)response {
- FSTAssert(!!response.found, @"Tried to deserialize a found document from a deleted document.");
- FSTDocumentKey *key = [self decodedDocumentKey:response.found.name];
- FSTObjectValue *value = [self decodedFields:response.found.fields];
- FSTSnapshotVersion *version = [self decodedVersion:response.found.updateTime];
- FSTAssert(![version isEqual:[FSTSnapshotVersion noVersion]],
- @"Got a document response with no snapshot version");
- return [FSTDocument documentWithData:value key:key version:version hasLocalMutations:NO];
- }
- - (FSTDeletedDocument *)decodedDeletedDocument:(GCFSBatchGetDocumentsResponse *)response {
- FSTAssert(!!response.missing, @"Tried to deserialize a deleted document from a found document.");
- FSTDocumentKey *key = [self decodedDocumentKey:response.missing];
- FSTSnapshotVersion *version = [self decodedVersion:response.readTime];
- FSTAssert(![version isEqual:[FSTSnapshotVersion noVersion]],
- @"Got a no document response with no snapshot version");
- return [FSTDeletedDocument documentWithKey:key version:version];
- }
- #pragma mark - FSTMutation => GCFSWrite proto
- - (GCFSWrite *)encodedMutation:(FSTMutation *)mutation {
- GCFSWrite *proto = [GCFSWrite message];
- Class mutationClass = [mutation class];
- if (mutationClass == [FSTSetMutation class]) {
- FSTSetMutation *set = (FSTSetMutation *)mutation;
- proto.update = [self encodedDocumentWithFields:set.value key:set.key];
- } else if (mutationClass == [FSTPatchMutation class]) {
- FSTPatchMutation *patch = (FSTPatchMutation *)mutation;
- proto.update = [self encodedDocumentWithFields:patch.value key:patch.key];
- proto.updateMask = [self encodedFieldMask:patch.fieldMask];
- } else if (mutationClass == [FSTTransformMutation class]) {
- FSTTransformMutation *transform = (FSTTransformMutation *)mutation;
- proto.transform = [GCFSDocumentTransform message];
- proto.transform.document = [self encodedDocumentKey:transform.key];
- proto.transform.fieldTransformsArray = [self encodedFieldTransforms:transform.fieldTransforms];
- // NOTE: We set a precondition of exists: true as a safety-check, since we always combine
- // FSTTransformMutations with an FSTSetMutation or FSTPatchMutation which (if successful) should
- // end up with an existing document.
- proto.currentDocument.exists = YES;
- } else if (mutationClass == [FSTDeleteMutation class]) {
- FSTDeleteMutation *delete = (FSTDeleteMutation *)mutation;
- proto.delete_p = [self encodedDocumentKey:delete.key];
- } else {
- FSTFail(@"Unknown mutation type %@", NSStringFromClass(mutationClass));
- }
- if (!mutation.precondition.isNone) {
- proto.currentDocument = [self encodedPrecondition:mutation.precondition];
- }
- return proto;
- }
- - (FSTMutation *)decodedMutation:(GCFSWrite *)mutation {
- FSTPrecondition *precondition = [mutation hasCurrentDocument]
- ? [self decodedPrecondition:mutation.currentDocument]
- : [FSTPrecondition none];
- switch (mutation.operationOneOfCase) {
- case GCFSWrite_Operation_OneOfCase_Update:
- if (mutation.hasUpdateMask) {
- return [[FSTPatchMutation alloc] initWithKey:[self decodedDocumentKey:mutation.update.name]
- fieldMask:[self decodedFieldMask:mutation.updateMask]
- value:[self decodedFields:mutation.update.fields]
- precondition:precondition];
- } else {
- return [[FSTSetMutation alloc] initWithKey:[self decodedDocumentKey:mutation.update.name]
- value:[self decodedFields:mutation.update.fields]
- precondition:precondition];
- }
- case GCFSWrite_Operation_OneOfCase_Delete_p:
- return [[FSTDeleteMutation alloc] initWithKey:[self decodedDocumentKey:mutation.delete_p]
- precondition:precondition];
- case GCFSWrite_Operation_OneOfCase_Transform: {
- FSTPreconditionExists exists = precondition.exists;
- FSTAssert(exists == FSTPreconditionExistsYes,
- @"Transforms must have precondition \"exists == true\"");
- return [[FSTTransformMutation alloc]
- initWithKey:[self decodedDocumentKey:mutation.transform.document]
- fieldTransforms:[self decodedFieldTransforms:mutation.transform.fieldTransformsArray]];
- }
- default:
- // Note that insert is intentionally unhandled, since we don't ever deal in them.
- FSTFail(@"Unknown mutation operation: %d", mutation.operationOneOfCase);
- }
- }
- - (GCFSPrecondition *)encodedPrecondition:(FSTPrecondition *)precondition {
- FSTAssert(!precondition.isNone, @"Can't serialize an empty precondition");
- GCFSPrecondition *message = [GCFSPrecondition message];
- if (precondition.updateTime) {
- message.updateTime = [self encodedVersion:precondition.updateTime];
- } else if (precondition.exists != FSTPreconditionExistsNotSet) {
- message.exists = precondition.exists == FSTPreconditionExistsYes;
- } else {
- FSTFail(@"Unknown precondition: %@", precondition);
- }
- return message;
- }
- - (FSTPrecondition *)decodedPrecondition:(GCFSPrecondition *)precondition {
- switch (precondition.conditionTypeOneOfCase) {
- case GCFSPrecondition_ConditionType_OneOfCase_GPBUnsetOneOfCase:
- return [FSTPrecondition none];
- case GCFSPrecondition_ConditionType_OneOfCase_Exists:
- return [FSTPrecondition preconditionWithExists:precondition.exists];
- case GCFSPrecondition_ConditionType_OneOfCase_UpdateTime:
- return [FSTPrecondition
- preconditionWithUpdateTime:[self decodedVersion:precondition.updateTime]];
- default:
- FSTFail(@"Unrecognized Precondition one-of case %@", precondition);
- }
- }
- - (GCFSDocumentMask *)encodedFieldMask:(FSTFieldMask *)fieldMask {
- GCFSDocumentMask *mask = [GCFSDocumentMask message];
- for (FSTFieldPath *field in fieldMask.fields) {
- [mask.fieldPathsArray addObject:field.canonicalString];
- }
- return mask;
- }
- - (FSTFieldMask *)decodedFieldMask:(GCFSDocumentMask *)fieldMask {
- NSMutableArray<FSTFieldPath *> *fields =
- [NSMutableArray arrayWithCapacity:fieldMask.fieldPathsArray_Count];
- for (NSString *path in fieldMask.fieldPathsArray) {
- [fields addObject:[FSTFieldPath pathWithServerFormat:path]];
- }
- return [[FSTFieldMask alloc] initWithFields:fields];
- }
- - (NSMutableArray<GCFSDocumentTransform_FieldTransform *> *)encodedFieldTransforms:
- (NSArray<FSTFieldTransform *> *)fieldTransforms {
- NSMutableArray *protos = [NSMutableArray array];
- for (FSTFieldTransform *fieldTransform in fieldTransforms) {
- FSTAssert([fieldTransform.transform isKindOfClass:[FSTServerTimestampTransform class]],
- @"Unknown transform: %@", fieldTransform.transform);
- GCFSDocumentTransform_FieldTransform *proto = [GCFSDocumentTransform_FieldTransform message];
- proto.fieldPath = fieldTransform.path.canonicalString;
- proto.setToServerValue = GCFSDocumentTransform_FieldTransform_ServerValue_RequestTime;
- [protos addObject:proto];
- }
- return protos;
- }
- - (NSArray<FSTFieldTransform *> *)decodedFieldTransforms:
- (NSArray<GCFSDocumentTransform_FieldTransform *> *)protos {
- NSMutableArray<FSTFieldTransform *> *fieldTransforms = [NSMutableArray array];
- for (GCFSDocumentTransform_FieldTransform *proto in protos) {
- FSTAssert(
- proto.setToServerValue == GCFSDocumentTransform_FieldTransform_ServerValue_RequestTime,
- @"Unknown transform setToServerValue: %d", proto.setToServerValue);
- [fieldTransforms
- addObject:[[FSTFieldTransform alloc]
- initWithPath:[FSTFieldPath pathWithServerFormat:proto.fieldPath]
- transform:[FSTServerTimestampTransform serverTimestampTransform]]];
- }
- return fieldTransforms;
- }
- #pragma mark - FSTMutationResult <= GCFSWriteResult proto
- - (FSTMutationResult *)decodedMutationResult:(GCFSWriteResult *)mutation {
- // NOTE: Deletes don't have an updateTime.
- FSTSnapshotVersion *_Nullable version =
- mutation.updateTime ? [self decodedVersion:mutation.updateTime] : nil;
- NSMutableArray *_Nullable transformResults = nil;
- if (mutation.transformResultsArray.count > 0) {
- transformResults = [NSMutableArray array];
- for (GCFSValue *result in mutation.transformResultsArray) {
- [transformResults addObject:[self decodedFieldValue:result]];
- }
- }
- return [[FSTMutationResult alloc] initWithVersion:version transformResults:transformResults];
- }
- #pragma mark - FSTQueryData => GCFSTarget proto
- - (nullable NSMutableDictionary<NSString *, NSString *> *)encodedListenRequestLabelsForQueryData:
- (FSTQueryData *)queryData {
- NSString *value = [self encodedLabelForPurpose:queryData.purpose];
- if (!value) {
- return nil;
- }
- NSMutableDictionary<NSString *, NSString *> *result =
- [NSMutableDictionary dictionaryWithCapacity:1];
- [result setObject:value forKey:@"goog-listen-tags"];
- return result;
- }
- - (nullable NSString *)encodedLabelForPurpose:(FSTQueryPurpose)purpose {
- switch (purpose) {
- case FSTQueryPurposeListen:
- return nil;
- case FSTQueryPurposeExistenceFilterMismatch:
- return @"existence-filter-mismatch";
- case FSTQueryPurposeLimboResolution:
- return @"limbo-document";
- default:
- FSTFail(@"Unrecognized query purpose: %lu", (unsigned long)purpose);
- }
- }
- - (GCFSTarget *)encodedTarget:(FSTQueryData *)queryData {
- GCFSTarget *result = [GCFSTarget message];
- FSTQuery *query = queryData.query;
- if ([query isDocumentQuery]) {
- result.documents = [self encodedDocumentsTarget:query];
- } else {
- result.query = [self encodedQueryTarget:query];
- }
- result.targetId = queryData.targetID;
- if (queryData.resumeToken.length > 0) {
- result.resumeToken = queryData.resumeToken;
- }
- return result;
- }
- - (GCFSTarget_DocumentsTarget *)encodedDocumentsTarget:(FSTQuery *)query {
- GCFSTarget_DocumentsTarget *result = [GCFSTarget_DocumentsTarget message];
- NSMutableArray<NSString *> *docs = result.documentsArray;
- [docs addObject:[self encodedQueryPath:query.path]];
- return result;
- }
- - (FSTQuery *)decodedQueryFromDocumentsTarget:(GCFSTarget_DocumentsTarget *)target {
- NSArray<NSString *> *documents = target.documentsArray;
- FSTAssert(documents.count == 1, @"DocumentsTarget contained other than 1 document %lu",
- (unsigned long)documents.count);
- NSString *name = documents[0];
- return [FSTQuery queryWithPath:[self decodedQueryPath:name]];
- }
- - (GCFSTarget_QueryTarget *)encodedQueryTarget:(FSTQuery *)query {
- // Dissect the path into parent, collectionId, and optional key filter.
- GCFSTarget_QueryTarget *queryTarget = [GCFSTarget_QueryTarget message];
- if (query.path.length == 0) {
- queryTarget.parent = [self encodedQueryPath:query.path];
- } else {
- FSTResourcePath *path = query.path;
- FSTAssert(path.length % 2 != 0, @"Document queries with filters are not supported.");
- queryTarget.parent = [self encodedQueryPath:[path pathByRemovingLastSegment]];
- GCFSStructuredQuery_CollectionSelector *from = [GCFSStructuredQuery_CollectionSelector message];
- from.collectionId = path.lastSegment;
- [queryTarget.structuredQuery.fromArray addObject:from];
- }
- // Encode the filters.
- GCFSStructuredQuery_Filter *_Nullable where = [self encodedFilters:query.filters];
- if (where) {
- queryTarget.structuredQuery.where = where;
- }
- NSArray<GCFSStructuredQuery_Order *> *orders = [self encodedSortOrders:query.sortOrders];
- if (orders.count) {
- [queryTarget.structuredQuery.orderByArray addObjectsFromArray:orders];
- }
- if (query.limit != NSNotFound) {
- queryTarget.structuredQuery.limit.value = (int32_t)query.limit;
- }
- if (query.startAt) {
- queryTarget.structuredQuery.startAt = [self encodedBound:query.startAt];
- }
- if (query.endAt) {
- queryTarget.structuredQuery.endAt = [self encodedBound:query.endAt];
- }
- return queryTarget;
- }
- - (FSTQuery *)decodedQueryFromQueryTarget:(GCFSTarget_QueryTarget *)target {
- FSTResourcePath *path = [self decodedQueryPath:target.parent];
- GCFSStructuredQuery *query = target.structuredQuery;
- NSUInteger fromCount = query.fromArray_Count;
- if (fromCount > 0) {
- FSTAssert(fromCount == 1,
- @"StructuredQuery.from with more than one collection is not supported.");
- GCFSStructuredQuery_CollectionSelector *from = query.fromArray[0];
- path = [path pathByAppendingSegment:from.collectionId];
- }
- NSArray<id<FSTFilter>> *filterBy;
- if (query.hasWhere) {
- filterBy = [self decodedFilters:query.where];
- } else {
- filterBy = @[];
- }
- NSArray<FSTSortOrder *> *orderBy;
- if (query.orderByArray_Count > 0) {
- orderBy = [self decodedSortOrders:query.orderByArray];
- } else {
- orderBy = @[];
- }
- NSInteger limit = NSNotFound;
- if (query.hasLimit) {
- limit = query.limit.value;
- }
- FSTBound *_Nullable startAt;
- if (query.hasStartAt) {
- startAt = [self decodedBound:query.startAt];
- }
- FSTBound *_Nullable endAt;
- if (query.hasEndAt) {
- endAt = [self decodedBound:query.endAt];
- }
- return [[FSTQuery alloc] initWithPath:path
- filterBy:filterBy
- orderBy:orderBy
- limit:limit
- startAt:startAt
- endAt:endAt];
- }
- #pragma mark Filters
- - (GCFSStructuredQuery_Filter *_Nullable)encodedFilters:(NSArray<id<FSTFilter>> *)filters {
- if (filters.count == 0) {
- return nil;
- }
- NSMutableArray<GCFSStructuredQuery_Filter *> *protos = [NSMutableArray array];
- for (id<FSTFilter> filter in filters) {
- if ([filter isKindOfClass:[FSTRelationFilter class]]) {
- [protos addObject:[self encodedRelationFilter:filter]];
- } else {
- [protos addObject:[self encodedUnaryFilter:filter]];
- }
- }
- if (protos.count == 1) {
- // Special case: no existing filters and we only need to add one filter. This can be made the
- // single root filter without a composite filter.
- return protos[0];
- }
- GCFSStructuredQuery_Filter *composite = [GCFSStructuredQuery_Filter message];
- composite.compositeFilter.op = GCFSStructuredQuery_CompositeFilter_Operator_And;
- composite.compositeFilter.filtersArray = protos;
- return composite;
- }
- - (NSArray<id<FSTFilter>> *)decodedFilters:(GCFSStructuredQuery_Filter *)proto {
- NSMutableArray<id<FSTFilter>> *result = [NSMutableArray array];
- NSArray<GCFSStructuredQuery_Filter *> *filters;
- if (proto.filterTypeOneOfCase ==
- GCFSStructuredQuery_Filter_FilterType_OneOfCase_CompositeFilter) {
- FSTAssert(proto.compositeFilter.op == GCFSStructuredQuery_CompositeFilter_Operator_And,
- @"Only AND-type composite filters are supported, got %d", proto.compositeFilter.op);
- filters = proto.compositeFilter.filtersArray;
- } else {
- filters = @[ proto ];
- }
- for (GCFSStructuredQuery_Filter *filter in filters) {
- switch (filter.filterTypeOneOfCase) {
- case GCFSStructuredQuery_Filter_FilterType_OneOfCase_CompositeFilter:
- FSTFail(@"Nested composite filters are not supported");
- case GCFSStructuredQuery_Filter_FilterType_OneOfCase_FieldFilter:
- [result addObject:[self decodedRelationFilter:filter.fieldFilter]];
- break;
- case GCFSStructuredQuery_Filter_FilterType_OneOfCase_UnaryFilter:
- [result addObject:[self decodedUnaryFilter:filter.unaryFilter]];
- break;
- default:
- FSTFail(@"Unrecognized Filter.filterType %d", filter.filterTypeOneOfCase);
- }
- }
- return result;
- }
- - (GCFSStructuredQuery_Filter *)encodedRelationFilter:(FSTRelationFilter *)filter {
- GCFSStructuredQuery_Filter *proto = [GCFSStructuredQuery_Filter message];
- GCFSStructuredQuery_FieldFilter *fieldFilter = proto.fieldFilter;
- fieldFilter.field = [self encodedFieldPath:filter.field];
- fieldFilter.op = [self encodedRelationFilterOperator:filter.filterOperator];
- fieldFilter.value = [self encodedFieldValue:filter.value];
- return proto;
- }
- - (FSTRelationFilter *)decodedRelationFilter:(GCFSStructuredQuery_FieldFilter *)proto {
- FSTFieldPath *fieldPath = [FSTFieldPath pathWithServerFormat:proto.field.fieldPath];
- FSTRelationFilterOperator filterOperator = [self decodedRelationFilterOperator:proto.op];
- FSTFieldValue *value = [self decodedFieldValue:proto.value];
- return [FSTRelationFilter filterWithField:fieldPath filterOperator:filterOperator value:value];
- }
- - (GCFSStructuredQuery_Filter *)encodedUnaryFilter:(id<FSTFilter>)filter {
- GCFSStructuredQuery_Filter *proto = [GCFSStructuredQuery_Filter message];
- proto.unaryFilter.field = [self encodedFieldPath:filter.field];
- if ([filter isKindOfClass:[FSTNanFilter class]]) {
- proto.unaryFilter.op = GCFSStructuredQuery_UnaryFilter_Operator_IsNan;
- } else if ([filter isKindOfClass:[FSTNullFilter class]]) {
- proto.unaryFilter.op = GCFSStructuredQuery_UnaryFilter_Operator_IsNull;
- } else {
- FSTFail(@"Unrecognized filter: %@", filter);
- }
- return proto;
- }
- - (id<FSTFilter>)decodedUnaryFilter:(GCFSStructuredQuery_UnaryFilter *)proto {
- FSTFieldPath *field = [FSTFieldPath pathWithServerFormat:proto.field.fieldPath];
- switch (proto.op) {
- case GCFSStructuredQuery_UnaryFilter_Operator_IsNan:
- return [[FSTNanFilter alloc] initWithField:field];
- case GCFSStructuredQuery_UnaryFilter_Operator_IsNull:
- return [[FSTNullFilter alloc] initWithField:field];
- default:
- FSTFail(@"Unrecognized UnaryFilter.operator %d", proto.op);
- }
- }
- - (GCFSStructuredQuery_FieldReference *)encodedFieldPath:(FSTFieldPath *)fieldPath {
- GCFSStructuredQuery_FieldReference *ref = [GCFSStructuredQuery_FieldReference message];
- ref.fieldPath = fieldPath.canonicalString;
- return ref;
- }
- - (GCFSStructuredQuery_FieldFilter_Operator)encodedRelationFilterOperator:
- (FSTRelationFilterOperator)filterOperator {
- switch (filterOperator) {
- case FSTRelationFilterOperatorLessThan:
- return GCFSStructuredQuery_FieldFilter_Operator_LessThan;
- case FSTRelationFilterOperatorLessThanOrEqual:
- return GCFSStructuredQuery_FieldFilter_Operator_LessThanOrEqual;
- case FSTRelationFilterOperatorEqual:
- return GCFSStructuredQuery_FieldFilter_Operator_Equal;
- case FSTRelationFilterOperatorGreaterThanOrEqual:
- return GCFSStructuredQuery_FieldFilter_Operator_GreaterThanOrEqual;
- case FSTRelationFilterOperatorGreaterThan:
- return GCFSStructuredQuery_FieldFilter_Operator_GreaterThan;
- default:
- FSTFail(@"Unhandled FSTRelationFilterOperator: %ld", (long)filterOperator);
- }
- }
- - (FSTRelationFilterOperator)decodedRelationFilterOperator:
- (GCFSStructuredQuery_FieldFilter_Operator)filterOperator {
- switch (filterOperator) {
- case GCFSStructuredQuery_FieldFilter_Operator_LessThan:
- return FSTRelationFilterOperatorLessThan;
- case GCFSStructuredQuery_FieldFilter_Operator_LessThanOrEqual:
- return FSTRelationFilterOperatorLessThanOrEqual;
- case GCFSStructuredQuery_FieldFilter_Operator_Equal:
- return FSTRelationFilterOperatorEqual;
- case GCFSStructuredQuery_FieldFilter_Operator_GreaterThanOrEqual:
- return FSTRelationFilterOperatorGreaterThanOrEqual;
- case GCFSStructuredQuery_FieldFilter_Operator_GreaterThan:
- return FSTRelationFilterOperatorGreaterThan;
- default:
- FSTFail(@"Unhandled FieldFilter.operator: %d", filterOperator);
- }
- }
- #pragma mark Property Orders
- - (NSArray<GCFSStructuredQuery_Order *> *)encodedSortOrders:(NSArray<FSTSortOrder *> *)orders {
- NSMutableArray<GCFSStructuredQuery_Order *> *protos = [NSMutableArray array];
- for (FSTSortOrder *order in orders) {
- [protos addObject:[self encodedSortOrder:order]];
- }
- return protos;
- }
- - (NSArray<FSTSortOrder *> *)decodedSortOrders:(NSArray<GCFSStructuredQuery_Order *> *)protos {
- NSMutableArray<FSTSortOrder *> *result = [NSMutableArray arrayWithCapacity:protos.count];
- for (GCFSStructuredQuery_Order *orderProto in protos) {
- [result addObject:[self decodedSortOrder:orderProto]];
- }
- return result;
- }
- - (GCFSStructuredQuery_Order *)encodedSortOrder:(FSTSortOrder *)sortOrder {
- GCFSStructuredQuery_Order *proto = [GCFSStructuredQuery_Order message];
- proto.field = [self encodedFieldPath:sortOrder.field];
- if (sortOrder.ascending) {
- proto.direction = GCFSStructuredQuery_Direction_Ascending;
- } else {
- proto.direction = GCFSStructuredQuery_Direction_Descending;
- }
- return proto;
- }
- - (FSTSortOrder *)decodedSortOrder:(GCFSStructuredQuery_Order *)proto {
- FSTFieldPath *fieldPath = [FSTFieldPath pathWithServerFormat:proto.field.fieldPath];
- BOOL ascending;
- switch (proto.direction) {
- case GCFSStructuredQuery_Direction_Ascending:
- ascending = YES;
- break;
- case GCFSStructuredQuery_Direction_Descending:
- ascending = NO;
- break;
- default:
- FSTFail(@"Unrecognized GCFSStructuredQuery_Direction %d", proto.direction);
- }
- return [FSTSortOrder sortOrderWithFieldPath:fieldPath ascending:ascending];
- }
- #pragma mark - Bounds/Cursors
- - (GCFSCursor *)encodedBound:(FSTBound *)bound {
- GCFSCursor *proto = [GCFSCursor message];
- proto.before = bound.isBefore;
- for (FSTFieldValue *fieldValue in bound.position) {
- GCFSValue *value = [self encodedFieldValue:fieldValue];
- [proto.valuesArray addObject:value];
- }
- return proto;
- }
- - (FSTBound *)decodedBound:(GCFSCursor *)proto {
- NSMutableArray<FSTFieldValue *> *indexComponents = [NSMutableArray array];
- for (GCFSValue *valueProto in proto.valuesArray) {
- FSTFieldValue *value = [self decodedFieldValue:valueProto];
- [indexComponents addObject:value];
- }
- return [FSTBound boundWithPosition:indexComponents isBefore:proto.before];
- }
- #pragma mark - FSTWatchChange <= GCFSListenResponse proto
- - (FSTWatchChange *)decodedWatchChange:(GCFSListenResponse *)watchChange {
- switch (watchChange.responseTypeOneOfCase) {
- case GCFSListenResponse_ResponseType_OneOfCase_TargetChange:
- return [self decodedTargetChangeFromWatchChange:watchChange.targetChange];
- case GCFSListenResponse_ResponseType_OneOfCase_DocumentChange:
- return [self decodedDocumentChange:watchChange.documentChange];
- case GCFSListenResponse_ResponseType_OneOfCase_DocumentDelete:
- return [self decodedDocumentDelete:watchChange.documentDelete];
- case GCFSListenResponse_ResponseType_OneOfCase_DocumentRemove:
- return [self decodedDocumentRemove:watchChange.documentRemove];
- case GCFSListenResponse_ResponseType_OneOfCase_Filter:
- return [self decodedExistenceFilterWatchChange:watchChange.filter];
- default:
- FSTFail(@"Unknown WatchChange.changeType %" PRId32, watchChange.responseTypeOneOfCase);
- }
- }
- - (FSTSnapshotVersion *)versionFromListenResponse:(GCFSListenResponse *)watchChange {
- // We have only reached a consistent snapshot for the entire stream if there is a read_time set
- // and it applies to all targets (i.e. the list of targets is empty). The backend is guaranteed to
- // send such responses.
- if (watchChange.responseTypeOneOfCase != GCFSListenResponse_ResponseType_OneOfCase_TargetChange) {
- return [FSTSnapshotVersion noVersion];
- }
- if (watchChange.targetChange.targetIdsArray.count != 0) {
- return [FSTSnapshotVersion noVersion];
- }
- return [self decodedVersion:watchChange.targetChange.readTime];
- }
- - (FSTWatchTargetChange *)decodedTargetChangeFromWatchChange:(GCFSTargetChange *)change {
- FSTWatchTargetChangeState state = [self decodedWatchTargetChangeState:change.targetChangeType];
- NSMutableArray<NSNumber *> *targetIDs =
- [NSMutableArray arrayWithCapacity:change.targetIdsArray_Count];
- [change.targetIdsArray enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
- [targetIDs addObject:@(value)];
- }];
- NSError *cause = nil;
- if (change.hasCause) {
- cause = [NSError errorWithDomain:FIRFirestoreErrorDomain
- code:change.cause.code
- userInfo:@{NSLocalizedDescriptionKey : change.cause.message}];
- }
- return [[FSTWatchTargetChange alloc] initWithState:state
- targetIDs:targetIDs
- resumeToken:change.resumeToken
- cause:cause];
- }
- - (FSTWatchTargetChangeState)decodedWatchTargetChangeState:
- (GCFSTargetChange_TargetChangeType)state {
- switch (state) {
- case GCFSTargetChange_TargetChangeType_NoChange:
- return FSTWatchTargetChangeStateNoChange;
- case GCFSTargetChange_TargetChangeType_Add:
- return FSTWatchTargetChangeStateAdded;
- case GCFSTargetChange_TargetChangeType_Remove:
- return FSTWatchTargetChangeStateRemoved;
- case GCFSTargetChange_TargetChangeType_Current:
- return FSTWatchTargetChangeStateCurrent;
- case GCFSTargetChange_TargetChangeType_Reset:
- return FSTWatchTargetChangeStateReset;
- default:
- FSTFail(@"Unexpected TargetChange.state: %" PRId32, state);
- }
- }
- - (NSArray<NSNumber *> *)decodedIntegerArray:(GPBInt32Array *)values {
- NSMutableArray<NSNumber *> *result = [NSMutableArray arrayWithCapacity:values.count];
- [values enumerateValuesWithBlock:^(int32_t value, NSUInteger idx, BOOL *stop) {
- [result addObject:@(value)];
- }];
- return result;
- }
- - (FSTDocumentWatchChange *)decodedDocumentChange:(GCFSDocumentChange *)change {
- FSTObjectValue *value = [self decodedFields:change.document.fields];
- FSTDocumentKey *key = [self decodedDocumentKey:change.document.name];
- FSTSnapshotVersion *version = [self decodedVersion:change.document.updateTime];
- FSTAssert(![version isEqual:[FSTSnapshotVersion noVersion]],
- @"Got a document change with no snapshot version");
- FSTMaybeDocument *document =
- [FSTDocument documentWithData:value key:key version:version hasLocalMutations:NO];
- NSArray<NSNumber *> *updatedTargetIds = [self decodedIntegerArray:change.targetIdsArray];
- NSArray<NSNumber *> *removedTargetIds = [self decodedIntegerArray:change.removedTargetIdsArray];
- return [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:updatedTargetIds
- removedTargetIDs:removedTargetIds
- documentKey:document.key
- document:document];
- }
- - (FSTDocumentWatchChange *)decodedDocumentDelete:(GCFSDocumentDelete *)change {
- FSTDocumentKey *key = [self decodedDocumentKey:change.document];
- // Note that version might be unset in which case we use [FSTSnapshotVersion noVersion]
- FSTSnapshotVersion *version = [self decodedVersion:change.readTime];
- FSTMaybeDocument *document = [FSTDeletedDocument documentWithKey:key version:version];
- NSArray<NSNumber *> *removedTargetIds = [self decodedIntegerArray:change.removedTargetIdsArray];
- return [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[]
- removedTargetIDs:removedTargetIds
- documentKey:document.key
- document:document];
- }
- - (FSTDocumentWatchChange *)decodedDocumentRemove:(GCFSDocumentRemove *)change {
- FSTDocumentKey *key = [self decodedDocumentKey:change.document];
- NSArray<NSNumber *> *removedTargetIds = [self decodedIntegerArray:change.removedTargetIdsArray];
- return [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[]
- removedTargetIDs:removedTargetIds
- documentKey:key
- document:nil];
- }
- - (FSTExistenceFilterWatchChange *)decodedExistenceFilterWatchChange:(GCFSExistenceFilter *)filter {
- // TODO(dimond): implement existence filter parsing
- FSTExistenceFilter *existenceFilter = [FSTExistenceFilter filterWithCount:filter.count];
- FSTTargetID targetID = filter.targetId;
- return [FSTExistenceFilterWatchChange changeWithFilter:existenceFilter targetID:targetID];
- }
- @end
- NS_ASSUME_NONNULL_END
|