|
|
|
|
@@ -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
|
|
|
|
|
|