[firestore][ios] re-write FirestoreDocReference internals + add support for Infinity & NaN numbers

This commit is contained in:
Salakar
2018-09-23 07:49:50 +01:00
parent 685270402e
commit 571d9b6031
5 changed files with 437 additions and 248 deletions

View File

@@ -24,7 +24,6 @@
- (void)onSnapshot:(NSString *)listenerId docListenOptions:(NSDictionary *) docListenOptions;
- (void)set:(NSDictionary *)data options:(NSDictionary *)options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)update:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (BOOL)hasListeners;
+ (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot;
+ (NSDictionary *)parseJSMap:(FIRFirestore *) firestore jsMap:(NSDictionary *) jsMap;
+ (NSArray *)parseJSArray:(FIRFirestore *) firestore jsArray:(NSArray *) jsArray;

View File

@@ -5,295 +5,439 @@
#if __has_include(<FirebaseFirestore/FirebaseFirestore.h>)
static NSMutableDictionary *_listeners;
static NSString *const typeKey = @"type";
static NSString *const keyPath = @"path";
static NSString *const keyData = @"data";
static NSString *const keyError = @"error";
static NSString *const valueKey = @"value";
static NSString *const keyMerge = @"merge";
static NSString *const keyAppName = @"appName";
static NSString *const keyLatitude = @"latitude";
static NSString *const keyMetadata = @"metadata";
static NSString *const keyLongitude = @"longitude";
static NSString *const keyFromCache = @"fromCache";
static NSString *const keyListenerId = @"listenerId";
static NSString *const keyDocumentSnapshot = @"documentSnapshot";
static NSString *const keyHasPendingWrites = @"hasPendingWrites";
static NSString *const keyIncludeMetaChanges = @"includeMetadataChanges";
static NSString *const typeNaN = @"nan";
static NSString *const typeNull = @"null";
static NSString *const typeBlob = @"blob";
static NSString *const typeDate = @"date";
static NSString *const typeArray = @"array";
static NSString *const typeObject = @"object";
static NSString *const typeString = @"string";
static NSString *const typeNumber = @"number";
static NSString *const typeDelete = @"delete";
static NSString *const typeBoolean = @"boolean";
static NSString *const typeInfinity = @"infinity";
static NSString *const typeGeoPoint = @"geopoint";
static NSString *const typeTimestamp = @"timestamp";
static NSString *const typeReference = @"reference";
static NSString *const typeDocumentId = @"documentid";
static NSString *const typeFieldValue = @"fieldvalue";
- (id)initWithPath:(RCTEventEmitter *)emitter
appDisplayName:(NSString *) appDisplayName
path:(NSString *) path {
self = [super init];
if (self) {
_emitter = emitter;
_appDisplayName = appDisplayName;
_path = path;
_ref = [[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] documentWithPath:_path];
}
// Initialise the static listeners object if required
if (!_listeners) {
_listeners = [[NSMutableDictionary alloc] init];
}
return self;
appDisplayName:(NSString *)appDisplayName
path:(NSString *)path {
self = [super init];
if (self) {
_emitter = emitter;
_appDisplayName = appDisplayName;
_path = path;
_ref = [[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] documentWithPath:_path];
}
// Initialise the static listeners object if required
if (!_listeners) {
_listeners = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)delete:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
[_ref deleteDocumentWithCompletion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
- (void)delete:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject {
[_ref deleteDocumentWithCompletion:^(NSError *_Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
}
- (void)get:(NSDictionary *) getOptions
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
FIRFirestoreSource source;
if (getOptions && getOptions[@"source"]) {
if ([getOptions[@"source"] isEqualToString:@"server"]) {
source = FIRFirestoreSourceServer;
} else if ([getOptions[@"source"] isEqualToString:@"cache"]) {
source = FIRFirestoreSourceCache;
} else {
source = FIRFirestoreSourceDefault;
}
- (void)get:(NSDictionary *)getOptions
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject {
FIRFirestoreSource source;
if (getOptions && getOptions[@"source"]) {
if ([getOptions[@"source"] isEqualToString:@"server"]) {
source = FIRFirestoreSourceServer;
} else if ([getOptions[@"source"] isEqualToString:@"cache"]) {
source = FIRFirestoreSourceCache;
} else {
source = FIRFirestoreSourceDefault;
source = FIRFirestoreSourceDefault;
}
[_ref getDocumentWithSource:source completion:^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
if (error) {
[RNFirebaseFirestore promiseRejectException:reject error:error];
} else {
NSDictionary *data = [RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot];
resolve(data);
}
}];
}
+ (void)offSnapshot:(NSString *) listenerId {
id<FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
[_listeners removeObjectForKey:listenerId];
[listener remove];
}
}
- (void)onSnapshot:(NSString *) listenerId
docListenOptions:(NSDictionary *) docListenOptions {
if (_listeners[listenerId] == nil) {
id listenerBlock = ^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) {
if (error) {
id<FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
[_listeners removeObjectForKey:listenerId];
[listener remove];
}
[self handleDocumentSnapshotError:listenerId error:error];
} else {
[self handleDocumentSnapshotEvent:listenerId documentSnapshot:snapshot];
}
};
bool includeMetadataChanges;
if (docListenOptions && docListenOptions[@"includeMetadataChanges"]) {
includeMetadataChanges = true;
} else {
includeMetadataChanges = false;
}
id<FIRListenerRegistration> listener = [_ref addSnapshotListenerWithIncludeMetadataChanges:includeMetadataChanges listener:listenerBlock];
_listeners[listenerId] = listener;
}
}
- (void)set:(NSDictionary *) data
options:(NSDictionary *) options
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
if (options && options[@"merge"]) {
[_ref setData:dictionary merge:true completion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
} else {
[_ref setData:dictionary completion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
}
}
- (void)update:(NSDictionary *) data
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
NSDictionary *dictionary = [RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
[_ref updateData:dictionary completion:^(NSError * _Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
}
- (BOOL)hasListeners {
return [[_listeners allKeys] count] > 0;
}
+ (void)handleWriteResponse:(NSError *) error
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
} else {
source = FIRFirestoreSourceDefault;
}
[_ref getDocumentWithSource:source completion:^(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable error) {
if (error) {
[RNFirebaseFirestore promiseRejectException:reject error:error];
[RNFirebaseFirestore promiseRejectException:reject error:error];
} else {
resolve(nil);
NSDictionary *data = [RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot];
resolve(data);
}
}];
}
+ (void)offSnapshot:(NSString *)listenerId {
id<FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
[_listeners removeObjectForKey:listenerId];
[listener remove];
}
}
- (void)onSnapshot:(NSString *)listenerId
docListenOptions:(NSDictionary *)docListenOptions {
if (_listeners[listenerId] == nil) {
id listenerBlock = ^(FIRDocumentSnapshot *_Nullable snapshot, NSError *_Nullable error) {
if (error) {
id<FIRListenerRegistration> listener = _listeners[listenerId];
if (listener) {
[_listeners removeObjectForKey:listenerId];
[listener remove];
}
[self handleDocumentSnapshotError:listenerId error:error];
} else {
[self handleDocumentSnapshotEvent:listenerId documentSnapshot:snapshot];
}
};
bool includeMetadataChanges;
if (docListenOptions && docListenOptions[keyIncludeMetaChanges]) {
includeMetadataChanges = true;
} else {
includeMetadataChanges = false;
}
id<FIRListenerRegistration>
listener = [_ref addSnapshotListenerWithIncludeMetadataChanges:includeMetadataChanges listener:listenerBlock];
_listeners[listenerId] = listener;
}
}
- (void)set:(NSDictionary *)data
options:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject {
NSDictionary *dictionary =
[RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
if (options && options[keyMerge]) {
[_ref setData:dictionary merge:true completion:^(NSError *_Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
} else {
[_ref setData:dictionary completion:^(NSError *_Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
}
}
- (void)update:(NSDictionary *)data
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject {
NSDictionary *dictionary =
[RNFirebaseFirestoreDocumentReference parseJSMap:[RNFirebaseFirestore getFirestoreForApp:_appDisplayName] jsMap:data];
[_ref updateData:dictionary completion:^(NSError *_Nullable error) {
[RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject];
}];
}
+ (void)handleWriteResponse:(NSError *)error
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject {
if (error) {
[RNFirebaseFirestore promiseRejectException:reject error:error];
} else {
resolve(nil);
}
}
+ (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot {
NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init];
[snapshot setValue:documentSnapshot.reference.path forKey:@"path"];
if (documentSnapshot.exists) {
[snapshot setValue:[RNFirebaseFirestoreDocumentReference buildNativeMap:documentSnapshot.data] forKey:@"data"];
}
if (documentSnapshot.metadata) {
NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init];
[metadata setValue:@(documentSnapshot.metadata.fromCache) forKey:@"fromCache"];
[metadata setValue:@(documentSnapshot.metadata.hasPendingWrites) forKey:@"hasPendingWrites"];
[snapshot setValue:metadata forKey:@"metadata"];
}
return snapshot;
NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init];
[snapshot setValue:documentSnapshot.reference.path forKey:keyPath];
if (documentSnapshot.exists) {
[snapshot setValue:[RNFirebaseFirestoreDocumentReference buildNativeMap:documentSnapshot.data] forKey:keyData];
}
if (documentSnapshot.metadata) {
NSMutableDictionary *metadata = [[NSMutableDictionary alloc] init];
[metadata setValue:@(documentSnapshot.metadata.fromCache) forKey:keyFromCache];
[metadata setValue:@(documentSnapshot.metadata.hasPendingWrites) forKey:keyHasPendingWrites];
[snapshot setValue:metadata forKey:keyMetadata];
}
return snapshot;
}
- (void)handleDocumentSnapshotError:(NSString *)listenerId
error:(NSError *)error {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_appDisplayName forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:@"error"];
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_path forKey:keyPath];
[event setValue:listenerId forKey:keyListenerId];
[event setValue:_appDisplayName forKey:keyAppName];
[event setValue:[RNFirebaseFirestore getJSError:error] forKey:keyError];
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
}
- (void)handleDocumentSnapshotEvent:(NSString *)listenerId
documentSnapshot:(FIRDocumentSnapshot *)documentSnapshot {
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_appDisplayName forKey:@"appName"];
[event setValue:_path forKey:@"path"];
[event setValue:listenerId forKey:@"listenerId"];
[event setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentSnapshot] forKey:@"documentSnapshot"];
NSMutableDictionary *event = [[NSMutableDictionary alloc] init];
[event setValue:_path forKey:keyPath];
[event setValue:listenerId forKey:keyListenerId];
[event setValue:_appDisplayName forKey:keyAppName];
[event setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentSnapshot] forKey:keyDocumentSnapshot];
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
[RNFirebaseUtil sendJSEvent:self.emitter name:FIRESTORE_DOCUMENT_SYNC_EVENT body:event];
}
+ (NSDictionary *)buildNativeMap:(NSDictionary *)nativeMap {
NSMutableDictionary *map = [[NSMutableDictionary alloc] init];
[nativeMap enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
NSDictionary *typeMap = [RNFirebaseFirestoreDocumentReference buildTypeMap:obj];
map[key] = typeMap;
}];
NSMutableDictionary *map = [[NSMutableDictionary alloc] init];
[nativeMap enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
NSDictionary *typeMap = [RNFirebaseFirestoreDocumentReference buildTypeMap:obj];
map[key] = typeMap;
}];
return map;
return map;
}
+ (NSArray *)buildNativeArray:(NSArray *)nativeArray {
NSMutableArray *array = [[NSMutableArray alloc] init];
[nativeArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSDictionary *typeMap = [RNFirebaseFirestoreDocumentReference buildTypeMap:obj];
[array addObject:typeMap];
}];
NSMutableArray *array = [[NSMutableArray alloc] init];
[nativeArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
NSDictionary *typeMap = [RNFirebaseFirestoreDocumentReference buildTypeMap:obj];
[array addObject:typeMap];
}];
return array;
return array;
}
+ (NSDictionary *)buildTypeMap:(id) value {
NSMutableDictionary *typeMap = [[NSMutableDictionary alloc] init];
if (!value) {
typeMap[@"type"] = @"null";
} else if ([value isKindOfClass:[NSString class]]) {
typeMap[@"type"] = @"string";
typeMap[@"value"] = value;
} else if ([value isKindOfClass:[NSDictionary class]]) {
typeMap[@"type"] = @"object";
typeMap[@"value"] = [RNFirebaseFirestoreDocumentReference buildNativeMap:value];
} else if ([value isKindOfClass:[NSArray class]]) {
typeMap[@"type"] = @"array";
typeMap[@"value"] = [RNFirebaseFirestoreDocumentReference buildNativeArray:value];
} else if ([value isKindOfClass:[FIRDocumentReference class]]) {
typeMap[@"type"] = @"reference";
FIRDocumentReference *ref = (FIRDocumentReference *)value;
typeMap[@"value"] = [ref path];
} else if ([value isKindOfClass:[FIRGeoPoint class]]) {
typeMap[@"type"] = @"geopoint";
FIRGeoPoint *point = (FIRGeoPoint *)value;
NSMutableDictionary *geopoint = [[NSMutableDictionary alloc] init];
geopoint[@"latitude"] = @([point latitude]);
geopoint[@"longitude"] = @([point longitude]);
typeMap[@"value"] = geopoint;
} else if ([value isKindOfClass:[NSDate class]]) {
typeMap[@"type"] = @"date";
// NOTE: The round() is important as iOS ends up giving .999 otherwise,
// and loses a millisecond when going between native and JS
typeMap[@"value"] = @(round([(NSDate *)value timeIntervalSince1970] * 1000.0));
} else if ([value isKindOfClass:[NSNumber class]]) {
NSNumber *number = (NSNumber *)value;
if (number == (void*)kCFBooleanFalse || number == (void*)kCFBooleanTrue) {
typeMap[@"type"] = @"boolean";
} else {
typeMap[@"type"] = @"number";
}
typeMap[@"value"] = value;
} else if ([value isKindOfClass:[NSData class]]) {
typeMap[@"type"] = @"blob";
NSData *blob = (NSData *)value;
typeMap[@"value"] = [blob base64EncodedStringWithOptions:0];
} else {
// TODO: Log an error
typeMap[@"type"] = @"null";
}
/**
*
* @param value
* @return
*/
+ (NSDictionary *)buildTypeMap:(id)value {
NSMutableDictionary *typeMap = [[NSMutableDictionary alloc] init];
// null
if (value == nil) {
typeMap[typeKey] = typeNull;
return typeMap;
}
// strings
if ([value isKindOfClass:[NSString class]]) {
typeMap[typeKey] = typeString;
typeMap[valueKey] = value;
return typeMap;
}
// objects
if ([value isKindOfClass:[NSDictionary class]]) {
typeMap[typeKey] = typeObject;
typeMap[valueKey] = [RNFirebaseFirestoreDocumentReference buildNativeMap:value];
return typeMap;
}
// array
if ([value isKindOfClass:[NSArray class]]) {
typeMap[typeKey] = typeArray;
typeMap[valueKey] = [RNFirebaseFirestoreDocumentReference buildNativeArray:value];
return typeMap;
}
// reference
if ([value isKindOfClass:[FIRDocumentReference class]]) {
typeMap[typeKey] = typeReference;
FIRDocumentReference *ref = (FIRDocumentReference *) value;
typeMap[valueKey] = [ref path];
return typeMap;
}
// geopoint
if ([value isKindOfClass:[FIRGeoPoint class]]) {
typeMap[typeKey] = typeGeoPoint;
FIRGeoPoint *point = (FIRGeoPoint *) value;
NSMutableDictionary *geopoint = [[NSMutableDictionary alloc] init];
geopoint[keyLatitude] = @([point latitude]);
geopoint[keyLongitude] = @([point longitude]);
typeMap[valueKey] = geopoint;
return typeMap;
}
// date
if ([value isKindOfClass:[NSDate class]]) {
typeMap[typeKey] = typeDate;
// round is required otherwise iOS ends up with .999 and loses a millisecond
// when going between native and JS
typeMap[valueKey] = @(round([(NSDate *) value timeIntervalSince1970] * 1000.0));
return typeMap;
}
// number / boolean / infinity / nan
if ([value isKindOfClass:[NSNumber class]]) {
NSNumber *number = (NSNumber *) value;
// infinity
if (number == @(INFINITY)) {
typeMap[typeKey] = typeInfinity;
return typeMap;
}
// boolean
if (number == [NSValue valueWithPointer:(void *) kCFBooleanFalse]
|| number == [NSValue valueWithPointer:(void *) kCFBooleanTrue]) {
typeMap[typeKey] = typeBoolean;
typeMap[valueKey] = value;
return typeMap;
}
// nan
if ([[value description].lowercaseString isEqual:@"nan"]) {
typeMap[typeKey] = typeNaN;
return typeMap;
}
// number
typeMap[typeKey] = typeNumber;
typeMap[valueKey] = value;
return typeMap;
}
// blobs (converted to base64)
if ([value isKindOfClass:[NSData class]]) {
NSData *blob = (NSData *) value;
typeMap[typeKey] = typeBlob;
typeMap[valueKey] = [blob base64EncodedStringWithOptions:0];
return typeMap;
}
DLog(@"RNFirebaseFirestore: Unsupported value sent to buildTypeMap - class type is %@",
NSStringFromClass([value class]));
typeMap[typeKey] = typeNull;
return typeMap;
}
+(NSDictionary *)parseJSMap:(FIRFirestore *) firestore
jsMap:(NSDictionary *) jsMap {
NSMutableDictionary* map = [[NSMutableDictionary alloc] init];
if (jsMap) {
[jsMap enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
map[key] = [RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:obj];
}];
}
return map;
/**
*
* @param firestore
* @param jsMap
* @return
*/
+ (NSDictionary *)parseJSMap:(FIRFirestore *)firestore
jsMap:(NSDictionary *)jsMap {
NSMutableDictionary *map = [[NSMutableDictionary alloc] init];
if (jsMap) {
[jsMap enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL *_Nonnull stop) {
map[key] = [RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:obj];
}];
}
return map;
}
+(NSArray *)parseJSArray:(FIRFirestore *) firestore
jsArray:(NSArray *) jsArray {
NSMutableArray* array = [[NSMutableArray alloc] init];
if (jsArray) {
[jsArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[array addObject:[RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:obj]];
}];
}
return array;
/**
*
* @param firestore
* @param jsArray
* @return
*/
+ (NSArray *)parseJSArray:(FIRFirestore *)firestore
jsArray:(NSArray *)jsArray {
NSMutableArray *array = [[NSMutableArray alloc] init];
if (jsArray) {
[jsArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
[array addObject:[RNFirebaseFirestoreDocumentReference parseJSTypeMap:firestore jsTypeMap:obj]];
}];
}
return array;
}
+(id)parseJSTypeMap:(FIRFirestore *) firestore
jsTypeMap:(NSDictionary *) jsTypeMap {
NSString *type = jsTypeMap[@"type"];
id value = jsTypeMap[@"value"];
if ([type isEqualToString:@"array"]) {
return [RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:value];
} else if ([type isEqualToString:@"object"]) {
return [RNFirebaseFirestoreDocumentReference parseJSMap:firestore jsMap:value];
} else if ([type isEqualToString:@"reference"]) {
return [firestore documentWithPath:value];
} else if ([type isEqualToString:@"blob"]) {
return [[NSData alloc] initWithBase64EncodedString:(NSString *) value options:0];
} else if ([type isEqualToString:@"geopoint"]) {
NSDictionary *geopoint = (NSDictionary*)value;
NSNumber *latitude = geopoint[@"latitude"];
NSNumber *longitude = geopoint[@"longitude"];
return [[FIRGeoPoint alloc] initWithLatitude:[latitude doubleValue] longitude:[longitude doubleValue]];
} else if ([type isEqualToString:@"date"]) {
return [NSDate dateWithTimeIntervalSince1970:([(NSNumber *)value doubleValue] / 1000.0)];
} else if ([type isEqualToString:@"documentid"]) {
return [FIRFieldPath documentID];
} else if ([type isEqualToString:@"fieldvalue"]) {
NSString *string = (NSString*)value;
if ([string isEqualToString:@"delete"]) {
return [FIRFieldValue fieldValueForDelete];
} else if ([string isEqualToString:@"timestamp"]) {
return [FIRFieldValue fieldValueForServerTimestamp];
} else {
// TODO: Log warning
return nil;
}
} else if ([type isEqualToString:@"boolean"] || [type isEqualToString:@"number"] || [type isEqualToString:@"string"] || [type isEqualToString:@"null"]) {
return value;
} else {
// TODO: Log error
return nil;
/**
*
* @param firestore
* @param jsTypeMap
* @return
*/
+ (id)parseJSTypeMap:(FIRFirestore *)firestore
jsTypeMap:(NSDictionary *)jsTypeMap {
id value = jsTypeMap[valueKey];
NSString *type = jsTypeMap[typeKey];
if ([type isEqualToString:typeArray]) {
return [RNFirebaseFirestoreDocumentReference parseJSArray:firestore jsArray:value];
}
if ([type isEqualToString:typeObject]) {
return [RNFirebaseFirestoreDocumentReference parseJSMap:firestore jsMap:value];
}
if ([type isEqualToString:typeReference]) {
return [firestore documentWithPath:value];
}
if ([type isEqualToString:typeBlob]) {
return [[NSData alloc] initWithBase64EncodedString:(NSString *) value options:0];
}
if ([type isEqualToString:typeGeoPoint]) {
NSDictionary *geopoint = (NSDictionary *) value;
NSNumber *latitude = geopoint[keyLatitude];
NSNumber *longitude = geopoint[keyLongitude];
return [[FIRGeoPoint alloc] initWithLatitude:[latitude doubleValue] longitude:[longitude doubleValue]];
}
if ([type isEqualToString:typeDate]) {
return [NSDate dateWithTimeIntervalSince1970:([(NSNumber *) value doubleValue] / 1000.0)];
}
if ([type isEqualToString:typeDocumentId]) {
return [FIRFieldPath documentID];
}
if ([type isEqualToString:typeFieldValue]) {
NSString *string = (NSString *) value;
if ([string isEqualToString:typeDelete]) {
return [FIRFieldValue fieldValueForDelete];
}
if ([string isEqualToString:typeTimestamp]) {
return [FIRFieldValue fieldValueForServerTimestamp];
}
DLog(@"RNFirebaseFirestore: Unsupported field-value sent to parseJSTypeMap - value is %@",
NSStringFromClass([value class]));
return nil;
}
if ([type isEqualToString:typeInfinity]) {
return @(INFINITY);
}
if ([type isEqualToString:typeNaN]) {
return [NSDecimalNumber notANumber];
}
if ([type isEqualToString:typeBoolean] || [type isEqualToString:typeNumber] || [type isEqualToString:typeString]
|| [type isEqualToString:typeNull]) {
return value;
}
return nil;
}
#endif

View File

@@ -46,6 +46,8 @@ export type NativeDocumentSnapshot = {
export type NativeTypeMap = {
type:
| 'nan'
| 'infinity'
| 'array'
| 'boolean'
| 'date'

View File

@@ -50,42 +50,63 @@ export const buildNativeArray = (array: Object[]): NativeTypeMap[] => {
export const buildTypeMap = (value: any): NativeTypeMap | null => {
const type = typeOf(value);
if (value === null || value === undefined || Number.isNaN(value)) {
if (Number.isNaN(value)) {
return {
type: 'nan',
value: null,
};
}
if (value === Infinity) {
return {
type: 'infinity',
value: null,
};
}
if (value === null || value === undefined) {
return {
type: 'null',
value: null,
};
}
if (value === DELETE_FIELD_VALUE) {
return {
type: 'fieldvalue',
value: 'delete',
};
}
if (value === SERVER_TIMESTAMP_FIELD_VALUE) {
return {
type: 'fieldvalue',
value: 'timestamp',
};
}
if (value === DOCUMENT_ID) {
return {
type: 'documentid',
value: null,
};
}
if (type === 'boolean' || type === 'number' || type === 'string') {
return {
type,
value,
};
}
if (type === 'array') {
return {
type,
value: buildNativeArray(value),
};
}
if (type === 'object') {
if (value instanceof DocumentReference) {
return {
@@ -93,6 +114,7 @@ export const buildTypeMap = (value: any): NativeTypeMap | null => {
value: value.path,
};
}
if (value instanceof GeoPoint) {
return {
type: 'geopoint',
@@ -102,23 +124,27 @@ export const buildTypeMap = (value: any): NativeTypeMap | null => {
},
};
}
if (value instanceof Date) {
return {
type: 'date',
value: value.getTime(),
};
}
if (value instanceof Blob) {
return {
type: 'blob',
value: value.toBase64(),
};
}
return {
type: 'object',
value: buildNativeMap(value),
};
}
console.warn(`Unknown data type received ${type}`);
return null;
};
@@ -160,27 +186,43 @@ const parseTypeMap = (firestore: Firestore, typeMap: NativeTypeMap): any => {
if (type === 'null') {
return null;
}
if (type === 'boolean' || type === 'number' || type === 'string') {
return value;
}
if (type === 'array') {
return parseNativeArray(firestore, value);
}
if (type === 'object') {
return parseNativeMap(firestore, value);
}
if (type === 'reference') {
return new DocumentReference(firestore, Path.fromName(value));
}
if (type === 'geopoint') {
return new GeoPoint(value.latitude, value.longitude);
}
if (type === 'date') {
return new Date(value);
}
if (type === 'blob') {
return Blob.fromBase64String(value);
}
if (type === 'infinity') {
return Infinity;
}
if (type === 'nan') {
return NaN;
}
console.warn(`Unknown data type received ${type}`);
return value;
};

View File

@@ -41,6 +41,8 @@ module.exports = {
gaz: 12.1234567,
geopoint: new firebase.firestore.GeoPoint(0, 0),
naz: null,
nan: NaN,
infinity: Infinity,
arrNumber: [1, 2, 3, 4],
arrString: ['a', 'b', 'c', 'd'],
object: {