From 237e1ba158c4c6231de1680ae28d8e51ba1dacac Mon Sep 17 00:00:00 2001 From: Yavor Georgiev Date: Wed, 14 Jun 2017 14:44:28 +0200 Subject: [PATCH] Support the new React Native C++ bridge (#1065) * Support the new React Native C++ bridge Fixes #1049 * address code review comments * handle the case when the websocket executor does not exist --- CHANGELOG.md | 11 ++++ react-native/ios/RealmReact/RealmReact.mm | 62 ++++++++++++++++------- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad70d254b2..d304af0617 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +vNext Release notes (TBD) +============================================================= +### Breaking changes +* None + +### Enhancements +* None + +### Bug fixes +* Fix crash when used with the React Native C++ bridge + 1.3.1 Release notes (2017-5-18) ============================================================= ### Breaking changes diff --git a/react-native/ios/RealmReact/RealmReact.mm b/react-native/ios/RealmReact/RealmReact.mm index 0a6c24479f..8161379750 100644 --- a/react-native/ios/RealmReact/RealmReact.mm +++ b/react-native/ios/RealmReact/RealmReact.mm @@ -49,6 +49,12 @@ - (instancetype)initWithJSContext:(JSContext *)context onThread:(NSThread *)thre - (JSContext *)context; @end +// the part of the RCTCxxBridge private class we care about +@interface RCTBridge (RCTCxxBridge) +- (JSGlobalContextRef)jsContextRef; +- (void)executeBlockOnJavaScriptThread:(dispatch_block_t)block; +@end + extern "C" JSGlobalContextRef RealmReactGetJSGlobalContextForExecutor(id executor, bool create) { Ivar contextIvar = class_getInstanceVariable([executor class], "_context"); if (!contextIvar) { @@ -265,6 +271,22 @@ - (void)dealloc { [self performSelectorOnMainThread:@selector(invalidate) withObject:nil waitUntilDone:YES]; } +typedef JSGlobalContextRef (^JSContextRefExtractor)(); + +void _initializeOnJSThread(JSContextRefExtractor jsContextExtractor) { + // Make sure the previous JS thread is completely finished before continuing. + static __weak NSThread *s_currentJSThread; + while (s_currentJSThread && !s_currentJSThread.finished) { + [NSThread sleepForTimeInterval:0.1]; + } + s_currentJSThread = [NSThread currentThread]; + + // Close all cached Realms from the previous JS thread. + realm::_impl::RealmCoordinator::clear_all_caches(); + + RJSInitializeInContext(jsContextExtractor()); +} + - (void)setBridge:(RCTBridge *)bridge { _bridge = bridge; @@ -272,16 +294,31 @@ - (void)setBridge:(RCTBridge *)bridge { [s_currentModule invalidate]; s_currentModule = self; - id executor = [bridge valueForKey:@"javaScriptExecutor"]; - - if ([executor isKindOfClass:NSClassFromString(@"RCTWebSocketExecutor")]) { + if (objc_lookUpClass("RCTWebSocketExecutor") && [bridge executorClass] == objc_lookUpClass("RCTWebSocketExecutor")) { #if DEBUG [self startRPC]; #else @throw [NSException exceptionWithName:@"Invalid Executor" reason:@"Chrome debug mode not supported in Release builds" userInfo:nil]; #endif - } - else { + } else if ([bridge isKindOfClass:objc_lookUpClass("RCTCxxBridge")]) { + // probe for the new C++ bridge in React Native 0.45+ + + __weak __typeof__(self) weakSelf = self; + __weak __typeof__(bridge) weakBridge = bridge; + [bridge executeBlockOnJavaScriptThread:^{ + __typeof__(self) self = weakSelf; + __typeof__(bridge) bridge = weakBridge; + if (!self || !bridge) { + return; + } + + _initializeOnJSThread(^{ + return [bridge jsContextRef]; + }); + }]; + return; + } else { // React Native 0.44 and older + id executor = [bridge valueForKey:@"javaScriptExecutor"]; __weak __typeof__(self) weakSelf = self; __weak __typeof__(executor) weakExecutor = executor; @@ -292,18 +329,9 @@ - (void)setBridge:(RCTBridge *)bridge { return; } - // Make sure the previous JS thread is completely finished before continuing. - static __weak NSThread *s_currentJSThread; - while (s_currentJSThread && !s_currentJSThread.finished) { - [NSThread sleepForTimeInterval:0.1]; - } - s_currentJSThread = [NSThread currentThread]; - - // Close all cached Realms from the previous JS thread. - realm::_impl::RealmCoordinator::clear_all_caches(); - - JSGlobalContextRef ctx = RealmReactGetJSGlobalContextForExecutor(executor, true); - RJSInitializeInContext(ctx); + _initializeOnJSThread(^ { + return RealmReactGetJSGlobalContextForExecutor(executor, true); + }); }]; } }