diff --git a/ios/RNFirebase.xcodeproj/project.pbxproj b/ios/RNFirebase.xcodeproj/project.pbxproj index 5d91af3c..ed509361 100644 --- a/ios/RNFirebase.xcodeproj/project.pbxproj +++ b/ios/RNFirebase.xcodeproj/project.pbxproj @@ -12,6 +12,9 @@ 8323CF071F6FBD870071420B /* NativeExpressComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF011F6FBD870071420B /* NativeExpressComponent.m */; }; 8323CF081F6FBD870071420B /* RNFirebaseAdMobBannerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF031F6FBD870071420B /* RNFirebaseAdMobBannerManager.m */; }; 8323CF091F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 8323CF051F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m */; }; + 8376F7141F7C149100D45A85 /* RNFirebaseFirestoreDocumentReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */; }; + 8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */; }; + 8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */; }; 839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */; }; 839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91511EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.m */; }; 839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91531EF3E20A0077C7C8 /* RNFirebaseAdMobRewardedVideo.m */; }; @@ -50,6 +53,12 @@ 8323CF031F6FBD870071420B /* RNFirebaseAdMobBannerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMobBannerManager.m; sourceTree = ""; }; 8323CF041F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobNativeExpressManager.h; sourceTree = ""; }; 8323CF051F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMobNativeExpressManager.m; sourceTree = ""; }; + 8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestoreDocumentReference.m; sourceTree = ""; }; + 8376F70F1F7C149000D45A85 /* RNFirebaseFirestore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestore.h; sourceTree = ""; }; + 8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestore.m; sourceTree = ""; }; + 8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseFirestoreCollectionReference.m; sourceTree = ""; }; + 8376F7121F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestoreDocumentReference.h; sourceTree = ""; }; + 8376F7131F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseFirestoreCollectionReference.h; sourceTree = ""; }; 839D914E1EF3E20A0077C7C8 /* RNFirebaseAdMob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMob.h; sourceTree = ""; }; 839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMob.m; sourceTree = ""; }; 839D91501EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobInterstitial.h; sourceTree = ""; }; @@ -105,6 +114,7 @@ 839D915A1EF3E20A0077C7C8 /* config */, 839D915D1EF3E20A0077C7C8 /* crash */, 839D91601EF3E20A0077C7C8 /* database */, + 8376F70D1F7C141500D45A85 /* firestore */, 839D91631EF3E20A0077C7C8 /* messaging */, 839D91661EF3E20A0077C7C8 /* perf */, 839D91691EF3E20A0077C7C8 /* storage */, @@ -115,6 +125,20 @@ ); sourceTree = ""; }; + 8376F70D1F7C141500D45A85 /* firestore */ = { + isa = PBXGroup; + children = ( + 8376F70F1F7C149000D45A85 /* RNFirebaseFirestore.h */, + 8376F7101F7C149000D45A85 /* RNFirebaseFirestore.m */, + 8376F7131F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.h */, + 8376F7111F7C149000D45A85 /* RNFirebaseFirestoreCollectionReference.m */, + 8376F7121F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.h */, + 8376F70E1F7C149000D45A85 /* RNFirebaseFirestoreDocumentReference.m */, + ); + name = firestore; + path = RNFirebase/firestore; + sourceTree = ""; + }; 839D914D1EF3E20A0077C7C8 /* admob */ = { isa = PBXGroup; children = ( @@ -277,9 +301,12 @@ files = ( 839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */, 839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */, + 8376F7161F7C149100D45A85 /* RNFirebaseFirestoreCollectionReference.m in Sources */, 839D91761EF3E20B0077C7C8 /* RNFirebaseStorage.m in Sources */, + 8376F7151F7C149100D45A85 /* RNFirebaseFirestore.m in Sources */, 839D91701EF3E20B0077C7C8 /* RNFirebaseAuth.m in Sources */, 8323CF091F6FBD870071420B /* RNFirebaseAdMobNativeExpressManager.m in Sources */, + 8376F7141F7C149100D45A85 /* RNFirebaseFirestoreDocumentReference.m in Sources */, 839D916F1EF3E20B0077C7C8 /* RNFirebaseAnalytics.m in Sources */, 839D91711EF3E20B0077C7C8 /* RNFirebaseRemoteConfig.m in Sources */, D950369E1D19C77400F7094D /* RNFirebase.m in Sources */, diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestore.h b/ios/RNFirebase/firestore/RNFirebaseFirestore.h new file mode 100644 index 00000000..af556283 --- /dev/null +++ b/ios/RNFirebase/firestore/RNFirebaseFirestore.h @@ -0,0 +1,26 @@ +#ifndef RNFirebaseFirestore_h +#define RNFirebaseFirestore_h + +#import + +#if __has_include() + +#import +#import +#import + +@interface RNFirebaseFirestore : RCTEventEmitter {} + ++ (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error; + ++ (FIRFirestore *)getFirestoreForApp:(NSString *)appName; + +@end + +#else +@interface RNFirebaseFirestore : NSObject +@end +#endif + +#endif + diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestore.m b/ios/RNFirebase/firestore/RNFirebaseFirestore.m new file mode 100644 index 00000000..c15a894f --- /dev/null +++ b/ios/RNFirebase/firestore/RNFirebaseFirestore.m @@ -0,0 +1,162 @@ +#import "RNFirebaseFirestore.h" + +#if __has_include() + +#import +#import "RNFirebaseEvents.h" +#import "RNFirebaseFirestoreCollectionReference.h" +#import "RNFirebaseFirestoreDocumentReference.h" + +@implementation RNFirebaseFirestore +RCT_EXPORT_MODULE(); + +- (id)init { + self = [super init]; + if (self != nil) { + + } + return self; +} + +RCT_EXPORT_METHOD(collectionGet:(NSString *) appName + path:(NSString *) path + filters:(NSArray *) filters + orders:(NSArray *) orders + options:(NSDictionary *) options + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject) { + [[self getCollectionForAppPath:appName path:path filters:filters orders:orders options:options] get:resolve rejecter:reject]; +} + +RCT_EXPORT_METHOD(documentBatch:(NSString *) appName + writes:(NSArray *) writes + commitOptions:(NSDictionary *) commitOptions + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject) { + FIRFirestore *firestore = [RNFirebaseFirestore getFirestoreForApp:appName]; + FIRWriteBatch *batch = [firestore batch]; + + for (NSDictionary *write in writes) { + NSString *type = write[@"type"]; + NSString *path = write[@"path"]; + NSDictionary *data = write[@"data"]; + + FIRDocumentReference *ref = [firestore documentWithPath:path]; + + if ([type isEqualToString:@"DELETE"]) { + batch = [batch deleteDocument:ref]; + } else if ([type isEqualToString:@"SET"]) { + NSDictionary *options = write[@"options"]; + if (options && options[@"merge"]) { + batch = [batch setData:data forDocument:ref options:[FIRSetOptions merge]]; + } else { + batch = [batch setData:data forDocument:ref]; + } + } else if ([type isEqualToString:@"UPDATE"]) { + batch = [batch updateData:data forDocument:ref]; + } + } + + [batch commitWithCompletion:^(NSError * _Nullable error) { + if (error) { + [RNFirebaseFirestore promiseRejectException:reject error:error]; + } else { + NSMutableArray *result = [[NSMutableArray alloc] init]; + for (NSDictionary *write in writes) { + // Missing fields from web SDK + // writeTime + [result addObject:@{}]; + } + resolve(result); + } + }]; +} + +RCT_EXPORT_METHOD(documentCollections:(NSString *) appName + path:(NSString *) path + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject) { + [[self getDocumentForAppPath:appName path:path] get:resolve rejecter:reject]; +} + +RCT_EXPORT_METHOD(documentCreate:(NSString *) appName + path:(NSString *) path + data:(NSDictionary *) data + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject) { + [[self getDocumentForAppPath:appName path:path] create:data resolver:resolve rejecter:reject]; +} + +RCT_EXPORT_METHOD(documentDelete:(NSString *) appName + path:(NSString *) path + options:(NSDictionary *) options + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject) { + [[self getDocumentForAppPath:appName path:path] delete:options resolver:resolve rejecter:reject]; +} + +RCT_EXPORT_METHOD(documentGet:(NSString *) appName + path:(NSString *) path + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject) { + [[self getDocumentForAppPath:appName path:path] get:resolve rejecter:reject]; +} + +RCT_EXPORT_METHOD(documentGetAll:(NSString *) appName + documents:(NSString *) documents + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject) { + // Not supported on iOS out of the box +} + +RCT_EXPORT_METHOD(documentSet:(NSString *) appName + path:(NSString *) path + data:(NSDictionary *) data + options:(NSDictionary *) options + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject) { + [[self getDocumentForAppPath:appName path:path] set:data options:options resolver:resolve rejecter:reject]; +} + +RCT_EXPORT_METHOD(documentUpdate:(NSString *) appName + path:(NSString *) path + data:(NSDictionary *) data + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject) { + [[self getDocumentForAppPath:appName path:path] update:data resolver:resolve rejecter:reject]; +} + +/* + * INTERNALS/UTILS + */ ++ (void)promiseRejectException:(RCTPromiseRejectBlock)reject error:(NSError *)error { + // TODO + // NSDictionary *jsError = [RNFirebaseDatabase getJSError:databaseError]; + // reject([jsError valueForKey:@"code"], [jsError valueForKey:@"message"], databaseError); + reject(@"TODO", [error description], error); +} + ++ (FIRFirestore *)getFirestoreForApp:(NSString *)appName { + FIRApp *app = [FIRApp appNamed:appName]; + return [FIRFirestore firestoreForApp:app]; +} + +- (RNFirebaseFirestoreCollectionReference *)getCollectionForAppPath:(NSString *)appName path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options { + return [[RNFirebaseFirestoreCollectionReference alloc] initWithPathAndModifiers:appName path:path filters:filters orders:orders options:options]; +} + +- (RNFirebaseFirestoreDocumentReference *)getDocumentForAppPath:(NSString *)appName path:(NSString *)path { + return [[RNFirebaseFirestoreDocumentReference alloc] initWithPath:appName path:path]; +} + +- (NSArray *)supportedEvents { + return @[DATABASE_SYNC_EVENT, DATABASE_TRANSACTION_EVENT]; +} + +@end + +#else +@implementation RNFirebaseFirestore +@end +#endif + diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.h b/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.h new file mode 100644 index 00000000..04c668dd --- /dev/null +++ b/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.h @@ -0,0 +1,30 @@ +#ifndef RNFirebaseFirestoreCollectionReference_h +#define RNFirebaseFirestoreCollectionReference_h +#import + +#if __has_include() + +#import +#import "RNFirebaseFirestore.h" +#import "RNFirebaseFirestoreDocumentReference.h" + +@interface RNFirebaseFirestoreCollectionReference : NSObject +@property NSString *app; +@property NSString *path; +@property NSArray *filters; +@property NSArray *orders; +@property NSDictionary *options; +@property FIRQuery *query; + +- (id)initWithPathAndModifiers:(NSString *)app path:(NSString *)path filters:(NSArray *)filters orders:(NSArray *)orders options:(NSDictionary *)options; +- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; ++ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot; +@end + +#else + +@interface RNFirebaseFirestoreCollectionReference : NSObject +@end +#endif + +#endif diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m b/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m new file mode 100644 index 00000000..1b558963 --- /dev/null +++ b/ios/RNFirebase/firestore/RNFirebaseFirestoreCollectionReference.m @@ -0,0 +1,145 @@ +#import "RNFirebaseFirestoreCollectionReference.h" + +@implementation RNFirebaseFirestoreCollectionReference + +#if __has_include() + +- (id)initWithPathAndModifiers:(NSString *) app + path:(NSString *) path + filters:(NSArray *) filters + orders:(NSArray *) orders + options:(NSDictionary *) options { + self = [super init]; + if (self) { + _app = app; + _path = path; + _filters = filters; + _orders = orders; + _options = options; + _query = [self buildQuery]; + } + return self; +} + +- (void)get:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject { + [_query getDocumentsWithCompletion:^(FIRQuerySnapshot * _Nullable snapshot, NSError * _Nullable error) { + if (error) { + [RNFirebaseFirestore promiseRejectException:reject error:error]; + } else { + NSDictionary *data = [RNFirebaseFirestoreCollectionReference snapshotToDictionary:snapshot]; + resolve(data); + } + }]; +} + +- (FIRQuery *)buildQuery { + FIRQuery *query = (FIRQuery*)[[RNFirebaseFirestore getFirestoreForApp:_app] collectionWithPath:_path]; + query = [self applyFilters:query]; + query = [self applyOrders:query]; + query = [self applyOptions:query]; + + return query; +} + +- (FIRQuery *)applyFilters:(FIRQuery *) query { + for (NSDictionary *filter in _filters) { + NSString *fieldPath = filter[@"fieldPath"]; + NSString *operator = filter[@"operator"]; + // TODO: Validate this works + id value = filter[@"value"]; + + if ([operator isEqualToString:@"EQUAL"]) { + query = [query queryWhereField:fieldPath isEqualTo:value]; + } else if ([operator isEqualToString:@"GREATER_THAN"]) { + query = [query queryWhereField:fieldPath isGreaterThan:value]; + } else if ([operator isEqualToString:@"GREATER_THAN_OR_EQUAL"]) { + query = [query queryWhereField:fieldPath isGreaterThanOrEqualTo:value]; + } else if ([operator isEqualToString:@"LESS_THAN"]) { + query = [query queryWhereField:fieldPath isLessThan:value]; + } else if ([operator isEqualToString:@"LESS_THAN_OR_EQUAL"]) { + query = [query queryWhereField:fieldPath isLessThanOrEqualTo:value]; + } + } + return query; +} + +- (FIRQuery *)applyOrders:(FIRQuery *) query { + for (NSDictionary *order in _orders) { + NSString *direction = order[@"direction"]; + NSString *fieldPath = order[@"fieldPath"]; + + query = [query queryOrderedByField:fieldPath descending:([direction isEqualToString:@"DESCENDING"])]; + } + return query; +} + +- (FIRQuery *)applyOptions:(FIRQuery *) query { + if (_options[@"endAt"]) { + query = [query queryEndingAtValues:_options[@"endAt"]]; + } + if (_options[@"endBefore"]) { + query = [query queryEndingBeforeValues:_options[@"endBefore"]]; + } + if (_options[@"offset"]) { + // iOS doesn't support offset + } + if (_options[@"selectFields"]) { + // iOS doesn't support selectFields + } + if (_options[@"startAfter"]) { + query = [query queryStartingAfterValues:_options[@"startAfter"]]; + } + if (_options[@"startAt"]) { + query = [query queryStartingAtValues:_options[@"startAt"]]; + } + return query; +} + ++ (NSDictionary *)snapshotToDictionary:(FIRQuerySnapshot *)querySnapshot { + NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init]; + [snapshot setValue:[self documentChangesToArray:querySnapshot.documentChanges] forKey:@"changes"]; + [snapshot setValue:[self documentSnapshotsToArray:querySnapshot.documents] forKey:@"documents"]; + + return snapshot; +} + ++ (NSArray *)documentChangesToArray:(NSArray *) documentChanges { + NSMutableArray *changes = [[NSMutableArray alloc] init]; + for (FIRDocumentChange *change in documentChanges) { + [changes addObject:[self documentChangeToDictionary:change]]; + } + + return changes; +} + ++ (NSDictionary *)documentChangeToDictionary:(FIRDocumentChange *)documentChange { + NSMutableDictionary *change = [[NSMutableDictionary alloc] init]; + [change setValue:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:documentChange.document] forKey:@"document"]; + [change setValue:@(documentChange.newIndex) forKey:@"newIndex"]; + [change setValue:@(documentChange.oldIndex) forKey:@"oldIndex"]; + + if (documentChange.type == FIRDocumentChangeTypeAdded) { + [change setValue:@"added" forKey:@"type"]; + } else if (documentChange.type == FIRDocumentChangeTypeRemoved) { + [change setValue:@"removed" forKey:@"type"]; + } else if (documentChange.type == FIRDocumentChangeTypeModified) { + [change setValue:@"modified" forKey:@"type"]; + } + + return change; +} + ++ (NSArray *)documentSnapshotsToArray:(NSArray *) documentSnapshots { + NSMutableArray *snapshots = [[NSMutableArray alloc] init]; + for (FIRDocumentSnapshot *snapshot in documentSnapshots) { + [snapshots addObject:[RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot]]; + } + + return snapshots; +} + +#endif + +@end + diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h new file mode 100644 index 00000000..b4113c2c --- /dev/null +++ b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.h @@ -0,0 +1,32 @@ +#ifndef RNFirebaseFirestoreDocumentReference_h +#define RNFirebaseFirestoreDocumentReference_h + +#import + +#if __has_include() + +#import +#import "RNFirebaseFirestore.h" + +@interface RNFirebaseFirestoreDocumentReference : NSObject +@property NSString *app; +@property NSString *path; +@property FIRDocumentReference *ref; + +- (id)initWithPath:(NSString *)app path:(NSString *)path; +- (void)collections:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; +- (void)create:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; +- (void)delete:(NSDictionary *)options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; +- (void)get:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; +- (void)set:(NSDictionary *)data options:(NSDictionary *)options resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; +- (void)update:(NSDictionary *)data resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject; ++ (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot; +@end + +#else + +@interface RNFirebaseFirestoreDocumentReference : NSObject +@end +#endif + +#endif diff --git a/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m new file mode 100644 index 00000000..8b1b1bbb --- /dev/null +++ b/ios/RNFirebase/firestore/RNFirebaseFirestoreDocumentReference.m @@ -0,0 +1,100 @@ +#import "RNFirebaseFirestoreDocumentReference.h" + +@implementation RNFirebaseFirestoreDocumentReference + +#if __has_include() + +- (id)initWithPath:(NSString *) app + path:(NSString *) path { + self = [super init]; + if (self) { + _app = app; + _path = path; + _ref = [[RNFirebaseFirestore getFirestoreForApp:_app] documentWithPath:_path]; + } + return self; +} + +- (void)collections:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject { + // Not supported on iOS +} + +- (void)create:(NSDictionary *) data + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject { + // Not supported on iOS out of the box +} + +- (void)delete:(NSDictionary *)options + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject { + [_ref deleteDocumentWithCompletion:^(NSError * _Nullable error) { + [RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject]; + }]; +} + +- (void)get:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject { + [_ref getDocumentWithCompletion:^(FIRDocumentSnapshot * _Nullable snapshot, NSError * _Nullable error) { + if (error) { + [RNFirebaseFirestore promiseRejectException:reject error:error]; + } else { + NSDictionary *data = [RNFirebaseFirestoreDocumentReference snapshotToDictionary:snapshot]; + resolve(data); + } + }]; +} + +- (void)set:(NSDictionary *) data + options:(NSDictionary *) options + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject { + if (options && options[@"merge"]) { + [_ref setData:data options:[FIRSetOptions merge] completion:^(NSError * _Nullable error) { + [RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject]; + }]; + } else { + [_ref setData:data completion:^(NSError * _Nullable error) { + [RNFirebaseFirestoreDocumentReference handleWriteResponse:error resolver:resolve rejecter:reject]; + }]; + } +} + +- (void)update:(NSDictionary *) data + resolver:(RCTPromiseResolveBlock) resolve + rejecter:(RCTPromiseRejectBlock) reject { + [_ref updateData:data 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 { + // Missing fields from web SDK + // writeTime + resolve(@{}); + } +} + ++ (NSDictionary *)snapshotToDictionary:(FIRDocumentSnapshot *)documentSnapshot { + NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init]; + [snapshot setValue:documentSnapshot.reference.path forKey:@"path"]; + [snapshot setValue:documentSnapshot.data forKey:@"data"]; + // Missing fields from web SDK + // createTime + // readTime + // updateTime + + return snapshot; +} + +#endif + +@end + + diff --git a/lib/modules/firestore/Query.js b/lib/modules/firestore/Query.js index 3d88ca3b..4ec64817 100644 --- a/lib/modules/firestore/Query.js +++ b/lib/modules/firestore/Query.js @@ -13,7 +13,6 @@ const DIRECTIONS = { DESC: 'DESCENDING', desc: 'DESCENDING', }; -const DOCUMENT_NAME_FIELD = '__name__'; const OPERATORS = { '=': 'EQUAL', diff --git a/tests/ios/GoogleService-Info.plist b/tests/ios/GoogleService-Info.plist index 30da4b8d..079738ad 100644 --- a/tests/ios/GoogleService-Info.plist +++ b/tests/ios/GoogleService-Info.plist @@ -7,34 +7,34 @@ AD_UNIT_ID_FOR_INTERSTITIAL_TEST ca-app-pub-3940256099942544/4411468910 CLIENT_ID - 305229645282-22imndi01abc2p6esgtu1i1m9mqrd0ib.apps.googleusercontent.com + 17067372085-h95lq6v2fbjdl2i1f6pl26iurah37i8p.apps.googleusercontent.com REVERSED_CLIENT_ID - com.googleusercontent.apps.305229645282-22imndi01abc2p6esgtu1i1m9mqrd0ib + com.googleusercontent.apps.17067372085-h95lq6v2fbjdl2i1f6pl26iurah37i8p API_KEY - AIzaSyAcdVLG5dRzA1ck_fa_xd4Z0cY7cga7S5A + AIzaSyC8ZEruBCvS_6woF8_l07ILy1eXaD6J4vQ GCM_SENDER_ID - 305229645282 + 17067372085 PLIST_VERSION 1 BUNDLE_ID com.invertase.ReactNativeFirebaseDemo PROJECT_ID - rnfirebase-b9ad4 + rnfirebase STORAGE_BUCKET - rnfirebase-b9ad4.appspot.com + rnfirebase.appspot.com IS_ADS_ENABLED - + IS_ANALYTICS_ENABLED - + IS_APPINVITE_ENABLED - + IS_GCM_ENABLED - + IS_SIGNIN_ENABLED - + GOOGLE_APP_ID - 1:305229645282:ios:7b45748cb1117d2d + 1:17067372085:ios:7b45748cb1117d2d DATABASE_URL - https://rnfirebase-b9ad4.firebaseio.com + https://rnfirebase-5579a.firebaseio.com - \ No newline at end of file + diff --git a/tests/ios/Podfile b/tests/ios/Podfile index 2f3300b0..af1fb7a7 100644 --- a/tests/ios/Podfile +++ b/tests/ios/Podfile @@ -19,6 +19,7 @@ target 'ReactNativeFirebaseDemo' do pod 'Firebase/Crash' pod 'Firebase/Database' pod 'Firebase/DynamicLinks' + pod 'Firestore', :podspec => 'https://storage.googleapis.com/firebase-preview-drop/ios/firestore/0.7.0/Firestore.podspec.json' pod 'Firebase/Messaging' pod 'Firebase/RemoteConfig' pod 'Firebase/Storage' diff --git a/tests/ios/Podfile.lock b/tests/ios/Podfile.lock index ebb97db9..7d2c3986 100644 --- a/tests/ios/Podfile.lock +++ b/tests/ios/Podfile.lock @@ -1,4 +1,10 @@ PODS: + - BoringSSL (8.2): + - BoringSSL/Implementation (= 8.2) + - BoringSSL/Interface (= 8.2) + - BoringSSL/Implementation (8.2): + - BoringSSL/Interface (= 8.2) + - BoringSSL/Interface (8.2) - Firebase/AdMob (4.1.0): - Firebase/Core - Google-Mobile-Ads-SDK (= 7.22.0) @@ -77,6 +83,13 @@ PODS: - FirebaseAnalytics (~> 4.0) - FirebaseCore (~> 4.0) - GTMSessionFetcher/Core (~> 1.1) + - Firestore (0.7.0): + - FirebaseAnalytics (~> 4.0) + - FirebaseAuth (~> 4.1) + - FirebaseCore (~> 4.0) + - gRPC-ProtoRPC (~> 1.0) + - leveldb-library (~> 1.18) + - Protobuf (~> 3.1) - Google-Mobile-Ads-SDK (7.22.0) - GoogleToolboxForMac/DebugUtils (2.1.1): - GoogleToolboxForMac/Defines (= 2.1.1) @@ -90,6 +103,22 @@ PODS: - GoogleToolboxForMac/Defines (= 2.1.1) - GoogleToolboxForMac/NSString+URLArguments (= 2.1.1) - GoogleToolboxForMac/NSString+URLArguments (2.1.1) + - gRPC (1.4.2): + - gRPC-Core (= 1.4.2) + - gRPC-RxLibrary (= 1.4.2) + - gRPC-Core (1.4.2): + - gRPC-Core/Implementation (= 1.4.2) + - gRPC-Core/Interface (= 1.4.2) + - gRPC-Core/Implementation (1.4.2): + - BoringSSL (~> 8.0) + - gRPC-Core/Interface (= 1.4.2) + - nanopb (~> 0.3) + - gRPC-Core/Interface (1.4.2) + - gRPC-ProtoRPC (1.4.2): + - gRPC (= 1.4.2) + - gRPC-RxLibrary (= 1.4.2) + - Protobuf (~> 3.0) + - gRPC-RxLibrary (1.4.2) - GTMSessionFetcher/Core (1.1.11) - leveldb-library (1.18.3) - nanopb (0.3.8): @@ -121,11 +150,14 @@ DEPENDENCIES: - Firebase/Performance - Firebase/RemoteConfig - Firebase/Storage + - Firestore (from `https://storage.googleapis.com/firebase-preview-drop/ios/firestore/0.7.0/Firestore.podspec.json`) - React (from `../node_modules/react-native`) - RNFirebase (from `./../../`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) EXTERNAL SOURCES: + Firestore: + :podspec: https://storage.googleapis.com/firebase-preview-drop/ios/firestore/0.7.0/Firestore.podspec.json React: :path: "../node_modules/react-native" RNFirebase: @@ -134,6 +166,7 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: + BoringSSL: 4135ae556ee2b82ee85477c39ba917a3dd5424ba Firebase: ebebf41db7f10e0c7668b6eaaa857fbe599aa478 FirebaseAnalytics: 76f754d37ca5b04f36856729b6af3ca0152d1069 FirebaseAuth: 8d1d2389cf82f891048d6d50d27d044f55ae09a6 @@ -146,8 +179,13 @@ SPEC CHECKSUMS: FirebasePerformance: 36bdb0500213b459ae991766801d5dc5399ff231 FirebaseRemoteConfig: 5b3e3301ef2f237b1b588e8ef3211b5a22e9e15d FirebaseStorage: 661fc1f8d4131891d256b62e82a45ace8b3f0c3b + Firestore: 80f352a0b5260500b11d7e4626b81a19d4eba312 Google-Mobile-Ads-SDK: 1bdf1a4244d0553b1840239874c209c01aef055f GoogleToolboxForMac: 8e329f1b599f2512c6b10676d45736bcc2cbbeb0 + gRPC: 74b57d3c8a9366e09493828e0a1d27f6d69a79fd + gRPC-Core: 642d29e59e5490374622b0629c2dd1c4c111775c + gRPC-ProtoRPC: 675ef3d484c06967ed2a5f5ee0e510a3756f755e + gRPC-RxLibrary: 7a25c5c25282669a82d1783d7e8a036f53e8ef27 GTMSessionFetcher: 5ad62e8200fa00ed011fe5e08d27fef72c5b1429 leveldb-library: 10fb39c39e243db4af1828441162405bbcec1404 nanopb: 5601e6bca2dbf1ed831b519092ec110f66982ca3 @@ -156,6 +194,6 @@ SPEC CHECKSUMS: RNFirebase: 60be8c01b94551a12e7be5431189e8ee8cefcdd3 Yoga: c90474ca3ec1edba44c97b6c381f03e222a9e287 -PODFILE CHECKSUM: 46b6a553f3c9fd264b449806b373d33b4af518b5 +PODFILE CHECKSUM: 49e66d8a1599e426396a3ba88a24baacc7f5423c COCOAPODS: 1.2.1 diff --git a/tests/src/main.firestore.js b/tests/src/main.firestore.js new file mode 100644 index 00000000..e3ce91fe --- /dev/null +++ b/tests/src/main.firestore.js @@ -0,0 +1,106 @@ +import React, { Component } from 'react'; +import { Text, View } from 'react-native'; +import fb from './firebase'; + +global.Promise = require('bluebird'); + +const firebase = fb.native; + + +function bootstrap() { + // Remove logging on production + if (!__DEV__) { + console.log = () => { + }; + console.warn = () => { + }; + console.error = () => { + }; + console.disableYellowBox = true; + } + + class Root extends Component { + + async componentDidMount() { + console.log(`Starting`); + const db = firebase.firestore(); + const docRef = await db.collection('chris').add({ first: 'Ada', last: 'Lovelace', born: 1815 }); + console.log(`Document written with ID: ${docRef.id}`); + const docRef2 = await db.collection('chris').add({ first: 'Alan', middle: 'Mathison', last: 'Turing', born: 1912 }); + console.log(`Document written with ID: ${docRef2.id}`); + await db.collection('chris').doc('manual').set({ first: 'Manual', last: 'Man', born: 1234 }); + console.log('Manual document set'); + await db.collection('chris').doc().set({ first: 'Auto', last: 'Man', born: 2000 }); + console.log('Auto document set'); + + const docRefT = db.doc(docRef.path); + const docRefS = await docRefT.get(); + console.log(`Should be the same as first written ID: ${docRefT.id}`, docRefS.data()); + + await docRefT.set({ empty: true }); + const docRefS2 = await docRefT.get(); + console.log(`Should have empty only: ${docRefT.id}`, docRefS2.data()); + + await docRefT.set({ first: 'Ada', last: 'Lovelace', born: 1815 }, { merge: true }); + const docRefS3 = await docRefT.get(); + console.log(`Should have everything plus empty: ${docRefT.id}`, docRefS3.data()); + + await docRefT.update({ first: 'AdaUpdated' }); + const docRefS4 = await docRefT.get(); + console.log(`Should have updated firstname: ${docRefT.id}`, docRefS4.data()); + + const docs = await db.collection('chris').get(); + const tasks = []; + docs.forEach((doc) => { + console.log(`Cleaning up ${doc.id}`, doc.data()); + tasks.push(doc.ref.delete()); + }); + Promise.all(tasks); + console.log('Finished cleaning collection'); + + const nycRef = db.collection('chris').doc('NYC'); + const sfRef = db.collection('chris').doc('SF'); + + await db.batch() + .set(nycRef, { name: 'New York City' }) + .set(sfRef, { name: 'San Francisco' }) + .commit(); + + const docs2 = await db.collection('chris').get(); + docs2.forEach((doc) => { + console.log(`Got ${doc.id}`, doc.data()); + }); + + await db.batch() + .update(nycRef, { population: 1000000 }) + .update(sfRef, { name: 'San Fran' }) + .commit(); + const docs3 = await db.collection('chris').get(); + docs3.forEach((doc) => { + console.log(`Got ${doc.id}`, doc.data()); + }); + + await db.batch() + .delete(nycRef) + .delete(sfRef) + .commit(); + const docs4 = await db.collection('chris').get(); + docs4.forEach((doc) => { + console.log(`Got ${doc.id}`, doc.data()); + }); + console.log('Finished'); + } + + render() { + return ( + + Check console logs + + ); + } + } + + return Root; +} + +export default bootstrap();