diff --git a/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m b/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m index 24967be4a..a3c4c4354 100644 --- a/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m +++ b/Examples/UIExplorer/UIExplorerUnitTests/RCTBridgeTests.m @@ -24,7 +24,7 @@ @property (nonatomic, strong, readonly) RCTBridge *batchedBridge; -- (void)_handleBuffer:(id)buffer; +- (void)handleBuffer:(id)buffer; - (void)setUp; @end @@ -169,7 +169,7 @@ _Pragma("clang diagnostic pop") NSArray *args = @[@1234, @5678, @"stringy", @{@"a": @1}, @42]; NSArray *buffer = @[@[testModuleID], @[testMethodID], @[args], @[], @1234567]; - [_bridge.batchedBridge _handleBuffer:buffer]; + [_bridge.batchedBridge handleBuffer:buffer]; dispatch_sync(_methodQueue, ^{ // clear the queue @@ -180,7 +180,7 @@ _Pragma("clang diagnostic pop") - (void)DISABLED_testBadArgumentsCount { //NSArray *bufferWithMissingArgument = @[@[@1], @[@0], @[@[@1234, @5678, @"stringy", @{@"a": @1}/*, @42*/]], @[], @1234567]; - //[_bridge _handleBuffer:bufferWithMissingArgument]; + //[_bridge handleBuffer:bufferWithMissingArgument]; NSLog(@"WARNING: testBadArgumentsCount is temporarily disabled until we have a better way to test cases that we expect to trigger redbox errors"); } diff --git a/React/Base/RCTBatchedBridge.m b/React/Base/RCTBatchedBridge.m index e996af146..aedb23008 100644 --- a/React/Base/RCTBatchedBridge.m +++ b/React/Base/RCTBatchedBridge.m @@ -63,6 +63,7 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); { BOOL _loading; BOOL _valid; + BOOL _wasBatchActive; __weak id _javaScriptExecutor; NSMutableArray *_pendingCalls; NSMutableArray *_moduleDataByID; @@ -386,6 +387,7 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void); // timing issues with RCTRootView dispatch_async(dispatch_get_main_queue(), ^{ [self didFinishLoading]; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification object:_parentBridge userInfo:@{ @"bridge": self }]; @@ -613,7 +615,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR @"error": RCTNullIfNil(error), }); - [self _handleBuffer:json]; + [self handleBuffer:json batchEnded:YES]; onComplete(error); }]; @@ -669,7 +671,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR return; } [[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil]; - [self _handleBuffer:json]; + [self handleBuffer:json batchEnded:YES]; }; [_javaScriptExecutor executeJSCall:module @@ -680,14 +682,26 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR #pragma mark - Payload Processing -- (void)_handleBuffer:(id)buffer +- (void)handleBuffer:(id)buffer batchEnded:(BOOL)batchEnded { RCTAssertJSThread(); - if (buffer == nil || buffer == (id)kCFNull) { - return; + if (buffer != nil && buffer != (id)kCFNull) { + _wasBatchActive = YES; + [self handleBuffer:buffer]; } + if (batchEnded) { + if (_wasBatchActive) { + [self batchDidComplete]; + } + + _wasBatchActive = NO; + } +} + +- (void)handleBuffer:(id)buffer +{ NSArray *requestsArray = [RCTConvert NSArray:buffer]; #if RCT_DEBUG @@ -765,7 +779,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleUR dispatch_async(queue, block); } } +} +- (void)batchDidComplete +{ // TODO: batchDidComplete is only used by RCTUIManager - can we eliminate this special case? for (RCTModuleData *moduleData in _moduleDataByID) { if ([moduleData.instance respondsToSelector:@selector(batchDidComplete)]) { diff --git a/React/Executors/RCTContextExecutor.m b/React/Executors/RCTContextExecutor.m index a62eb9164..a0f9f0be8 100644 --- a/React/Executors/RCTContextExecutor.m +++ b/React/Executors/RCTContextExecutor.m @@ -90,6 +90,13 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init) @end +// Private bridge interface to allow middle-batch calls +@interface RCTBridge (RCTContextExecutor) + +- (void)handleBuffer:(NSArray *)buffer batchEnded:(BOOL)hasEnded; + +@end + @implementation RCTContextExecutor { RCTJavaScriptContext *_context; @@ -336,6 +343,16 @@ static void RCTInstallJSCProfiler(RCTBridge *bridge, JSContextRef context) } [strongSelf _addNativeHook:RCTNativeLoggingHook withName:"nativeLoggingHook"]; [strongSelf _addNativeHook:RCTNoop withName:"noop"]; + + __weak RCTBridge *bridge = strongSelf->_bridge; + strongSelf->_context.context[@"nativeFlushQueueImmediate"] = ^(NSArray *calls){ + if (!weakSelf.valid || !calls) { + return; + } + + [bridge handleBuffer:calls batchEnded:NO]; + }; + #if RCT_DEV [strongSelf _addNativeHook:RCTNativeTraceBeginSection withName:"nativeTraceBeginSection"]; [strongSelf _addNativeHook:RCTNativeTraceEndSection withName:"nativeTraceEndSection"];