diff --git a/packages/firestore/__tests__/firestore.test.ts b/packages/firestore/__tests__/firestore.test.ts index ae4a74731a..c20904faf7 100644 --- a/packages/firestore/__tests__/firestore.test.ts +++ b/packages/firestore/__tests__/firestore.test.ts @@ -1314,6 +1314,90 @@ describe('Firestore', function () { ); }); + it('FirestoreDocumentSnapshot.data() respects SnapshotOptions.serverTimestamps', function () { + const firestore = getFirestore(); + const snapshot = new FirestoreDocumentSnapshot( + // @ts-expect-error calling a private constructor directly which expects FirestoreInternal type + firestore, + { + data: { createdAt: [3] }, + dataEstimate: { createdAt: [13, [123, 456]] }, + dataPrevious: { createdAt: [13, [42, 0]] }, + dataNone: { createdAt: [3] }, + metadata: [false, true], + path: 'foo/bar', + exists: true, + }, + null, + ); + + expect(snapshot.data()).toEqual({ createdAt: null }); + expect(snapshot.data({ serverTimestamps: 'estimate' })?.createdAt).toBeInstanceOf(Timestamp); + expect(snapshot.data({ serverTimestamps: 'estimate' })?.createdAt).toMatchObject({ + seconds: 123, + nanoseconds: 456, + }); + expect(snapshot.data({ serverTimestamps: 'previous' })?.createdAt).toMatchObject({ + seconds: 42, + nanoseconds: 0, + }); + expect(snapshot.data({ serverTimestamps: 'none' })).toEqual({ createdAt: null }); + }); + + it('FirestoreDocumentSnapshot.get() respects SnapshotOptions.serverTimestamps', function () { + const firestore = getFirestore(); + const snapshot = new FirestoreDocumentSnapshot( + // @ts-expect-error calling a private constructor directly which expects FirestoreInternal type + firestore, + { + data: { nested: [16, { createdAt: [3] }] }, + dataEstimate: { nested: [16, { createdAt: [13, [123, 456]] }] }, + dataPrevious: { nested: [16, { createdAt: [13, [42, 0]] }] }, + dataNone: { nested: [16, { createdAt: [3] }] }, + metadata: [false, true], + path: 'foo/bar', + exists: true, + }, + null, + ); + + expect(snapshot.get('nested.createdAt')).toBeNull(); + expect(snapshot.get('nested.createdAt', { serverTimestamps: 'estimate' })).toMatchObject({ + seconds: 123, + nanoseconds: 456, + }); + expect(snapshot.get('nested.createdAt', { serverTimestamps: 'previous' })).toMatchObject({ + seconds: 42, + nanoseconds: 0, + }); + expect(snapshot.get('nested.createdAt', { serverTimestamps: 'none' })).toBeNull(); + }); + + it('FirestoreDocumentSnapshot.data() passes SnapshotOptions through converter snapshots', function () { + const firestore = getFirestore(); + const snapshot = new FirestoreDocumentSnapshot( + // @ts-expect-error calling a private constructor directly which expects FirestoreInternal type + firestore, + { + data: { createdAt: [3] }, + dataEstimate: { createdAt: [13, [123, 456]] }, + metadata: [false, true], + path: 'foo/bar', + exists: true, + }, + { + toFirestore: data => data, + fromFirestore: converterSnapshot => + converterSnapshot.data({ serverTimestamps: 'estimate' }), + }, + ); + + expect(snapshot.data()?.createdAt).toMatchObject({ + seconds: 123, + nanoseconds: 456, + }); + }); + describe('FieldValue', function () { it('FieldValue.delete()', function () { const fieldValue = firestore.FieldValue; diff --git a/packages/firestore/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreSerialize.java b/packages/firestore/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreSerialize.java index 75a8593981..485b9e3630 100644 --- a/packages/firestore/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreSerialize.java +++ b/packages/firestore/android/src/reactnative/java/io/invertase/firebase/firestore/ReactNativeFirebaseFirestoreSerialize.java @@ -78,6 +78,9 @@ public class ReactNativeFirebaseFirestoreSerialize { // Keys private static final String TYPE = "type"; private static final String KEY_DATA = "data"; + private static final String KEY_DATA_ESTIMATE = "dataEstimate"; + private static final String KEY_DATA_PREVIOUS = "dataPrevious"; + private static final String KEY_DATA_NONE = "dataNone"; private static final String KEY_PATH = "path"; private static final String KEY_EXISTS = "exists"; private static final String KEY_META = "metadata"; @@ -118,15 +121,39 @@ static WritableMap snapshotToWritableMap( getServerTimestampBehavior(appName, databaseId); if (documentSnapshot.exists()) { - if (documentSnapshot.getData(timestampBehavior) != null) { - documentMap.putMap( - KEY_DATA, objectMapToWritable(documentSnapshot.getData(timestampBehavior))); + Map data = documentSnapshot.getData(timestampBehavior); + putSnapshotData(documentMap, KEY_DATA, data); + + if (timestampBehavior != DocumentSnapshot.ServerTimestampBehavior.ESTIMATE) { + putSnapshotData( + documentMap, + KEY_DATA_ESTIMATE, + documentSnapshot.getData(DocumentSnapshot.ServerTimestampBehavior.ESTIMATE)); + } + if (timestampBehavior != DocumentSnapshot.ServerTimestampBehavior.PREVIOUS) { + putSnapshotData( + documentMap, + KEY_DATA_PREVIOUS, + documentSnapshot.getData(DocumentSnapshot.ServerTimestampBehavior.PREVIOUS)); + } + if (timestampBehavior != DocumentSnapshot.ServerTimestampBehavior.NONE) { + putSnapshotData( + documentMap, + KEY_DATA_NONE, + documentSnapshot.getData(DocumentSnapshot.ServerTimestampBehavior.NONE)); } } return documentMap; } + private static void putSnapshotData( + WritableMap documentMap, String key, @Nullable Map data) { + if (data != null) { + documentMap.putMap(key, objectMapToWritable(data)); + } + } + /** * Convert a Firestore QuerySnapshot instance to a RN serializable WritableMap type map * diff --git a/packages/firestore/consumer-type-test.ts b/packages/firestore/consumer-type-test.ts index f8bbcbb209..36c897e745 100644 --- a/packages/firestore/consumer-type-test.ts +++ b/packages/firestore/consumer-type-test.ts @@ -348,9 +348,12 @@ nsQuery.get().then((snap: FirebaseFirestoreTypes.QuerySnapshot) => { nsDocRef.get().then((snap: FirebaseFirestoreTypes.DocumentSnapshot) => { if (snap.exists()) { const d = snap.data(); + const estimate = snap.data({ serverTimestamps: 'estimate' }); void d; + void estimate; } void snap.get('field'); + void snap.get('field', { serverTimestamps: 'previous' }); void snap.metadata.isEqual(snap.metadata); }); @@ -638,6 +641,8 @@ getAggregateFromServer(modQuery1, aggSpec).then( // ----- getDoc, getDocFromCache, getDocFromServer ----- getDoc(modDoc).then(snap => snap.data()); +getDoc(modDoc).then(snap => snap.data({ serverTimestamps: 'estimate' })); +getDoc(modDoc).then(snap => snap.get('field', { serverTimestamps: 'previous' })); getDocFromCache(modDoc).then(snap => snap.data()); getDocFromServer(modDoc).then(snap => snap.data()); diff --git a/packages/firestore/e2e/withConverter.e2e.js b/packages/firestore/e2e/withConverter.e2e.js index 9658e6dd8c..2b83da96a4 100644 --- a/packages/firestore/e2e/withConverter.e2e.js +++ b/packages/firestore/e2e/withConverter.e2e.js @@ -25,12 +25,14 @@ const { addDoc, setDoc, getDoc, + onSnapshot, query, where, getDocs, writeBatch, increment, - initializeFirestore, + serverTimestamp, + Timestamp, } = firestoreModular; // Used for testing the FirestoreDataConverter. @@ -99,9 +101,12 @@ function withTestDb(fn) { async function withModifiedUndefinedPropertiesTestDb(fn) { const db = getFirestore(); const previousValue = db._settings.ignoreUndefinedProperties; - initializeFirestore(db.app, { ignoreUndefinedProperties: false }); - await fn(db); - initializeFirestore(db.app, { ignoreUndefinedProperties: previousValue }); + db._settings.ignoreUndefinedProperties = false; + try { + await fn(db); + } finally { + db._settings.ignoreUndefinedProperties = previousValue; + } } function withTestCollection(fn) { @@ -590,6 +595,70 @@ describe('firestore.withConverter', function () { }); }); + it('passes data() serverTimestamps options through converter snapshots', async function () { + if (Platform.other) { + // macOS uses the Firestore web lite path, which does not support snapshot listeners. + return; + } + + const timestampCases = [ + { serverTimestamps: 'estimate', expectTimestamp: true }, + { serverTimestamps: 'previous', expectTimestamp: false }, + { serverTimestamps: 'none', expectTimestamp: false }, + ]; + + for (const { serverTimestamps, expectTimestamp } of timestampCases) { + const timestampConverter = { + toFirestore() { + return { + createdAt: serverTimestamp(), + updatedAt: serverTimestamp(), + }; + }, + fromFirestore(snapshot) { + return snapshot.data({ serverTimestamps }); + }, + }; + + await withTestCollection(async coll => { + const ref = doc(coll).withConverter(timestampConverter); + await new Promise((resolve, reject) => { + const unsubscribe = onSnapshot( + ref, + { includeMetadataChanges: true }, + snapshot => { + try { + if (!snapshot.exists() || !snapshot.metadata.hasPendingWrites) { + return; + } + + const data = snapshot.data(); + if (expectTimestamp) { + data.createdAt.should.be.an.instanceOf(Timestamp); + data.updatedAt.should.be.an.instanceOf(Timestamp); + } else { + should.equal(data.createdAt, null); + should.equal(data.updatedAt, null); + } + unsubscribe(); + resolve(); + } catch (error) { + unsubscribe(); + reject(error); + } + }, + reject, + ); + + setDoc(ref, {}).catch(error => { + unsubscribe(); + reject(error); + }); + }); + }); + } + }); + it('supports partials with merge', async function () { return withTestCollection(async coll => { const ref = doc(coll, 'post').withConverter(postConverterMerge); diff --git a/packages/firestore/ios/RNFBFirestore/RNFBFirestoreSerialize.m b/packages/firestore/ios/RNFBFirestore/RNFBFirestoreSerialize.m index 70dd25e085..26f97108e8 100644 --- a/packages/firestore/ios/RNFBFirestore/RNFBFirestoreSerialize.m +++ b/packages/firestore/ios/RNFBFirestore/RNFBFirestoreSerialize.m @@ -26,6 +26,9 @@ @implementation RNFBFirestoreSerialize static NSString *const KEY_PATH = @"path"; static NSString *const KEY_DATA = @"data"; +static NSString *const KEY_DATA_ESTIMATE = @"dataEstimate"; +static NSString *const KEY_DATA_PREVIOUS = @"dataPrevious"; +static NSString *const KEY_DATA_NONE = @"dataNone"; static NSString *const KEY_EXISTS = @"exists"; static NSString *const KEY_CHANGES = @"changes"; static NSString *const KEY_METADATA = @"metadata"; @@ -188,6 +191,14 @@ + (NSDictionary *)documentChangeToDictionary:(FIRDocumentChange *)documentChange return changeMap; } ++ (void)putSnapshotData:(NSMutableDictionary *)documentMap + key:(NSString *)key + data:(NSDictionary *)data { + if (data != nil) { + documentMap[key] = [self serializeDictionary:data]; + } +} + // Native DocumentSnapshot -> NSDictionary (for JS) + (NSDictionary *)documentSnapshotToDictionary:(FIRDocumentSnapshot *)snapshot firestoreKey:(NSString *)firestoreKey { @@ -218,7 +229,25 @@ + (NSDictionary *)documentSnapshotToDictionary:(FIRDocumentSnapshot *)snapshot } NSDictionary *data = [snapshot dataWithServerTimestampBehavior:serverTimestampBehavior]; - documentMap[KEY_DATA] = [self serializeDictionary:data]; + [self putSnapshotData:documentMap key:KEY_DATA data:data]; + + if (serverTimestampBehavior != FIRServerTimestampBehaviorEstimate) { + NSDictionary *estimateData = + [snapshot dataWithServerTimestampBehavior:FIRServerTimestampBehaviorEstimate]; + [self putSnapshotData:documentMap key:KEY_DATA_ESTIMATE data:estimateData]; + } + + if (serverTimestampBehavior != FIRServerTimestampBehaviorPrevious) { + NSDictionary *previousData = + [snapshot dataWithServerTimestampBehavior:FIRServerTimestampBehaviorPrevious]; + [self putSnapshotData:documentMap key:KEY_DATA_PREVIOUS data:previousData]; + } + + if (serverTimestampBehavior != FIRServerTimestampBehaviorNone) { + NSDictionary *noneData = + [snapshot dataWithServerTimestampBehavior:FIRServerTimestampBehaviorNone]; + [self putSnapshotData:documentMap key:KEY_DATA_NONE data:noneData]; + } } return documentMap; diff --git a/packages/firestore/lib/FirestoreDocumentChange.ts b/packages/firestore/lib/FirestoreDocumentChange.ts index 3a3a4304bc..556329b583 100644 --- a/packages/firestore/lib/FirestoreDocumentChange.ts +++ b/packages/firestore/lib/FirestoreDocumentChange.ts @@ -28,7 +28,15 @@ const TYPE_MAP: Record = { export interface DocumentChangeNativeData { type: string; - doc: { path: string; data?: unknown; metadata?: [boolean, boolean]; exists?: boolean }; + doc: { + path: string; + data?: unknown; + dataEstimate?: unknown; + dataPrevious?: unknown; + dataNone?: unknown; + metadata?: [boolean, boolean]; + exists?: boolean; + }; ni: number; oi: number; isMetadataChange?: boolean; diff --git a/packages/firestore/lib/FirestoreDocumentReference.ts b/packages/firestore/lib/FirestoreDocumentReference.ts index 82197dd96b..5a4b0ba81b 100644 --- a/packages/firestore/lib/FirestoreDocumentReference.ts +++ b/packages/firestore/lib/FirestoreDocumentReference.ts @@ -30,6 +30,7 @@ import { parseUpdateArgs, validateWithConverter, applyFirestoreDataConverter, + throwIfLiteSdkSnapshotListenerUnsupported, } from './utils'; import { buildNativeMap, provideDocumentReferenceClass } from './utils/serialize'; @@ -201,6 +202,8 @@ export default class DocumentReference< } onSnapshot(...args: unknown[]): () => void { + throwIfLiteSdkSnapshotListenerUnsupported(); + let snapshotListenOptions: { includeMetadataChanges?: boolean; source?: ListenSource }; let callback: ( snapshot: DocumentSnapshot | null, diff --git a/packages/firestore/lib/FirestoreDocumentSnapshot.ts b/packages/firestore/lib/FirestoreDocumentSnapshot.ts index 6f1d558a99..376d0d62bf 100644 --- a/packages/firestore/lib/FirestoreDocumentSnapshot.ts +++ b/packages/firestore/lib/FirestoreDocumentSnapshot.ts @@ -38,6 +38,9 @@ import type { export interface DocumentSnapshotNativeData { path: string; data?: unknown; + dataEstimate?: unknown; + dataPrevious?: unknown; + dataNone?: unknown; metadata?: [boolean, boolean]; exists?: boolean; } @@ -49,6 +52,9 @@ export default class DocumentSnapshot< _firestore: FirestoreInternal; _nativeData: DocumentSnapshotNativeData; _data: Record | undefined; + _dataEstimate: Record | undefined; + _dataPrevious: Record | undefined; + _dataNone: Record | undefined; _metadata: SnapshotMetadata; _ref: DocumentReference; _exists: boolean; @@ -63,6 +69,18 @@ export default class DocumentSnapshot< this._nativeData = nativeData; this._converter = converter; this._data = parseNativeMap(firestore, nativeData.data as Record | undefined); + this._dataEstimate = parseNativeMap( + firestore, + nativeData.dataEstimate as Record | undefined, + ); + this._dataPrevious = parseNativeMap( + firestore, + nativeData.dataPrevious as Record | undefined, + ); + this._dataNone = parseNativeMap( + firestore, + nativeData.dataNone as Record | undefined, + ); this._metadata = new SnapshotMetadata(nativeData.metadata ?? [false, false]); this._ref = new DocumentReference( firestore, @@ -72,6 +90,20 @@ export default class DocumentSnapshot< this._exists = nativeData.exists ?? false; } + _dataForOptions(options?: SnapshotOptions): Record | undefined { + // Older native payloads only include `data`; fall back to it if an option-specific map is absent. + switch (options?.serverTimestamps) { + case 'estimate': + return this._dataEstimate ?? this._data; + case 'previous': + return this._dataPrevious ?? this._data; + case 'none': + return this._dataNone ?? this._data; + default: + return this._data; + } + } + get id(): string { return this._ref.id; } @@ -105,10 +137,10 @@ export default class DocumentSnapshot< ); } } - return this._data as AppModelType | undefined; + return this._dataForOptions(options) as AppModelType | undefined; } - get(fieldPath: string | FieldPath, _options?: SnapshotOptions): DocumentFieldValueInternal { + get(fieldPath: string | FieldPath, options?: SnapshotOptions): DocumentFieldValueInternal { if (!isString(fieldPath) && !(fieldPath instanceof FieldPath)) { throw new Error( "firebase.firestore() DocumentSnapshot.get(*) 'fieldPath' expected type string or FieldPath.", @@ -129,7 +161,10 @@ export default class DocumentSnapshot< path = fieldPath; } - return extractFieldPathData(this._data, path._segments) as DocumentFieldValueInternal; + return extractFieldPathData( + this._dataForOptions(options), + path._segments, + ) as DocumentFieldValueInternal; } isEqual(other: DocumentSnapshot): boolean { diff --git a/packages/firestore/lib/FirestoreQuery.ts b/packages/firestore/lib/FirestoreQuery.ts index 5050588084..99ea512f90 100644 --- a/packages/firestore/lib/FirestoreQuery.ts +++ b/packages/firestore/lib/FirestoreQuery.ts @@ -31,7 +31,11 @@ import FieldPath, { fromDotSeparatedString } from './FieldPath'; import { _Filter, generateFilters } from './FirestoreFilter'; import QueryModifiers from './FirestoreQueryModifiers'; import QuerySnapshot, { type QuerySnapshotNativeData } from './FirestoreQuerySnapshot'; -import { parseSnapshotArgs, validateWithConverter } from './utils'; +import { + parseSnapshotArgs, + throwIfLiteSdkSnapshotListenerUnsupported, + validateWithConverter, +} from './utils'; import type FirestorePath from './FirestorePath'; import type { DocumentData, FirestoreDataConverter, ListenSource } from './types/firestore'; @@ -349,6 +353,8 @@ export default class Query< } onSnapshot(...args: unknown[]): () => void { + throwIfLiteSdkSnapshotListenerUnsupported(); + let snapshotListenOptions: { includeMetadataChanges?: boolean; source?: ListenSource }; let callback: ( snapshot: QuerySnapshot | null, diff --git a/packages/firestore/lib/FirestoreQuerySnapshot.ts b/packages/firestore/lib/FirestoreQuerySnapshot.ts index 16e5f2769f..0199053f6d 100644 --- a/packages/firestore/lib/FirestoreQuerySnapshot.ts +++ b/packages/firestore/lib/FirestoreQuerySnapshot.ts @@ -35,7 +35,15 @@ export interface QuerySnapshotNativeData { excludesMetadataChanges?: boolean; changes: Array<{ type: string; - doc: { path: string; data?: unknown; metadata?: [boolean, boolean]; exists?: boolean }; + doc: { + path: string; + data?: unknown; + dataEstimate?: unknown; + dataPrevious?: unknown; + dataNone?: unknown; + metadata?: [boolean, boolean]; + exists?: boolean; + }; ni: number; oi: number; isMetadataChange?: boolean; @@ -43,6 +51,9 @@ export interface QuerySnapshotNativeData { documents: Array<{ path: string; data?: unknown; + dataEstimate?: unknown; + dataPrevious?: unknown; + dataNone?: unknown; metadata?: [boolean, boolean]; exists?: boolean; }>; diff --git a/packages/firestore/lib/types/internal.ts b/packages/firestore/lib/types/internal.ts index 46dde1f175..e67304210c 100644 --- a/packages/firestore/lib/types/internal.ts +++ b/packages/firestore/lib/types/internal.ts @@ -386,6 +386,9 @@ export interface FirestoreDocumentSnapshotDataInternal { path: string; exists: boolean; data?: Record; + dataEstimate?: Record; + dataPrevious?: Record; + dataNone?: Record; metadata: [boolean, boolean]; } diff --git a/packages/firestore/lib/types/namespaced.ts b/packages/firestore/lib/types/namespaced.ts index 4aaac6f448..6cd4b04339 100644 --- a/packages/firestore/lib/types/namespaced.ts +++ b/packages/firestore/lib/types/namespaced.ts @@ -642,7 +642,7 @@ export namespace FirebaseFirestoreTypes { * console.log('User', user.data()); * ``` */ - data(): AppModelType | undefined; + data(options?: SnapshotOptions): AppModelType | undefined; /** * Retrieves the field specified by fieldPath. Returns undefined if the document or field doesn't exist. @@ -659,6 +659,7 @@ export namespace FirebaseFirestoreTypes { */ get( fieldPath: keyof AppModelType | string | FieldPath, + options?: SnapshotOptions, ): fieldType; /** @@ -708,7 +709,7 @@ export namespace FirebaseFirestoreTypes { * } * ``` */ - data(): AppModelType; + data(options?: SnapshotOptions): AppModelType; } /** diff --git a/packages/firestore/lib/utils/index.ts b/packages/firestore/lib/utils/index.ts index 7d2d2a7941..90e50ccbe7 100644 --- a/packages/firestore/lib/utils/index.ts +++ b/packages/firestore/lib/utils/index.ts @@ -20,10 +20,12 @@ import { isArray, isBoolean, isFunction, + isOther, isObject, isString, isUndefined, } from '@react-native-firebase/app/dist/module/common'; +import NativeFirebaseError from '@react-native-firebase/app/dist/module/internal/NativeFirebaseError'; import type { ConverterWithOptionalMethodsInternal, ConverterWithOptionalToFirestoreInternal, @@ -242,6 +244,20 @@ export function parseSnapshotArgs(args: unknown[]): ParseSnapshotArgsResult { return { snapshotListenOptions, callback, onNext, onError }; } +export function throwIfLiteSdkSnapshotListenerUnsupported(): void { + if (!isOther) { + return; + } + + throw NativeFirebaseError.fromEvent( + { + code: 'unsupported', + message: 'Not supported in the lite SDK.', + }, + 'firestore', + ); +} + export function validateWithConverter(converter: unknown): void { if (isUndefined(converter) || !isObject(converter)) { throw new Error('expected an object value.'); diff --git a/packages/firestore/lib/web/RNFBFirestoreModule.ts b/packages/firestore/lib/web/RNFBFirestoreModule.ts index 8b7f6c10a1..fd85bf828c 100644 --- a/packages/firestore/lib/web/RNFBFirestoreModule.ts +++ b/packages/firestore/lib/web/RNFBFirestoreModule.ts @@ -65,13 +65,18 @@ function rejectWithCodeAndMessage(code: string, message: string): Promise type DocumentSnapshotLike = { exists(): boolean; ref: { path: string }; - data(): Record | undefined; + data( + options?: { serverTimestamps?: 'estimate' | 'previous' | 'none' }, + ): Record | undefined; }; function documentSnapshotToObject(snapshot: DocumentSnapshotLike): { path: string; exists: boolean; data?: unknown; + dataEstimate?: unknown; + dataPrevious?: unknown; + dataNone?: unknown; metadata: [boolean, boolean]; } { const exists = snapshot.exists(); @@ -79,6 +84,9 @@ function documentSnapshotToObject(snapshot: DocumentSnapshotLike): { path: string; exists: boolean; data?: unknown; + dataEstimate?: unknown; + dataPrevious?: unknown; + dataNone?: unknown; metadata: [boolean, boolean]; } = { metadata: [false, false], @@ -87,6 +95,13 @@ function documentSnapshotToObject(snapshot: DocumentSnapshotLike): { }; if (exists) { out.data = objectToWriteable(snapshot.data() ?? {}); + out.dataEstimate = objectToWriteable( + snapshot.data({ serverTimestamps: 'estimate' }) ?? {}, + ); + out.dataPrevious = objectToWriteable( + snapshot.data({ serverTimestamps: 'previous' }) ?? {}, + ); + out.dataNone = objectToWriteable(snapshot.data({ serverTimestamps: 'none' }) ?? {}); } return out; } diff --git a/packages/firestore/type-test.ts b/packages/firestore/type-test.ts index 6f6d06558b..a4b636c874 100644 --- a/packages/firestore/type-test.ts +++ b/packages/firestore/type-test.ts @@ -342,9 +342,12 @@ nsQuery.get().then((snap: FirebaseFirestoreTypes.QuerySnapshot) => { nsDocRef.get().then((snap: FirebaseFirestoreTypes.DocumentSnapshot) => { if (snap.exists()) { const d = snap.data(); + const estimate = snap.data({ serverTimestamps: 'estimate' }); console.log(d); + console.log(estimate); } console.log(snap.get('field')); + console.log(snap.get('field', { serverTimestamps: 'previous' })); console.log(snap.metadata.isEqual(snap.metadata)); }); @@ -642,6 +645,8 @@ getAggregateFromServer(modQuery1, aggSpec).then( // ----- getDoc, getDocFromCache, getDocFromServer ----- getDoc(modDoc).then(snap => snap.data()); +getDoc(modDoc).then(snap => snap.data({ serverTimestamps: 'estimate' })); +getDoc(modDoc).then(snap => snap.get('field', { serverTimestamps: 'previous' })); getDocFromCache(modDoc).then(snap => snap.data()); getDocFromServer(modDoc).then(snap => snap.data()); diff --git a/tests/ios/Podfile.lock b/tests/ios/Podfile.lock index dc06c43808..3009bc561f 100644 --- a/tests/ios/Podfile.lock +++ b/tests/ios/Podfile.lock @@ -2539,85 +2539,85 @@ SPEC CHECKSUMS: nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 - RCT-Folly: 46b84c7990d4a373ccef77134c23c763ba5cf3a0 + RCT-Folly: d533c1d21249a85e67a4be97f4d793665de0222e RCTDeprecation: cf39863b43871c2031050605fb884019b6193910 RCTRequired: 8fdd66f4a97f352b66f38cfef13fc11b12d2c884 RCTTypeSafety: c9c9e64389bc545fc137030615b387ef1654dcee React: 14a80ea4f13387cfdaa4250b46fbfe19754c220c React-callinvoker: fed1dad5d6cf992c7b4b5fdbf1bf67fe2e8fb6c5 - React-Core: 3c803e7f3be6fa68e3dabcac283a5a5f87340a60 - React-CoreModules: 17ac445c8d86fdbb1dbf149e05d6c07ed5cc0080 - React-cxxreact: 425bf54ebddab5d65f76e06ac531d35c9be0e671 + React-Core: f703e7a56fcedc3e959b8b7899995e57fd58539a + React-CoreModules: 945ed8a2827720af6852b97c8ddd2ac2ceb04157 + React-cxxreact: 26c744a099a29f2f995f9bb4b755d8ecbf7c5392 React-debug: a665bbe67eb786b7a7a815ce1b7212b3f9fa962c - React-defaultsnativemodule: 8f0bea8d0d3434aa98957302f2a8741600781edd - React-domnativemodule: 889d261cc9691b91063a1c1d89d408e19903923d - React-Fabric: af1700943aed47ada6c0cc23da4e33cea8d595e5 - React-FabricComponents: b9cdcb82a8613a476a525289801c9c51fcf3a50c - React-FabricImage: f14bd75c4d09143a657383bcee73186907ee0339 + React-defaultsnativemodule: 7e4a2c2b13ec2943f2f3b8adec32130443909de6 + React-domnativemodule: dffaa482180243bd1e2b7fba329fd4adc12a2445 + React-Fabric: 37160a25edcb01c72b66562a2ad200667609412d + React-FabricComponents: 8c69a05e36b7f73341e95011a5e9571e92c67cf2 + React-FabricImage: c8870f298331e4c2cf9c3114265aef27d9bb3ba2 React-featureflags: 55800b546a28b63a8a0f419e1a45871d43523d32 - React-featureflagsnativemodule: b200bca78e00f9e5c7cd5a1c9f2957223fcfa33a - React-graphics: 464698dcbb6e57ae828e8b1bd666a22176dc2790 - React-hermes: 12bd452081482a5c40766671b06bf80c15f4d454 - React-idlecallbacksnativemodule: dc10ee2e5ba5ae7ad56aa093aedda582345bce16 - React-ImageManager: 08592583c7737aec2b2c2a12e7c4f0ad763ae5c4 - React-jserrorhandler: eede06f57f67c8d3978ff1a9c78107aea5cbdf45 - React-jsi: ff84dfaf45d058e317db844f174292cf61ac1010 - React-jsiexecutor: 96e4df91d8cb172941d71cfb9179fc27196cdc6b - React-jsinspector: 7a7e033c64cc40e6a1d6daf7fad4827bc1e7dd12 - React-jsinspectortracing: 77f4d18502af6e7599e77390b35f04f02814f7ce - React-jsitracing: 0608ea7ee711370d42fdd2b4a474dbced68dd275 - React-logger: 8d00d3d794041a77bd890158353361e2709b04c1 - React-Mapbuffer: 45ca4d30efe99834a8cd8d98f803c94766db534f - React-microtasksnativemodule: b5901a0b15f92ce0666ee5131eb8ab646f1d5a27 - React-NativeModulesApple: 7a9ec626a1852d444d0e699b016dc55a064b7569 - React-perflogger: d06f0fd0727356651a5535f6d717130187aeb667 - React-performancetimeline: c397114f2c025aa73412a9f21e021b08127fe820 + React-featureflagsnativemodule: a0ea334fdd3342a2e4dc05085c3e7653e16839d3 + React-graphics: 705f31502711f1214ef58274162a6d61ec8c1191 + React-hermes: 1ee5a6b835bc939b7aba500cb716a1095b3e0959 + React-idlecallbacksnativemodule: 74d091304aad1ceb0c5b86e5dec14372fcdc0f34 + React-ImageManager: 6b2a95469d9a126f14410bbe10fb7378d37ed0e0 + React-jserrorhandler: 2643140639cbf047bf45f5c1a3ea95b04d748582 + React-jsi: 0810ff01e7f73a1cfba83d176e21c29302f6f3aa + React-jsiexecutor: 70f6865d195eba534f39ac9504a9aa178e5389f7 + React-jsinspector: 6d768dfb189027f7ff2161be31ccd69215426ded + React-jsinspectortracing: a6a70eb5c9d767d99391d0373330a7239fb6f9d0 + React-jsitracing: 69280997c7a80ac0af62b95f01a241d68581fb52 + React-logger: e6c3c1b55c18cc1b945b647ff9ada53e0e710b50 + React-Mapbuffer: 57bea44149376ecf1241dd9f02a6222adab43e57 + React-microtasksnativemodule: 2739fc8a8cb99486820c933ce560791c93be5337 + React-NativeModulesApple: 4849912ee050e6ae346f5789bd631570580d8b84 + React-perflogger: 069d41f741187be92ed46c6ac67add03b99f3166 + React-performancetimeline: eda794c5007eb6834e13bc6026a879b5f6d49c74 React-RCTActionSheet: a078d5008632fed31b0024c420ee02e612b317d5 - React-RCTAnimation: b197cc109a896c6ce23981e02e317cfc055f6fda - React-RCTAppDelegate: f7f1d7362256b7c142d9ab49f980df488101f869 - React-RCTBlob: d418d0af4ac208354933ffce569cf62144440d97 - React-RCTFabric: 8cdcde7157a22aac04dfeb579dfc3a1141446846 - React-RCTFBReactNativeSpec: c3a78cb9f2a98146443f1b732a4f21b2ce736abd - React-RCTImage: 7a3d9d67161c714fa4d9b93820da39a266d0f1ff - React-RCTLinking: f860b917500cd3974235a48d5b199a74a4ed6c26 - React-RCTNetwork: 6a984ab1b5a81d17a2df6cc02c24b249fb055deb - React-RCTSettings: e9a39068d8b60d78a5271dcb68f6ea7f59569cb2 - React-RCTText: 44457242238664a5ad69f06ec7a5f273a6967711 - React-RCTVibration: f448ad875c60b2ddc5fc5b06d3f5e2dfc3f18c09 + React-RCTAnimation: 82e31d191af4175e0c2df5bdac2c8569a5f3ab54 + React-RCTAppDelegate: a5c1ff79f5987462b4f62b27387459ba84012439 + React-RCTBlob: 605c283c68fee9e095206fefbfe69d0d23df1a00 + React-RCTFabric: 56b946204edb5d563885b3b045bdacbb387b27e7 + React-RCTFBReactNativeSpec: 8392ef66ad156cfa848546859bbff3b5e8a09458 + React-RCTImage: 10fad63f1bb8adbd519c4c2ef6bec3c0d95fdd32 + React-RCTLinking: 3843288a44dc33ec083c843f3ff31dd7d96ece41 + React-RCTNetwork: f237299bda8bbd56c4d01d2825110e40b75c438a + React-RCTSettings: c24ce1ee96c9b001ff5059ddd53412a20b7d5e71 + React-RCTText: d97cfb9c89b06de9530577dd43f178c47ea07853 + React-RCTVibration: 2fcefee071a4f0d416e4368416bb073ea6893451 React-rendererconsistency: c9f31b6d55877c5d49d25d69270b89f9cb208e26 - React-rendererdebug: 61786b94758c96379bb4bf94978e1cd13c75bf54 + React-rendererdebug: 1de2a6279dbd8a149522ec8ac683f80a2b144995 React-rncore: 90e637179a4ce46643d445a9ef16f53af02a8d25 - React-RuntimeApple: 3df87718b4a8e438b4a02d1d7d47677dfcab35a5 - React-RuntimeCore: e188aa1b0fe0450f3a4e6098f2bb8d4b27c147cf + React-RuntimeApple: 713b7c24b3abed07fa39766b35deaabd679ba48e + React-RuntimeCore: 236d704919077fd3393a26fd0ecbaecc081ec94f React-runtimeexecutor: 2de0d537fc6d5b4a7074587b4459ea331c7e5715 - React-RuntimeHermes: 5aa429111da4c0a62d94b37ad5b5144deb8f49d0 - React-runtimescheduler: c3738ed7f0ba2e51b1612ec8d6cbe104c2b9b59a + React-RuntimeHermes: 3e87ad8c5160c976addacd679774a5e4fdb3c4b4 + React-runtimescheduler: bafaf0af0f68bd761d63ff4de3bf13e391820f79 React-timing: 7ad7dc61dfc93ceb4ec2b3e6d1a6ad9ad3652fe0 - React-utils: d6a3bec920c7fa710e8fb5b7c28d81fe54be8c75 - ReactAppDependencyProvider: 5df090fa3cbfc923c6bd0595b64d5ef6d89f7134 - ReactCodegen: 0c213020a601c6adda74f8826629bff9c6c408d3 - ReactCommon: c18c9308463e582898abcec12ffbd2df2b7e8fdd + React-utils: cf358d29b6802cca3d1bec20a939f2f5d50d40ba + ReactAppDependencyProvider: ad88c80e06f29900f2e6f9ccf1d4cb0bfc3e1bbc + ReactCodegen: 69c7aec61821e1860aaaf959189218ecca40e811 + ReactCommon: 0df51c56138bd6f6bfd027e91eda0e3a4e3233eb RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba - RNCAsyncStorage: 6a8127b6987dc9fbce778669b252b14c8355c7ce - RNDeviceInfo: 4c852998208b60dc192ae3529e5867817719ad1e - RNFBAnalytics: 474520619039cb536584ca4cf41264d8199d0d9c - RNFBApp: b26b3c94412f6a1302df6c92ea1d43c0d72f1c38 - RNFBAppCheck: 94fe77ecd93b2dbded08a2f93421512b160fbc98 - RNFBAppDistribution: 89aeb28feab88f8725cee5017a5a7068b13fccc4 - RNFBAuth: d591c967280010ee687d03962a6a107118ef48e0 - RNFBCrashlytics: 8cde40b13db4dc910f2b9538691895e9dfc7d984 - RNFBDatabase: 30c01b8cd059ddd3a93d101196ca00188e1205da - RNFBFirestore: f38f6f5f94bf4c4b0e63f1577428378c65ffb9c0 - RNFBFunctions: a5fabc86f4fa6883dcc015d2c40519b3ad0b526e - RNFBInAppMessaging: b4ecb102aa1a261edd3b01ea8901459b6ee48c38 - RNFBInstallations: e25a80acd3e4d00dc0ae8b42b469df3e9923ba11 - RNFBMessaging: 52eadb0d130cc348b449d805a6a290a386ca1c6d - RNFBML: c7d77b4bdc3ecfb786f5f986b994d4d932808f0b - RNFBPerf: 7e95aebe466e18744f08ef1fee9f681125e32264 - RNFBRemoteConfig: 8ba2602b0568abe67ed35ef9f89a4fb4e0af7903 - RNFBStorage: 37a24dbcba9cc04c3f8a93942a7baecd2bd8040a + RNCAsyncStorage: 481acf401089f312189e100815088ea5dafc583c + RNDeviceInfo: d79872e11c8e9c4de0d65b0ee6e0cee719f37fea + RNFBAnalytics: fe8bbc24c774e0a8499816c526f30c10a38a1474 + RNFBApp: b34af55f0a0f5da38ecaaf33dd03c1e6a965a72a + RNFBAppCheck: 61bc4416bfe0d4deea9864b057469344d5954199 + RNFBAppDistribution: dd51aaa60fde0f06001e3abe1a90cc891b2b9fc2 + RNFBAuth: e28fd028bcde0d6c96ab43d014c4713b62eca232 + RNFBCrashlytics: c9dd67475e50cbac5b198a98c4cf431e936f790d + RNFBDatabase: e31b630fb269f8c06efcea8b37c303ae89a7900b + RNFBFirestore: 52e55ed0dbd5ec43714dead8dfd498ec02c70411 + RNFBFunctions: 70f93e372b4ca89e7361bca4bd3587f3a2ea87ea + RNFBInAppMessaging: f8b4be2b9534737c4514c75e86a7c8e6d03a6899 + RNFBInstallations: fe503b1f73ec6bbfb35e7d83761e017c9658f06c + RNFBMessaging: 70b7e2b3ac06554d8736fcc0b279f91163224be8 + RNFBML: 8ee530cad1835b8ea9b6d6e32b1f40a5a1335b64 + RNFBPerf: 26cf0b1d9b3996f96cd71ec75c2149b9d851365c + RNFBRemoteConfig: afdbedf75e7f0fa9b1dcc0c3bbe523233b220a05 + RNFBStorage: 8fe7998c81653d6b1b89323f8b38492e335c8bc5 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 3bb1ee33b5133befbd33872601fa46efdd48e841 + Yoga: 66a9fd80007d5d5fce19d1676ce17b4d5e16e9b1 PODFILE CHECKSUM: dbd2dddd0ee0d9aea4622842cf7e57adc932db8e diff --git a/tests/macos/Podfile.lock b/tests/macos/Podfile.lock index 26fd49c194..fee7f58e23 100644 --- a/tests/macos/Podfile.lock +++ b/tests/macos/Podfile.lock @@ -1696,66 +1696,66 @@ SPEC CHECKSUMS: FBLazyVector: 31dd89ddefc65148109e00de4270d3ac484bd8c1 fmt: c64af9f14aca1ddf7c631bacdfa473a3e084b321 glog: b7594b792ee4e02ed1f44b01d046ca25fa713e3d - RCT-Folly: 759c186750e3844b0d1cf71f7fda0c7fa2933ac4 + RCT-Folly: f0225a3a045e6e6fff9747c6479b56d0ad564dd6 RCTDeprecation: 9da6c2d8a3b1802142718283260fb06d702ddb07 RCTRequired: 574f9d55bda1d50676530b6c36bab4605612dfb6 RCTTypeSafety: 7de929c405e619c023116e7747118a2c5d5b2320 React: ee782717a9c48bc876ab9d625324790a750775a9 React-callinvoker: 13537424f973520ee8d5b6a9d3c6f84443c6d1ff - React-Core: 4842999f1b7850b39a0f697b37cebaa118779ee4 - React-CoreModules: cde636ff0f5b23e184fa42dfee86908ee8bf6ca1 - React-cxxreact: 6545dc6b533888bd00da73436b27cb21cbcec14f + React-Core: 3aa02c310c18b21155fc969fda0a927fda51ecb8 + React-CoreModules: 86e7642d5e1b9820332b63da1b5c58e66a85f47d + React-cxxreact: 0320f9da3b33ca64755dfcb56469ad6d2abeb95e React-debug: 2bc559a0e9d4fcdb3ac425f94270cc6ed7df9e05 - React-defaultsnativemodule: 756c26280e2074525293ba1f982284f4a22aab3a - React-domnativemodule: 404b6aa2a9041b25ececa481747d0e5091444aef - React-Fabric: ae1ff092778472268f4d52bcdee6131d1eda766a - React-FabricComponents: 17cecbdd567dfcf9841cee57828a53e943210294 - React-FabricImage: faafab4f68d4416825d5ceba317016ea53ad262d + React-defaultsnativemodule: dd27faec88633ff9bb04500d1cb01999383c4974 + React-domnativemodule: 30cf9005cc8e89ea167756f6248f041956f0d734 + React-Fabric: 6b1fe0f80d7fa4afb0e24369bb97823b422307bb + React-FabricComponents: 319102424ff592e2cd122dcc2227362d1e6c564f + React-FabricImage: 15e2b1379e51eab32992bfe5b252a91b30718fcb React-featureflags: 430dcdb3434a1e0e82a86fd9e54cb60c00727c91 - React-featureflagsnativemodule: a2cd701246c85ab22cd866855e2d7f88b73388c7 - React-graphics: 3fcb6cc610aeb4382ad6633959a0b1c47c5073cc - React-idlecallbacksnativemodule: 39617353e600b0b6bcaecc3224a2888ac7f05e3c - React-ImageManager: 45c24f173763276f768de21a6402d97ffaa99a96 + React-featureflagsnativemodule: ad8c74e23811350846eaac6dfc066fe0f1f591ae + React-graphics: c923f8a55307484e89d59430ffaf1e0d36313831 + React-idlecallbacksnativemodule: d696528ae1854f3beafb8c0374b72420e0720654 + React-ImageManager: f3aec32f5a763636b7413b7dcb5ec255b12fcf91 React-jsc: 110824a6d8488ea7d8a3b72cb35e528e462f9281 - React-jserrorhandler: c558b7d139d1d27580faf5eb4ed7c7fb1bd17b2f - React-jsi: fd8de08b2c9580bd8642b162f8d0e0d521a91b51 - React-jsiexecutor: 17781836b7c14e0ff818cf0f452879d44e276ff8 - React-jsinspector: e82b641cceefe7d64b02ff102aa357bebc6c3fab - React-jsinspectortracing: c8531ea5125d017530aac9039936d6a1d0e6848f - React-jsitracing: d43e7b3d7a953e91c79490bbd74ef8abd2a9b3e7 - React-logger: 4decf7656034a9a718ba347ecc1c87c79a04b58b - React-Mapbuffer: 4ad71d8e010591048bfa792f1ad6178181b87b9f - React-microtasksnativemodule: 06c2ee7b0abc0680ce7c32d04b1e36f58e8271e1 - React-NativeModulesApple: 3a407fb81694679cb400fc2504b37354e9234272 - React-perflogger: 96cceccf29bf5507b3f48339f1d7282827a5274c - React-performancetimeline: f3f35f8a93f3788a0f920a09f0a99d58d14dcd06 + React-jserrorhandler: 024b0f3663b22e184c2cb121bb31235a1e2c93d7 + React-jsi: 6dfdf32e927fd161dd8369d5e421a32bb5edb20e + React-jsiexecutor: 6cc1d7ce881faa1d5dca82b0d0667d5c09884b27 + React-jsinspector: 05e7f743e02c296a912a4f0c0f57e586442dc4c8 + React-jsinspectortracing: 240cbca9e33331b849d6b20f0d8b0d984994dc6b + React-jsitracing: 60136e095299445d4322fe0e07ddfdf775d3250e + React-logger: 8e5c62114dc6a8ae688d6c655dac1bc988892e64 + React-Mapbuffer: f0ac9fd9d7d860258ca7972d1660a05a3550a344 + React-microtasksnativemodule: 964c92e955968e8d086fcfcc4d6d34a587af38eb + React-NativeModulesApple: 3021d910a10f52e5fd565cce8518d2f7ab8fa512 + React-perflogger: e13bcc7a9221c3709d3c8178d9e7f709be4f1350 + React-performancetimeline: 985dbe15d489dd2004d7de871ff464050e90efb4 React-RCTActionSheet: c5496a827b39f505ffc1d27a52400df58e7a7433 - React-RCTAnimation: 85bd4ea6add91fcebc8d878f77b06e461b56ad71 - React-RCTAppDelegate: 789cec3d32ea5d2956b132f75ba957c0c9c360ad - React-RCTBlob: e80761ffa6796e2564d368a05d06fb739f9b91a4 - React-RCTFabric: d246c64f884b1e1b54ad1743a0159aa9c1eadf2f - React-RCTFBReactNativeSpec: d94c834bd99b35ec7b229c2be14539c2690a1d8c - React-RCTImage: 0be56a9f1eb4751f49ef45fc6ad27e71746d86fd - React-RCTLinking: 18d73b57aaeca804b842722362ed26ff4685ba49 - React-RCTNetwork: 0740e1a7716e67826e86277a363c14d8cd762733 - React-RCTSettings: 013a99ae1b136d66ab43a6af136f799de59be8a4 - React-RCTText: 64e02ee03d40e880fff0577945059ab13c197e48 - React-RCTVibration: ba41c5511e2631fc85868dc641711c85f7014c0d + React-RCTAnimation: 2a3fe9a75306411bb35bb076f1460fdc20100424 + React-RCTAppDelegate: 1f046d75ff2d15a7eeda999caef966b1a4ebdf0a + React-RCTBlob: 3a148c8d13ee0bd1ea47d0abe3cee59c8cee7b29 + React-RCTFabric: 996d9e8996d15af7d137a744d60adb832a72f74a + React-RCTFBReactNativeSpec: cf50271a4ea3cf07f362a91d913157a1f2216ca0 + React-RCTImage: a3d738fd2f91fde3ed01ecc02e7330ac08fddba3 + React-RCTLinking: 852bf1d4e09642bac25ae9914e1bf526e7a2f482 + React-RCTNetwork: 9258e83aa911199f6b42a95f3f6f1bdfb3d40c73 + React-RCTSettings: aed7da613ab1b362ac60c0ee9ce2cdbaa32c8924 + React-RCTText: 6328e0e445b8b83809cab4eab7662be8b276e585 + React-RCTVibration: 3eb6934cecfd666e03d0ab67984a07be2384396f React-rendererconsistency: 7abe42d721328057bcbc198e30a683708a4fed3e - React-rendererdebug: 8704ce40919948e41ff03906cfcc08a439cf06f3 + React-rendererdebug: 8022352a38e4265058541a907c146822bb050bd4 React-rncore: a0f8d109f16242155e59631d96a128e3d55f1d54 - React-RuntimeApple: 295b11341ca12f204bb8cae819e73678673396d2 - React-RuntimeCore: 76c8cba00208ce89bccc44e85fae08eeb988430d + React-RuntimeApple: 6622b3f91601680bf17340b5efdbbc22a2031de6 + React-RuntimeCore: 86b83e750d4a1c457220d6862f820ab3a668e6e1 React-runtimeexecutor: 4b57851b6252cb71c8812a50e8055cb0086fc180 - React-runtimescheduler: 6f7e00c01ddd78bae5bb8dae541b8697cf92fab8 + React-runtimescheduler: d38dc6c06bc96a5cfe8202d576db24de53470321 React-timing: 756815d960e39ff0d09ff9c7cb7159299edf0169 - React-utils: e35ed3ed4e48a381fbb35e446c0e89cce5a0605f - ReactAppDependencyProvider: 78d1aa823d2f280f83372f9abb50502cee4d0424 - ReactCodegen: cfbf6d5eabb2335c65a821c9fd8e1883e87c1ef7 - ReactCommon: 6bb5e1ee21c4b0e6e203cece50d86cc3f7a5af1b - RNCAsyncStorage: b44e8a4e798c3e1f56bffccd0f591f674fb9198f + React-utils: 6a1d2e32976fdd017617b21a1a371f914a69afdb + ReactAppDependencyProvider: 3a460ab76f1149df99ed324bb1c764b76de7a730 + ReactCodegen: 4fdcda55518b6fbc14e919d871a2d38856dcd469 + ReactCommon: 4194f3e1a5350e92e0080ea62d6072a3d8ec8071 + RNCAsyncStorage: 23e56519cc41d3bade3c8d4479f7760cb1c11996 SocketRocket: 03f7111df1a343b162bf5b06ead333be808e1e0a - Yoga: 187db0b2a37012c01dac36afb886a29b92bfd943 + Yoga: 8b70615ff4b017f445272a7caa9db908e6ae0ef8 PODFILE CHECKSUM: 787ee60864ef7264ea1a666923a1119b7db1a281