diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm index 5deab25265a1ca..7df790a129e38a 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm @@ -178,18 +178,6 @@ - (void)hostDidStart:(RCTHost *)host { } -- (void)host:(RCTHost *)host - didReceiveJSErrorStack:(NSArray *> *)stack - message:(NSString *)message - originalMessage:(NSString *_Nullable)originalMessage - name:(NSString *_Nullable)name - componentStack:(NSString *_Nullable)componentStack - exceptionId:(NSUInteger)exceptionId - isFatal:(BOOL)isFatal - extraData:(NSDictionary *)extraData -{ -} - #pragma mark - Bridge and Bridge Adapter properties - (RCTBridge *)bridge diff --git a/packages/react-native/React/CoreModules/RCTExceptionsManager.h b/packages/react-native/React/CoreModules/RCTExceptionsManager.h index d4b350961c5abd..d47ad65cf28042 100644 --- a/packages/react-native/React/CoreModules/RCTExceptionsManager.h +++ b/packages/react-native/React/CoreModules/RCTExceptionsManager.h @@ -34,7 +34,7 @@ NS_ASSUME_NONNULL_BEGIN - (void)reportJsException:(nullable NSString *)message stack:(nullable NSArray *)stack exceptionId:(double)exceptionId - isFatal:(bool)isFatal; + isFatal:(bool)isFatal __attribute__((deprecated)); @property (nonatomic, weak) id delegate; diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/React-RuntimeApple.podspec b/packages/react-native/ReactCommon/react/runtime/platform/ios/React-RuntimeApple.podspec index e14016ae6340b1..3cc0f51a90d712 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/React-RuntimeApple.podspec +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/React-RuntimeApple.podspec @@ -22,7 +22,7 @@ folly_version = folly_config[:version] folly_dep_name = folly_config[:dep_name] boost_config = get_boost_config() -boost_compiler_flags = boost_config[:compiler_flags] +boost_compiler_flags = boost_config[:compiler_flags] header_search_paths = [ "$(PODS_ROOT)/boost", @@ -70,6 +70,8 @@ Pod::Spec.new do |s| s.dependency "React-jserrorhandler" s.dependency "React-jsinspector" + add_dependency(s, "ReactCodegen") + if ENV["USE_HERMES"] == nil || ENV["USE_HERMES"] == "1" s.dependency "hermes-engine" s.dependency "React-RuntimeHermes" diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h index 25e213a11c27b5..37bef80f625763 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.h @@ -27,6 +27,15 @@ typedef NSURL *_Nullable (^RCTHostBundleURLProvider)(void); @protocol RCTHostDelegate +- (void)hostDidStart:(RCTHost *)host; + +@optional +- (void)loadBundleAtURL:(NSURL *)sourceURL + onProgress:(RCTSourceLoadProgressBlock)onProgress + onComplete:(RCTSourceLoadBlock)loadCallback; + +// TODO(T205780509): Remove this api in react native v0.78 +// The bridgeless js error handling api will just call into exceptionsmanager directly - (void)host:(RCTHost *)host didReceiveJSErrorStack:(NSArray *> *)stack message:(NSString *)message @@ -35,15 +44,7 @@ typedef NSURL *_Nullable (^RCTHostBundleURLProvider)(void); componentStack:(NSString *_Nullable)componentStack exceptionId:(NSUInteger)exceptionId isFatal:(BOOL)isFatal - extraData:(NSDictionary *)extraData; - -- (void)hostDidStart:(RCTHost *)host; - -@optional -- (void)loadBundleAtURL:(NSURL *)sourceURL - onProgress:(RCTSourceLoadProgressBlock)onProgress - onComplete:(RCTSourceLoadBlock)loadCallback; - + extraData:(NSDictionary *)extraData __attribute__((deprecated)); @end @protocol RCTHostRuntimeDelegate diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm index 75fe99c2353cbd..814e8503d9e997 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm @@ -315,7 +315,7 @@ - (void)dealloc #pragma mark - RCTInstanceDelegate -- (void)instance:(RCTInstance *)instance +- (BOOL)instance:(RCTInstance *)instance didReceiveJSErrorStack:(NSArray *> *)stack message:(NSString *)message originalMessage:(NSString *_Nullable)originalMessage @@ -325,6 +325,12 @@ - (void)instance:(RCTInstance *)instance isFatal:(BOOL)isFatal extraData:(NSDictionary *)extraData { + if (![_hostDelegate respondsToSelector:@selector(host: + didReceiveJSErrorStack:message:originalMessage:name:componentStack + :exceptionId:isFatal:extraData:)]) { + return NO; + } + [_hostDelegate host:self didReceiveJSErrorStack:stack message:message @@ -334,6 +340,7 @@ - (void)instance:(RCTInstance *)instance exceptionId:exceptionId isFatal:isFatal extraData:extraData]; + return YES; } - (void)instance:(RCTInstance *)instance didInitializeRuntime:(facebook::jsi::Runtime &)runtime diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h index b77c8994e35a2f..0593dccc87a0c6 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.h @@ -37,7 +37,15 @@ RCT_EXTERN void RCTInstanceSetRuntimeDiagnosticFlags(NSString *_Nullable flags); @protocol RCTInstanceDelegate -- (void)instance:(RCTInstance *)instance +- (void)instance:(RCTInstance *)instance didInitializeRuntime:(facebook::jsi::Runtime &)runtime; + +- (void)loadBundleAtURL:(NSURL *)sourceURL + onProgress:(RCTSourceLoadProgressBlock)onProgress + onComplete:(RCTSourceLoadBlock)loadCallback; + +// TODO(T205780509): Remove this api in react native v0.78 +// The bridgeless js error handling api will just call into exceptionsmanager directly +- (BOOL)instance:(RCTInstance *)instance didReceiveJSErrorStack:(NSArray *> *)stack message:(NSString *)message originalMessage:(NSString *_Nullable)originalMessage @@ -45,14 +53,7 @@ RCT_EXTERN void RCTInstanceSetRuntimeDiagnosticFlags(NSString *_Nullable flags); componentStack:(NSString *_Nullable)componentStack exceptionId:(NSUInteger)exceptionId isFatal:(BOOL)isFatal - extraData:(NSDictionary *)extraData; - -- (void)instance:(RCTInstance *)instance didInitializeRuntime:(facebook::jsi::Runtime &)runtime; - -- (void)loadBundleAtURL:(NSURL *)sourceURL - onProgress:(RCTSourceLoadProgressBlock)onProgress - onComplete:(RCTSourceLoadBlock)loadCallback; - + extraData:(NSDictionary *)extraData __attribute__((deprecated)); @end /** diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm index 09ee426ea0af80..16117722794050 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTInstance.mm @@ -9,6 +9,7 @@ #import +#import #import #import #import @@ -467,32 +468,57 @@ - (void)_loadScriptFromSource:(RCTSource *)source - (void)_handleJSError:(const JsErrorHandler::ParsedError &)error withRuntime:(jsi::Runtime &)runtime { - NSString *message = @(error.message.c_str()); + NSMutableDictionary *errorData = [NSMutableDictionary new]; + errorData[@"message"] = @(error.message.c_str()); + if (error.originalMessage) { + errorData[@"originalMessage"] = @(error.originalMessage->c_str()); + } + if (error.name) { + errorData[@"name"] = @(error.name->c_str()); + } + if (error.componentStack) { + errorData[@"componentStack"] = @(error.componentStack->c_str()); + } + NSMutableArray *> *stack = [NSMutableArray new]; for (const JsErrorHandler::ParsedError::StackFrame &frame : error.stack) { - [stack addObject:@{ - @"file" : frame.file ? @((*frame.file).c_str()) : [NSNull null], - @"methodName" : @(frame.methodName.c_str()), - @"lineNumber" : frame.lineNumber ? @(*frame.lineNumber) : [NSNull null], - @"column" : frame.column ? @(*frame.column) : [NSNull null], - }]; + NSMutableDictionary *stackFrame = [NSMutableDictionary new]; + if (frame.file) { + stackFrame[@"file"] = @(frame.file->c_str()); + } + stackFrame[@"methodName"] = @(frame.methodName.c_str()); + if (frame.lineNumber) { + stackFrame[@"lineNumber"] = @(*frame.lineNumber); + } + if (frame.column) { + stackFrame[@"column"] = @(*frame.column); + } + [stack addObject:stackFrame]; } - NSString *originalMessage = error.originalMessage ? @(error.originalMessage->c_str()) : nil; - NSString *name = error.name ? @(error.name->c_str()) : nil; - NSString *componentStack = error.componentStack ? @(error.componentStack->c_str()) : nil; + errorData[@"stack"] = stack; + errorData[@"id"] = @(error.id); + errorData[@"isFatal"] = @(error.isFatal); + id extraData = TurboModuleConvertUtils::convertJSIValueToObjCObject(runtime, jsi::Value(runtime, error.extraData), nullptr); + if (extraData) { + errorData[@"extraData"] = extraData; + } - [_delegate instance:self - didReceiveJSErrorStack:stack - message:message - originalMessage:originalMessage - name:name - componentStack:componentStack - exceptionId:error.id - isFatal:error.isFatal - extraData:extraData]; + if (![_delegate instance:self + didReceiveJSErrorStack:errorData[@"stack"] + message:errorData[@"message"] + originalMessage:errorData[@"originalMessage"] + name:errorData[@"name"] + componentStack:errorData[@"componentStack"] + exceptionId:error.id + isFatal:errorData[@"isFatal"] + extraData:errorData[@"extraData"]]) { + JS::NativeExceptionsManager::ExceptionData jsErrorData{errorData}; + id exceptionsManager = [_turboModuleManager moduleForName:"ExceptionsManager"]; + [exceptionsManager reportException:jsErrorData]; + } } @end