2
0
mirror of synced 2025-01-11 06:35:51 +00:00

[database][ios][wip] Replicate Android changes on iOS

This commit is contained in:
Chris Bianca 2017-08-02 16:51:00 +01:00
parent 3504715a53
commit a785b050db
6 changed files with 413 additions and 244 deletions

View File

@ -74,6 +74,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
*/ */
@ReactMethod @ReactMethod
public void keepSynced(String appName, String path, Boolean state) { public void keepSynced(String appName, String path, Boolean state) {
// TODO: Needs to take into account modifiers as well as just the path
getReferenceForAppPath(appName, path).keepSynced(state); getReferenceForAppPath(appName, path).keepSynced(state);
} }
@ -579,7 +580,7 @@ public class RNFirebaseDatabase extends ReactContextBaseJavaModule {
break; break;
default: default:
code = getCodeWithService(service, "unknown"); code = getCodeWithService(service, "unknown");
message = getMessageWithService("An unknown error occurred", service, code); message = getMessageWithService("An unknown error occurred.", service, code);
} }
errorMap.putString("code", code); errorMap.putString("code", code);

View File

@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
8300A7AE1F31E143001B16AB /* RNFirebaseDatabaseReference.m in Sources */ = {isa = PBXBuildFile; fileRef = 8300A7AD1F31E143001B16AB /* RNFirebaseDatabaseReference.m */; };
839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */; }; 839D916C1EF3E20B0077C7C8 /* RNFirebaseAdMob.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */; };
839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91511EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.m */; }; 839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91511EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.m */; };
839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91531EF3E20A0077C7C8 /* RNFirebaseAdMobRewardedVideo.m */; }; 839D916E1EF3E20B0077C7C8 /* RNFirebaseAdMobRewardedVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D91531EF3E20A0077C7C8 /* RNFirebaseAdMobRewardedVideo.m */; };
@ -35,6 +36,8 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRNFirebase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFirebase.a; sourceTree = BUILT_PRODUCTS_DIR; }; 134814201AA4EA6300B7C361 /* libRNFirebase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNFirebase.a; sourceTree = BUILT_PRODUCTS_DIR; };
8300A7AC1F31E143001B16AB /* RNFirebaseDatabaseReference.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseDatabaseReference.h; sourceTree = "<group>"; };
8300A7AD1F31E143001B16AB /* RNFirebaseDatabaseReference.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseDatabaseReference.m; sourceTree = "<group>"; };
839D914E1EF3E20A0077C7C8 /* RNFirebaseAdMob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMob.h; sourceTree = "<group>"; }; 839D914E1EF3E20A0077C7C8 /* RNFirebaseAdMob.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMob.h; sourceTree = "<group>"; };
839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMob.m; sourceTree = "<group>"; }; 839D914F1EF3E20A0077C7C8 /* RNFirebaseAdMob.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNFirebaseAdMob.m; sourceTree = "<group>"; };
839D91501EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobInterstitial.h; sourceTree = "<group>"; }; 839D91501EF3E20A0077C7C8 /* RNFirebaseAdMobInterstitial.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNFirebaseAdMobInterstitial.h; sourceTree = "<group>"; };
@ -157,6 +160,8 @@
839D91601EF3E20A0077C7C8 /* database */ = { 839D91601EF3E20A0077C7C8 /* database */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
8300A7AC1F31E143001B16AB /* RNFirebaseDatabaseReference.h */,
8300A7AD1F31E143001B16AB /* RNFirebaseDatabaseReference.m */,
839D91611EF3E20A0077C7C8 /* RNFirebaseDatabase.h */, 839D91611EF3E20A0077C7C8 /* RNFirebaseDatabase.h */,
839D91621EF3E20A0077C7C8 /* RNFirebaseDatabase.m */, 839D91621EF3E20A0077C7C8 /* RNFirebaseDatabase.m */,
); );
@ -262,6 +267,7 @@
839D91741EF3E20B0077C7C8 /* RNFirebaseMessaging.m in Sources */, 839D91741EF3E20B0077C7C8 /* RNFirebaseMessaging.m in Sources */,
839D91751EF3E20B0077C7C8 /* RNFirebasePerformance.m in Sources */, 839D91751EF3E20B0077C7C8 /* RNFirebasePerformance.m in Sources */,
839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */, 839D916D1EF3E20B0077C7C8 /* RNFirebaseAdMobInterstitial.m in Sources */,
8300A7AE1F31E143001B16AB /* RNFirebaseDatabaseReference.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -4,12 +4,19 @@
#import <React/RCTBridgeModule.h> #import <React/RCTBridgeModule.h>
#if __has_include(<FirebaseDatabase/FIRDatabase.h>) #if __has_include(<FirebaseDatabase/FIRDatabase.h>)
#import "Firebase.h"
#import <React/RCTEventEmitter.h> #import <React/RCTEventEmitter.h>
@interface RNFirebaseDatabase : RCTEventEmitter<RCTBridgeModule> {} @interface RNFirebaseDatabase : RCTEventEmitter<RCTBridgeModule> {}
@property NSMutableDictionary *dbReferences; @property NSMutableDictionary *dbReferences;
@property NSMutableDictionary *transactions; @property NSMutableDictionary *transactions;
@property dispatch_queue_t transactionQueue; @property dispatch_queue_t transactionQueue;
+ (void)handlePromise:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock) reject databaseError:(NSError *)databaseError;
+ (FIRDatabase *)getDatabaseForApp:(NSString*)appName;
+ (NSString *) getMessageWithService:(NSString *) message service:(NSString *) service fullCode:(NSString *) fullCode;
+ (NSString *) getCodeWithService:(NSString *) service code:(NSString *) code;
@end @end
#else #else

View File

@ -3,7 +3,6 @@
#if __has_include(<FirebaseDatabase/FIRDatabase.h>) #if __has_include(<FirebaseDatabase/FIRDatabase.h>)
#import "RNFirebaseDatabaseReference.h" #import "RNFirebaseDatabaseReference.h"
#import "RNFirebaseEvents.h" #import "RNFirebaseEvents.h"
#import "Firebase.h"
@implementation RNFirebaseDatabase @implementation RNFirebaseDatabase
RCT_EXPORT_MODULE(); RCT_EXPORT_MODULE();
@ -18,32 +17,70 @@ RCT_EXPORT_MODULE();
return self; return self;
} }
- (void)sendTransactionEvent:(NSString *)type body:(id)body { RCT_EXPORT_METHOD(goOnline:(NSString *) appName) {
@try { [[RNFirebaseDatabase getDatabaseForApp:appName] goOnline];
[self sendEventWithName:type body:body];
} @catch (NSException *err) {
NSLog(@"An error occurred in sendJSEvent: %@", [err debugDescription]);
NSLog(@"Tried to send: %@ with %@", type, body);
}
} }
RCT_EXPORT_METHOD(startTransaction: RCT_EXPORT_METHOD(goOffline:(NSString *) appName) {
(NSString *) path [[RNFirebaseDatabase getDatabaseForApp:appName] goOffline];
identifier: }
(NSString *) identifier
applyLocally: RCT_EXPORT_METHOD(setPersistence:(NSString *) appName
(BOOL) applyLocally) { state:(BOOL) state) {
[RNFirebaseDatabase getDatabaseForApp:appName].persistenceEnabled = state;
}
RCT_EXPORT_METHOD(keepSynced:(NSString *) appName
path:(NSString *) path
state:(BOOL) state) {
[[self getReferenceForAppPath:appName path:path] keepSynced:state];
}
RCT_EXPORT_METHOD(transactionTryCommit:(NSString *) appName
transactionId:(nonnull NSNumber *) transactionId
updates:(NSDictionary *) updates) {
__block NSMutableDictionary *transactionState;
dispatch_sync(_transactionQueue, ^{
transactionState = _transactions[transactionId];
});
if (!transactionState) {
NSLog(@"tryCommitTransaction for unknown ID %@", transactionId);
return;
}
dispatch_semaphore_t sema = [transactionState valueForKey:@"semaphore"];
BOOL abort = [[updates valueForKey:@"abort"] boolValue];
if (abort) {
[transactionState setValue:@true forKey:@"abort"];
} else {
id newValue = [updates valueForKey:@"value"];
[transactionState setValue:newValue forKey:@"value"];
}
dispatch_semaphore_signal(sema);
}
RCT_EXPORT_METHOD(transactionStart:(NSString *) appName
path:(NSString *) path
transactionId:(nonnull NSNumber *) transactionId
applyLocally:(BOOL) applyLocally) {
dispatch_async(_transactionQueue, ^{ dispatch_async(_transactionQueue, ^{
NSMutableDictionary *transactionState = [NSMutableDictionary new]; NSMutableDictionary *transactionState = [NSMutableDictionary new];
dispatch_semaphore_t sema = dispatch_semaphore_create(0); dispatch_semaphore_t sema = dispatch_semaphore_create(0);
transactionState[@"semaphore"] = sema; transactionState[@"semaphore"] = sema;
FIRDatabaseReference *ref = [self getPathRef:path]; FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
[ref runTransactionBlock:^FIRTransactionResult * _Nonnull(FIRMutableData * [ref runTransactionBlock:^FIRTransactionResult * _Nonnull(FIRMutableData *
_Nonnull currentData) { _Nonnull currentData) {
dispatch_barrier_async(_transactionQueue, ^{ dispatch_barrier_async(_transactionQueue, ^{
[_transactions setValue:transactionState forKey:identifier]; [_transactions setValue:transactionState forKey:transactionId];
[self sendTransactionEvent:DATABASE_TRANSACTION_EVENT body:@{@"id": identifier, @"type": @"update", @"value": currentData.value}]; NSDictionary *updateMap = [self createTransactionUpdateMap:appName transactionId:transactionId updatesData:currentData];
[self sendEventWithName:DATABASE_TRANSACTION_EVENT body:updateMap];
}); });
// wait for the js event handler to call tryCommitTransaction // wait for the js event handler to call tryCommitTransaction
@ -57,7 +94,7 @@ RCT_EXPORT_METHOD(startTransaction:
id value = [transactionState valueForKey:@"value"]; id value = [transactionState valueForKey:@"value"];
dispatch_barrier_async(_transactionQueue, ^{ dispatch_barrier_async(_transactionQueue, ^{
[_transactions removeObjectForKey:identifier]; [_transactions removeObjectForKey:transactionId];
}); });
if (abort) { if (abort) {
@ -69,155 +106,285 @@ RCT_EXPORT_METHOD(startTransaction:
} }
andCompletionBlock: andCompletionBlock:
^(NSError *_Nullable databaseError, BOOL committed, FIRDataSnapshot *_Nullable snapshot) { ^(NSError *_Nullable databaseError, BOOL committed, FIRDataSnapshot *_Nullable snapshot) {
if (databaseError != nil) { NSDictionary *resultMap = [self createTransactionResultMap:appName transactionId:transactionId error:databaseError committed:committed snapshot:snapshot];
[self sendTransactionEvent:DATABASE_TRANSACTION_EVENT body:@{@"id": identifier, @"type": @"error", @"code": @([databaseError code]), @"message": [databaseError description]}]; [self sendEventWithName:DATABASE_TRANSACTION_EVENT body:resultMap];
} else {
[self sendTransactionEvent:DATABASE_TRANSACTION_EVENT body:@{@"id": identifier, @"type": @"complete", @"committed": @(committed), @"snapshot": [RNFirebaseDatabaseReference snapshotToDict:snapshot],}];
}
} }
withLocalEvents: withLocalEvents:
applyLocally]; applyLocally];
}); });
} }
RCT_EXPORT_METHOD(tryCommitTransaction: RCT_EXPORT_METHOD(onDisconnectSet:(NSString *) appName
(NSString *) identifier path:(NSString *) path
withData: props:(NSDictionary *) props
(NSDictionary *) data) { resolver:(RCTPromiseResolveBlock) resolve
__block NSMutableDictionary *transactionState; rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
dispatch_sync(_transactionQueue, ^{ [ref onDisconnectSetValue:props[@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
transactionState = _transactions[identifier]; [RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
});
if (!transactionState) {
NSLog(@"tryCommitTransaction for unknown ID %@", identifier);
return;
}
dispatch_semaphore_t sema = [transactionState valueForKey:@"semaphore"];
BOOL abort = [[data valueForKey:@"abort"] boolValue];
if (abort) {
[transactionState setValue:@true forKey:@"abort"];
} else {
id newValue = [data valueForKey:@"value"];
[transactionState setValue:newValue forKey:@"value"];
}
dispatch_semaphore_signal(sema);
}
RCT_EXPORT_METHOD(enablePersistence:
(BOOL) enable
callback:
(RCTResponseSenderBlock) callback) {
BOOL isEnabled = [FIRDatabase database].persistenceEnabled;
if (isEnabled != enable) {
@try {
[FIRDatabase database].persistenceEnabled = enable;
} @catch (NSException *exception) {
// do nothing - for RN packager reloads
}
}
callback(@[[NSNull null], @{@"result": @"success"}]);
}
RCT_EXPORT_METHOD(keepSynced:
(NSString *) path
withEnable:
(BOOL) enable
callback:
(RCTResponseSenderBlock) callback) {
FIRDatabaseReference *ref = [self getPathRef:path];
[ref keepSynced:enable];
callback(@[[NSNull null], @{@"status": @"success", @"path": path}]);
}
RCT_EXPORT_METHOD(set:
(NSString *) path
data:
(NSDictionary *) data
callback:
(RCTResponseSenderBlock) callback) {
FIRDatabaseReference *ref = [self getPathRef:path];
[ref setValue:[data valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[self handleCallback:@"set" callback:callback databaseError:error];
}]; }];
} }
RCT_EXPORT_METHOD(priority:(NSString *) path RCT_EXPORT_METHOD(onDisconnectUpdate:(NSString *) appName
priorityData:(NSDictionary *) priorityData path:(NSString *) path
callback:(RCTResponseSenderBlock) callback) { props:(NSDictionary *) props
FIRDatabaseReference *ref = [self getPathRef:path]; resolver:(RCTPromiseResolveBlock) resolve
[ref setPriority:[priorityData valueForKey:@"value"] withCompletionBlock:^(NSError * _Nullable error, FIRDatabaseReference * _Nonnull ref) { rejecter:(RCTPromiseRejectBlock) reject) {
[self handleCallback:@"priority" callback:callback databaseError:error]; FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
[ref onDisconnectUpdateChildValues:props withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}]; }];
} }
RCT_EXPORT_METHOD(withPriority:(NSString *) path RCT_EXPORT_METHOD(onDisconnectRemove:(NSString *) appName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
[ref onDisconnectRemoveValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectCancel:(NSString *) appName
path:(NSString *) path
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
[ref cancelDisconnectOperationsWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(set:(NSString *) appName
path:(NSString *) path
props:(NSDictionary *) props
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
[ref setValue:[props valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(setPriority:(NSString *) appName
path:(NSString *) path
priority:(NSDictionary *) priority
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
[ref setPriority:[priority valueForKey:@"value"] withCompletionBlock:^(NSError * _Nullable error, FIRDatabaseReference * _Nonnull ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}];
}
RCT_EXPORT_METHOD(setWithPriority:(NSString *) appName
path:(NSString *) path
data:(NSDictionary *) data data:(NSDictionary *) data
priorityData:(NSDictionary *) priorityData priority:(NSDictionary *) priority
callback:(RCTResponseSenderBlock) callback) { resolver:(RCTPromiseResolveBlock) resolve
FIRDatabaseReference *ref = [self getPathRef:path]; rejecter:(RCTPromiseRejectBlock) reject) {
[ref setValue:[data valueForKey:@"value"] andPriority:[priorityData valueForKey:@"value"] withCompletionBlock:^(NSError * _Nullable error, FIRDatabaseReference * _Nonnull ref) { FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
[self handleCallback:@"withPriority" callback:callback databaseError:error]; [ref setValue:[data valueForKey:@"value"] andPriority:[priority valueForKey:@"value"] withCompletionBlock:^(NSError * _Nullable error, FIRDatabaseReference * _Nonnull ref) {
[RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}]; }];
} }
RCT_EXPORT_METHOD(update: RCT_EXPORT_METHOD(update:(NSString *) appName
(NSString *) path path:(NSString *) path
value: props:(NSDictionary *) props
(NSDictionary *) value resolver:(RCTPromiseResolveBlock) resolve
callback: rejecter:(RCTPromiseRejectBlock) reject) {
(RCTResponseSenderBlock) callback) { FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
FIRDatabaseReference *ref = [self getPathRef:path]; [ref updateChildValues:props withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[ref updateChildValues:value withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) { [RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
[self handleCallback:@"update" callback:callback databaseError:error];
}]; }];
} }
RCT_EXPORT_METHOD(remove: RCT_EXPORT_METHOD(remove:(NSString *) appName
(NSString *) path path:(NSString *) path
callback: resolver:(RCTPromiseResolveBlock) resolve
(RCTResponseSenderBlock) callback) { rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *ref = [self getPathRef:path]; FIRDatabaseReference *ref = [self getReferenceForAppPath:appName path:path];
[ref removeValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) { [ref removeValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[self handleCallback:@"remove" callback:callback databaseError:error]; [RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}]; }];
} }
RCT_EXPORT_METHOD(push: RCT_EXPORT_METHOD(once:(NSString *) appName
(NSString *) path refId:(nonnull NSNumber *) refId
data: path:(NSString *) path
(NSDictionary *) data modifiers:(NSArray *) modifiers
callback: eventName:(NSString *) eventName
(RCTResponseSenderBlock) callback) { resolver:(RCTPromiseResolveBlock) resolve
FIRDatabaseReference *ref = [self getPathRef:path]; rejecter:(RCTPromiseRejectBlock) reject) {
FIRDatabaseReference *newRef = [ref childByAutoId]; RNFirebaseDatabaseReference *ref = [self getInternalReferenceForApp:appName refId:refId path:path modifiers:modifiers keep:false];
[ref addSingleEventHandler:eventName resolver:resolve rejecter:reject];
}
NSURL *url = [NSURL URLWithString:newRef.URL]; /*
NSString *newPath = [url path]; * INTERNALS/UTILS
*/
+ (void) handlePromise:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject
databaseError:(NSError *) databaseError {
if (databaseError != nil) {
NSDictionary *jsError = [RNFirebaseDatabase getJSError:databaseError];
reject([jsError valueForKey:@"code"], [jsError valueForKey:@"message"], databaseError);
} else {
resolve([NSNull null]);
}
}
if ([data count] > 0) { + (FIRDatabase *) getDatabaseForApp:(NSString *) appName {
[newRef setValue:[data valueForKey:@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) { FIRApp *app = [FIRApp appNamed:appName];
return [FIRDatabase databaseForApp:app];
}
- (FIRDatabaseReference *) getReferenceForAppPath:(NSString *) appName
path:(NSString *) path {
return [[RNFirebaseDatabase getDatabaseForApp:appName] referenceWithPath:path];
}
- (RNFirebaseDatabaseReference *) getInternalReferenceForApp:(NSString *) appName
refId:(NSNumber *) refId
path:(NSString *) path
modifiers:(NSArray *) modifiers
keep:(BOOL) keep {
RNFirebaseDatabaseReference *ref = _dbReferences[refId];
if (ref == nil) {
ref = [[RNFirebaseDatabaseReference alloc] initWithPathAndModifiers:self app:appName refId:refId refPath:path modifiers:modifiers];
if (keep) {
_dbReferences[refId] = ref;
}
}
return ref;
}
// TODO: Move to error util for use in other modules
+ (NSString *) getMessageWithService:(NSString *) message
service:(NSString *) service
fullCode:(NSString *) fullCode {
return [NSString stringWithFormat:@"%@: %@ (%@).", service, message, [fullCode lowercaseString]];
}
+ (NSString *) getCodeWithService:(NSString *) service
code:(NSString *) code {
return [NSString stringWithFormat:@"%@/%@", [service uppercaseString], [code uppercaseString]];
}
+ (NSDictionary *) getJSError:(NSError *) nativeError {
NSMutableDictionary *errorMap = [[NSMutableDictionary alloc] init];
[errorMap setValue:@(nativeError.code) forKey:@"nativeErrorCode"];
[errorMap setValue:[nativeError localizedDescription] forKey:@"nativeErrorMessage"];
NSString *code;
NSString *message;
NSString *service = @"Database";
switch (nativeError.code) {
// iOS confirmed codes
case 1: // -3 on Android
code = [RNFirebaseDatabase getCodeWithService:service code:@"permission-denied"];
message = [RNFirebaseDatabase getMessageWithService:@"Client doesn't have permission to access the desired data." service:service fullCode:code];
break;
case 2: // -10 on Android
code = [RNFirebaseDatabase getCodeWithService:service code:@"unavailable"];
message = [RNFirebaseDatabase getMessageWithService:@"The service is unavailable." service:service fullCode:code];
break;
case 3: // -25 on Android
code = [RNFirebaseDatabase getCodeWithService:service code:@"write-cancelled"];
message = [RNFirebaseDatabase getMessageWithService:@"The write was canceled by the user." service:service fullCode:code];
break;
// TODO: Missing iOS equivalent codes
case -1:
code = [RNFirebaseDatabase getCodeWithService:service code:@"data-stale"];
message = [RNFirebaseDatabase getMessageWithService:@"The transaction needs to be run again with current data." service:service fullCode:code];
break;
case -2:
code = [RNFirebaseDatabase getCodeWithService:service code:@"failure"];
message = [RNFirebaseDatabase getMessageWithService:@"The server indicated that this operation failed." service:service fullCode:code];
break;
case -4:
code = [RNFirebaseDatabase getCodeWithService:service code:@"disconnected"];
message = [RNFirebaseDatabase getMessageWithService:@"The operation had to be aborted due to a network disconnect." service:service fullCode:code];
break;
case -6:
code = [RNFirebaseDatabase getCodeWithService:service code:@"expired-token"];
message = [RNFirebaseDatabase getMessageWithService:@"The supplied auth token has expired." service:service fullCode:code];
break;
case -7:
code = [RNFirebaseDatabase getCodeWithService:service code:@"invalid-token"];
message = [RNFirebaseDatabase getMessageWithService:@"The supplied auth token was invalid." service:service fullCode:code];
break;
case -8:
code = [RNFirebaseDatabase getCodeWithService:service code:@"max-retries"];
message = [RNFirebaseDatabase getMessageWithService:@"The transaction had too many retries." service:service fullCode:code];
break;
case -9:
code = [RNFirebaseDatabase getCodeWithService:service code:@"overridden-by-set"];
message = [RNFirebaseDatabase getMessageWithService:@"The transaction was overridden by a subsequent set." service:service fullCode:code];
break;
case -11:
code = [RNFirebaseDatabase getCodeWithService:service code:@"user-code-exception"];
message = [RNFirebaseDatabase getMessageWithService:@"User code called from the Firebase Database runloop threw an exception." service:service fullCode:code];
break;
case -24:
code = [RNFirebaseDatabase getCodeWithService:service code:@"network-error"];
message = [RNFirebaseDatabase getMessageWithService:@"The operation could not be performed due to a network error." service:service fullCode:code];
break;
default:
code = [RNFirebaseDatabase getCodeWithService:service code:@"unknown"];
message = [RNFirebaseDatabase getMessageWithService:@"An unknown error occurred." service:service fullCode:code];
break;
}
[errorMap setValue:code forKey:@"code"];
[errorMap setValue:message forKey:@"message"];
return errorMap;
}
- (NSDictionary *) createTransactionUpdateMap:(NSString *) appName
transactionId:(NSNumber *) transactionId
updatesData:(FIRMutableData *) updatesData {
NSMutableDictionary *updatesMap = [[NSMutableDictionary alloc] init];
[updatesMap setValue:transactionId forKey:@"id"];
[updatesMap setValue:@"update" forKey:@"type"];
[updatesMap setValue:appName forKey:@"appName"];
[updatesMap setValue:updatesData.value forKey:@"value"];
return updatesMap;
}
- (NSDictionary *) createTransactionResultMap:(NSString *) appName
transactionId:(NSNumber *) transactionId
error:(NSError *) error
committed:(BOOL) committed
snapshot:(FIRDataSnapshot *) snapshot {
NSMutableDictionary *resultMap = [[NSMutableDictionary alloc] init];
[resultMap setValue:transactionId forKey:@"id"];
[resultMap setValue:appName forKey:@"appName"];
// TODO: no timeout on iOS
[resultMap setValue:@(committed) forKey:@"committed"];
// TODO: no interrupted on iOS
if (error != nil) { if (error != nil) {
// Error handling [resultMap setValue:@"error" forKey:@"type"];
NSDictionary *evt = @{@"code": @([error code]), @"details": [error debugDescription], @"message": [error localizedDescription], @"description": [error description]}; [resultMap setValue:[RNFirebaseDatabase getJSError:error] forKey:@"error"];
// TODO: timeout error on iOS
callback(@[evt]);
} else { } else {
callback(@[[NSNull null], @{@"status": @"success", @"ref": newPath}]); [resultMap setValue:@"complete" forKey:@"type"];
} [resultMap setValue:[RNFirebaseDatabaseReference snapshotToDict:snapshot] forKey:@"snapshot"];
}];
} else {
callback(@[[NSNull null], @{@"status": @"success", @"ref": newPath}]);
} }
return resultMap;
} }
/* TODO
RCT_EXPORT_METHOD(on: RCT_EXPORT_METHOD(on:
(nonnull (nonnull
NSNumber *) refId NSNumber *) refId
@ -231,17 +398,6 @@ RCT_EXPORT_METHOD(on:
callback(@[[NSNull null], @{@"status": @"success", @"refId": refId, @"handle": path}]); callback(@[[NSNull null], @{@"status": @"success", @"refId": refId, @"handle": path}]);
} }
RCT_EXPORT_METHOD(once:
(nonnull
NSNumber *) refId
path:(NSString *) path
modifiers:(NSArray *) modifiers
eventName:(NSString *) eventName
callback:(RCTResponseSenderBlock) callback) {
RNFirebaseDatabaseReference *ref = [self getDBHandle:refId path:path modifiers:modifiers];
[ref addSingleEventHandler:eventName callback:callback];
}
RCT_EXPORT_METHOD(off: RCT_EXPORT_METHOD(off:
(nonnull (nonnull
NSNumber *) refId NSNumber *) refId
@ -261,60 +417,6 @@ RCT_EXPORT_METHOD(off:
callback(@[[NSNull null], @{@"status": @"success", @"refId": refId,}]); callback(@[[NSNull null], @{@"status": @"success", @"refId": refId,}]);
} }
// On disconnect
RCT_EXPORT_METHOD(onDisconnectSet:
(NSString *) path
props:
(NSDictionary *) props
callback:
(RCTResponseSenderBlock) callback) {
FIRDatabaseReference *ref = [self getPathRef:path];
[ref onDisconnectSetValue:props[@"value"] withCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[self handleCallback:@"onDisconnectSetObject" callback:callback databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectRemove:
(NSString *) path
callback:
(RCTResponseSenderBlock) callback) {
FIRDatabaseReference *ref = [self getPathRef:path];
[ref onDisconnectRemoveValueWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[self handleCallback:@"onDisconnectRemove" callback:callback databaseError:error];
}];
}
RCT_EXPORT_METHOD(onDisconnectCancel:
(NSString *) path
callback:
(RCTResponseSenderBlock) callback) {
FIRDatabaseReference *ref = [self getPathRef:path];
[ref cancelDisconnectOperationsWithCompletionBlock:^(NSError *_Nullable error, FIRDatabaseReference *_Nonnull _ref) {
[self handleCallback:@"onDisconnectCancel" callback:callback databaseError:error];
}];
}
RCT_EXPORT_METHOD(goOffline) {
[FIRDatabase database].goOffline;
}
RCT_EXPORT_METHOD(goOnline) {
[FIRDatabase database].goOnline;
}
- (FIRDatabaseReference *)getPathRef:(NSString *)path {
return [[[FIRDatabase database] reference] child:path];
}
- (void)handleCallback:(NSString *)methodName callback:(RCTResponseSenderBlock)callback databaseError:(NSError *)databaseError {
if (databaseError != nil) {
NSDictionary *evt = @{@"code": @([databaseError code]), @"details": [databaseError debugDescription], @"message": [databaseError localizedDescription], @"description": [databaseError description]};
callback(@[evt]);
} else {
callback(@[[NSNull null], @{@"status": @"success", @"method": methodName}]);
}
}
- (RNFirebaseDatabaseReference *)getDBHandle:(NSNumber *)refId path:(NSString *)path modifiers:(NSArray *)modifiers { - (RNFirebaseDatabaseReference *)getDBHandle:(NSNumber *)refId path:(NSString *)path modifiers:(NSArray *)modifiers {
RNFirebaseDatabaseReference *ref = _dbReferences[refId]; RNFirebaseDatabaseReference *ref = _dbReferences[refId];
@ -324,7 +426,7 @@ RCT_EXPORT_METHOD(goOnline) {
_dbReferences[refId] = ref; _dbReferences[refId] = ref;
} }
return ref; return ref;
} } */
// Not sure how to get away from this... yet // Not sure how to get away from this... yet
- (NSArray<NSString *> *)supportedEvents { - (NSArray<NSString *> *)supportedEvents {

View File

@ -8,13 +8,14 @@
@interface RNFirebaseDatabaseReference : NSObject @interface RNFirebaseDatabaseReference : NSObject
@property RCTEventEmitter *emitter; @property RCTEventEmitter *emitter;
@property FIRDatabaseQuery *query; @property FIRDatabaseQuery *query;
@property NSString *app;
@property NSNumber *refId; @property NSNumber *refId;
@property NSString *path; @property NSString *path;
@property NSMutableDictionary *listeners; @property NSMutableDictionary *listeners;
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter database:(FIRDatabase *)database refId:(NSNumber *)refId path:(NSString *)path modifiers:(NSArray *)modifiers; - (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter app:(NSString *)app refId:(NSNumber *)refId refPath:(NSString *)refPath modifiers:(NSArray *)modifiers;
- (void)addEventHandler:(NSNumber *)listenerId eventName:(NSString *)eventName; - (void)addEventHandler:(NSNumber *)listenerId eventName:(NSString *)eventName;
- (void)addSingleEventHandler:(NSString *)eventName callback:(RCTResponseSenderBlock)callback; - (void)addSingleEventHandler:(NSString *)eventName resolver:(RCTPromiseResolveBlock) resolve rejecter:(RCTPromiseRejectBlock) reject;
- (void)removeEventHandler:(NSNumber *)listenerId eventName:(NSString *)eventName; - (void)removeEventHandler:(NSNumber *)listenerId eventName:(NSString *)eventName;
- (BOOL)hasListeners; - (BOOL)hasListeners;
+ (NSDictionary *)snapshotToDict:(FIRDataSnapshot *)snapshot; + (NSDictionary *)snapshotToDict:(FIRDataSnapshot *)snapshot;

View File

@ -1,18 +1,27 @@
#import "RNFirebaseDatabaseReference.h" #import "RNFirebaseDatabaseReference.h"
#if __has_include(<FirebaseDatabase/FIRDatabase.h>) #if __has_include(<FirebaseDatabase/FIRDatabase.h>)
#import "RNFirebaseDatabase.h"
#import "RNFirebaseEvents.h" #import "RNFirebaseEvents.h"
@implementation RNFirebaseDatabaseReference @implementation RNFirebaseDatabaseReference
- (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter database:(FIRDatabase *)database refId:(NSNumber *)refId path:(NSString *)path modifiers:(NSArray *)modifiers { - (id)initWithPathAndModifiers:(RCTEventEmitter *)emitter
app:(NSString *) app
refId:(NSNumber *) refId
refPath:(NSString *) refPath
modifiers:(NSArray *) modifiers {
self = [super init]; self = [super init];
if (self) { if (self) {
_emitter = emitter; _emitter = emitter;
_app = app;
_refId = refId; _refId = refId;
_path = path; _path = refPath;
_query = [self buildQueryAtPathWithModifiers:database path:path modifiers:modifiers];
// TODO: Only create if needed
_listeners = [[NSMutableDictionary alloc] init]; _listeners = [[NSMutableDictionary alloc] init];
_query = [self buildQueryAtPathWithModifiers:refPath modifiers:modifiers];
} }
return self; return self;
} }
@ -36,15 +45,17 @@
} }
} }
- (void)addSingleEventHandler:(NSString *)eventName callback:(RCTResponseSenderBlock)callback { - (void)addSingleEventHandler:(NSString *)eventName
resolver:(RCTPromiseResolveBlock) resolve
rejecter:(RCTPromiseRejectBlock) reject {
FIRDataEventType firDataEventType = (FIRDataEventType)[self eventTypeFromName:eventName]; FIRDataEventType firDataEventType = (FIRDataEventType)[self eventTypeFromName:eventName];
[_query observeSingleEventOfType:firDataEventType andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *_Nonnull snapshot, NSString *_Nullable previousChildName) { [_query observeSingleEventOfType:firDataEventType andPreviousSiblingKeyWithBlock:^(FIRDataSnapshot *_Nonnull snapshot, NSString *_Nullable previousChildName) {
NSDictionary *props = [RNFirebaseDatabaseReference snapshotToDict:snapshot]; NSDictionary *data = [RNFirebaseDatabaseReference snapshotToDictionary:eventName path:_path dataSnapshot:snapshot previousChildName:previousChildName refId:_refId listenerId:0];
callback(@[[NSNull null], @{@"eventName": eventName, @"path": _path, @"refId": _refId, @"snapshot": props, @"previousChildName": previousChildName != nil ? previousChildName : [NSNull null]}]); resolve(data);
} withCancelBlock:^(NSError *_Nonnull error) { } withCancelBlock:^(NSError *_Nonnull error) {
NSLog(@"Error onDBEventOnce: %@", [error debugDescription]); NSLog(@"Error onDBEventOnce: %@", [error debugDescription]);
callback(@[@{@"eventName": DATABASE_ERROR_EVENT, @"path": _path, @"refId": _refId, @"code": @([error code]), @"details": [error debugDescription], @"message": [error localizedDescription], @"description": [error description]}]); [RNFirebaseDatabase handlePromise:resolve rejecter:reject databaseError:error];
}]; }];
} }
@ -56,6 +67,45 @@
} }
} }
+ (NSDictionary *) snapshotToDictionary:(NSString *) eventName
path:(NSString *) path
dataSnapshot:(FIRDataSnapshot *) dataSnapshot
previousChildName:(NSString *) previousChildName
refId:(NSNumber *) refId
listenerId:(NSNumber *) listenerId {
NSMutableDictionary *snapshot = [[NSMutableDictionary alloc] init];
NSMutableDictionary *eventMap = [[NSMutableDictionary alloc] init];
[snapshot setValue:dataSnapshot.key forKey:@"key"];
[snapshot setValue:@(dataSnapshot.exists) forKey:@"exists"];
[snapshot setValue:@(dataSnapshot.hasChildren) forKey:@"hasChildren"];
[snapshot setValue:@(dataSnapshot.childrenCount) forKey:@"childrenCount"];
[snapshot setValue:dataSnapshot.value forKey:@"value"];
[snapshot setValue:[RNFirebaseDatabaseReference getChildKeys:dataSnapshot] forKey:@"childKeys"];
[snapshot setValue:dataSnapshot.priority forKey:@"priority"];
[eventMap setValue:refId forKey:@"refId"];
[eventMap setValue:path forKey:@"path"];
[eventMap setValue:snapshot forKey:@"snapshot"];
[eventMap setValue:eventName forKey:@"eventName"];
[eventMap setValue:listenerId forKey:@"listenerId"];
[eventMap setValue:previousChildName forKey:@"previousChildName"];
return eventMap;
}
+ (NSMutableArray *) getChildKeys:(FIRDataSnapshot *) snapshot {
NSMutableArray *childKeys = [NSMutableArray array];
if (snapshot.childrenCount > 0) {
NSEnumerator *children = [snapshot children];
FIRDataSnapshot *child;
while (child = [children nextObject]) {
[childKeys addObject:child.key];
}
}
return childKeys;
}
+ (NSDictionary *)snapshotToDict:(FIRDataSnapshot *)snapshot { + (NSDictionary *)snapshotToDict:(FIRDataSnapshot *)snapshot {
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:snapshot.key forKey:@"key"]; [dict setValue:snapshot.key forKey:@"key"];
@ -103,8 +153,10 @@
} }
} }
- (FIRDatabaseQuery *)buildQueryAtPathWithModifiers:(FIRDatabase *)database path:(NSString *)path modifiers:(NSArray *)modifiers { - (FIRDatabaseQuery *)buildQueryAtPathWithModifiers:(NSString *) path
FIRDatabaseQuery *query = [[database reference] child:path]; modifiers:(NSArray *)modifiers {
FIRDatabase *firebaseDatabase = [RNFirebaseDatabase getDatabaseForApp:_app];
FIRDatabaseQuery *query = [[firebaseDatabase reference] child:path];
for (NSDictionary *modifier in modifiers) { for (NSDictionary *modifier in modifiers) {
NSString *type = [modifier valueForKey:@"type"]; NSString *type = [modifier valueForKey:@"type"];