[ios][database] transaction support implemented

This commit is contained in:
Salakar 2017-03-25 00:59:27 +00:00
parent 5824e5405f
commit fda59ecdcb
3 changed files with 170 additions and 83 deletions

View File

@ -10,6 +10,8 @@
}
@property NSMutableDictionary *dbReferences;
@property NSMutableDictionary *transactions;
@property dispatch_queue_t transactionQueue;
@end

View File

@ -13,6 +13,7 @@
@property FIRDatabaseHandle childRemovedHandler;
@property FIRDatabaseHandle childMovedHandler;
@property FIRDatabaseHandle childValueHandler;
+ (NSDictionary *) snapshotToDict:(FIRDataSnapshot *) snapshot;
@end
@implementation RNFirebaseDBReference
@ -38,7 +39,7 @@
{
if (![self isListeningTo:eventName]) {
id withBlock = ^(FIRDataSnapshot * _Nonnull snapshot) {
NSDictionary *props = [self snapshotToDict:snapshot];
NSDictionary *props = [RNFirebaseDBReference snapshotToDict:snapshot];
[self sendJSEvent:DATABASE_DATA_EVENT
title:eventName
props: @{
@ -69,7 +70,7 @@
{
[_query observeSingleEventOfType:FIRDataEventTypeValue
withBlock:^(FIRDataSnapshot * _Nonnull snapshot) {
NSDictionary *props = [self snapshotToDict:snapshot];
NSDictionary *props = [RNFirebaseDBReference snapshotToDict:snapshot];
callback(@[[NSNull null], @{
@"eventName": @"value",
@"path": _path,
@ -131,7 +132,7 @@
[self unsetListeningOn:name];
}
- (NSDictionary *) snapshotToDict:(FIRDataSnapshot *) snapshot
+ (NSDictionary *) snapshotToDict:(FIRDataSnapshot *) snapshot
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:snapshot.key forKey:@"key"];
@ -184,18 +185,10 @@
return event;
}
- (void) sendJSEvent:(NSString *)type
title:(NSString *)title
props:(NSDictionary *)props
{
- (void) sendJSEvent:(NSString *)type title:(NSString *)title props:(NSDictionary *)props {
@try {
[_emitter sendEventWithName:type
body:@{
@"eventName": title,
@"body": props
}];
}
@catch (NSException *err) {
[_emitter sendEventWithName:type body:@{ @"eventName": title, @"body": props }];
} @catch (NSException *err) {
NSLog(@"An error occurred in sendJSEvent: %@", [err debugDescription]);
NSLog(@"Tried to send: %@ with %@", title, props);
}
@ -370,19 +363,109 @@
@end
@implementation RNFirebaseDatabase
RCT_EXPORT_MODULE(RNFirebaseDatabase);
- (id) init
{
- (id) init {
self = [super init];
if (self != nil) {
_dbReferences = [[NSMutableDictionary alloc] init];
_transactions = [[NSMutableDictionary alloc] init];
_transactionQueue = dispatch_queue_create("io.invertase.react-native-firebase", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (void) sendTransactionEvent:(NSString *)type body:(id)body {
@try {
[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:(NSString *) path identifier:(NSString *) identifier applyLocally:(BOOL) applyLocally) {
dispatch_async(_transactionQueue, ^{
NSMutableDictionary *transactionState = [NSMutableDictionary new];
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[transactionState setObject:sema forKey:@"semaphore"];
FIRDatabaseReference *ref = [self getPathRef:path];
[ref runTransactionBlock:^FIRTransactionResult * _Nonnull(FIRMutableData * _Nonnull currentData) {
dispatch_barrier_async(_transactionQueue, ^{
[_transactions setValue:transactionState forKey:identifier];
[self sendTransactionEvent:DATABASE_TRANSACTION_EVENT body:@{ @"id": identifier, @"type": @"update", @"value": currentData.value }];
});
// wait for the js event handler to call tryCommitTransaction
// this wait occurs on the Firebase Worker Queue
// so if the tryCommitTransaction fails to signal the semaphore
// no further blocks will be executed by Firebase until the timeout expires
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC);
BOOL timedout = dispatch_semaphore_wait(sema, delayTime) != 0;
BOOL abort = [transactionState valueForKey:@"abort"] || timedout;
id value = [transactionState valueForKey:@"value"];
dispatch_barrier_async(_transactionQueue, ^{
[_transactions removeObjectForKey:identifier];
});
if (abort) {
return [FIRTransactionResult abort];
} else {
currentData.value = value;
return [FIRTransactionResult successWithValue:currentData];
}
} andCompletionBlock:^(NSError * _Nullable databaseError, BOOL committed, FIRDataSnapshot * _Nullable snapshot) {
if (databaseError != nil) {
[self sendTransactionEvent:DATABASE_TRANSACTION_EVENT body:@{
@"id": identifier,
@"type": @"error",
@"code": [NSNumber numberWithInt:[databaseError code]],
@"message": [databaseError description]
}];
} else {
[self sendTransactionEvent:DATABASE_TRANSACTION_EVENT body:@{
@"id": identifier,
@"type": @"complete",
@"committed": @(committed),
@"snapshot": [RNFirebaseDBReference snapshotToDict:snapshot],
}];
}
} withLocalEvents:applyLocally];
});
}
RCT_EXPORT_METHOD(tryCommitTransaction:(NSString *) identifier withData:(NSDictionary *) data) {
__block NSMutableDictionary *transactionState;
dispatch_sync(_transactionQueue, ^{
transactionState = [_transactions objectForKey: identifier];
});
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)
{
@ -634,7 +717,7 @@ RCT_EXPORT_METHOD(goOnline)
// Not sure how to get away from this... yet
- (NSArray<NSString *> *)supportedEvents {
return @[DATABASE_DATA_EVENT, DATABASE_ERROR_EVENT];
return @[DATABASE_DATA_EVENT, DATABASE_ERROR_EVENT, DATABASE_TRANSACTION_EVENT];
}

View File

@ -21,6 +21,7 @@ static NSString *const DEBUG_EVENT = @"debug";
// Database
static NSString *const DATABASE_DATA_EVENT = @"database_event";
static NSString *const DATABASE_ERROR_EVENT = @"database_error";
static NSString *const DATABASE_TRANSACTION_EVENT = @"database_transaction_event";
static NSString *const DATABASE_VALUE_EVENT = @"value";
static NSString *const DATABASE_CHILD_ADDED_EVENT = @"child_added";
@ -28,6 +29,7 @@ static NSString *const DATABASE_CHILD_MODIFIED_EVENT = @"child_changed";
static NSString *const DATABASE_CHILD_REMOVED_EVENT = @"child_removed";
static NSString *const DATABASE_CHILD_MOVED_EVENT = @"child_moved";
// Storage
static NSString *const STORAGE_EVENT = @"storage_event";
static NSString *const STORAGE_ERROR = @"storage_error";