diff --git a/framework/ios/base/bridge/HippyBridge.h b/framework/ios/base/bridge/HippyBridge.h index 31c048b7016..1bfe678c6cc 100644 --- a/framework/ios/base/bridge/HippyBridge.h +++ b/framework/ios/base/bridge/HippyBridge.h @@ -283,6 +283,19 @@ HP_EXTERN NSString *HippyBridgeModuleNameForClass(Class bridgeModuleClass); @end + +@interface HippyBridge (RedBoxDebug) + +/// The last current active bridge instance. ++ (instancetype)currentBridge; + +/// Record the last active bridge instance. +/// - Parameter currentBridge: bridge instance, pass nil to reset. ++ (void)setCurrentBridge:(nullable HippyBridge *)currentBridge; + +@end + + HP_EXTERN void HippyBridgeFatal(NSError *, HippyBridge *); HP_EXTERN void HippyBridgeHandleException(NSException *exception, HippyBridge *bridge); diff --git a/framework/ios/base/bridge/HippyBridge.mm b/framework/ios/base/bridge/HippyBridge.mm index 5d85d19e7da..f9a934e5cee 100644 --- a/framework/ios/base/bridge/HippyBridge.mm +++ b/framework/ios/base/bridge/HippyBridge.mm @@ -150,6 +150,7 @@ - (instancetype)initWithDelegate:(id)delegate HPExecuteOnMainThread(^{ [self bindKeys]; }, YES); + [HippyBridge setCurrentBridge:self]; HPLogInfo(@"[Hippy_OC_Log][Life_Circle],%@ Init %p", NSStringFromClass([self class]), self); } return self; @@ -838,6 +839,10 @@ - (void)invalidate { _moduleSetup = nil; _startTime = footstone::TimePoint::SystemNow(); self.moduleSemaphore = nil; + + if ([HippyBridge currentBridge] == self) { + [HippyBridge setCurrentBridge:nil]; + } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ [jsExecutor executeBlockOnJavaScriptQueue:^{ @autoreleasepool { @@ -994,3 +999,26 @@ void HippyBridgeFatal(NSError *error, HippyBridge *bridge) { void HippyBridgeHandleException(NSException *exception, HippyBridge *bridge) { HPHandleException(exception, bridge?@{@"bridge": bridge}:nil); } + + +#pragma mark - + +@implementation HippyBridge (RedBoxDebug) + +static HippyBridge *HippyCurrentBridgeInstance = nil; + +/** + * The last current active bridge instance. This is set automatically whenever + * the bridge is accessed. It can be useful for static functions or singletons + * that need to access the bridge for purposes such as logging, but should not + * be relied upon to return any particular instance, due to race conditions. + */ ++ (instancetype)currentBridge { + return HippyCurrentBridgeInstance; +} + ++ (void)setCurrentBridge:(nullable HippyBridge *)currentBridge { + HippyCurrentBridgeInstance = currentBridge; +} + +@end diff --git a/modules/ios/base/HPLog.h b/modules/ios/base/HPLog.h index 48bd34b234e..02b4573dcf8 100644 --- a/modules/ios/base/HPLog.h +++ b/modules/ios/base/HPLog.h @@ -32,6 +32,14 @@ #endif //#ifdef DEBUG #endif //#ifndef HP_LOG_ENABLED +/** + * Thresholds for logs to display a redbox. You can override these values when debugging + * in order to tweak the default logging behavior. + */ +#ifndef HPLOG_REDBOX_LEVEL +#define HPLOG_REDBOX_LEVEL HPLogLevelError +#endif + /** * Logging macros. Use these to log information, warnings and errors in your * own code. diff --git a/modules/ios/base/HPLog.mm b/modules/ios/base/HPLog.mm index 7eb26af3fff..09c57215706 100644 --- a/modules/ios/base/HPLog.mm +++ b/modules/ios/base/HPLog.mm @@ -21,7 +21,8 @@ */ #import "HPLog.h" - +#import "HippyBridge.h" +#import "HippyRedBox.h" #include #include @@ -165,7 +166,7 @@ void HPLogNativeInternal(HPLogLevel level, const char *fileName, int lineNumber, va_end(args); NSArray *callStacks = nil; #if HP_DEBUG - if (level >= HPLogLevelError) { + if (level >= HPLOG_REDBOX_LEVEL) { NSArray *stackSymbols = [NSThread callStackSymbols]; NSMutableArray *stack = [NSMutableArray arrayWithCapacity:(stackSymbols.count - 1)]; [stackSymbols enumerateObjectsUsingBlock:^(NSString *frameSymbols, NSUInteger idx, __unused BOOL *stop) { @@ -182,6 +183,12 @@ void HPLogNativeInternal(HPLogLevel level, const char *fileName, int lineNumber, } }]; callStacks = [stack copy]; + + dispatch_async(dispatch_get_main_queue(), ^{ + // red box is thread safe, but by deferring to main queue we avoid a startup + // race condition that causes the module to be accessed before it has loaded + [[HippyBridge currentBridge].redBox showErrorMessage:message withStack:stack]; + }); } #endif // Call log function