React Native monorepo iOS release构建启动崩溃 AppRegistry报错
React Native Monorepo iOS Release包启动崩溃排查
问题背景
现有monorepo项目结构如下:
├── node_modules ├── packages ├── app # React native project ├── android └── ios ├── common # TS package └── web # Next JS project
common包存放web端和React Native App共用的服务代码(如API调用逻辑),开发阶段app、web项目构建运行均正常- 准备发布App到App Store时,无法编译出可正常运行的iOS release版本,应用启动直接崩溃,崩溃日志如下:
2022-06-02 15:33:14.173898+0200 XXXX[33899:2579321] *** Terminating app due to uncaught exception 'RCTFatalException: Unhandled JS Exception: Invariant Violation: Module AppRegistry is not a registered callable module (calling runApplication). A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.', reason: 'Unhandled JS Exception: Invariant Violation: Module AppRegistry is not a registered callable module (calling runApplication). A frequent cause of the error is that the applica..., stack: <unknown>@13:289 value@760:3143 <unknown>@760:747 value@760:2538 value@760:719 value@(null):(null) ' *** First throw call stack: (0x185361288 0x19e05b744 0x1003c0538 0x10043fd8c 0x10044066c 0x1852e9b24 0x185307610 0x18533e5fc 0x1003d7bd4 0x1003f5e98 0x1003f5ac8 0x184fc6e6c 0x184fc8a30 0x184fd0124 0x184fd0c80 0x184fdb500 0x1f6c240bc 0x1f6c23e5c) libc++abi: terminating with uncaught exception of type NSException
现有配置
- Xcode Build Phases > Bundle React Native code and images 配置:
set -e export NODE_BINARY=node export EXTRA_PACKAGER_ARGS="--entry-file packages/app/index.js --reset-cache" ../../../node_modules/react-native/scripts/react-native-xcode.sh "packages/app/index.js"
- Start packager 脚本配置:
export RCT_METRO_PORT="${RCT_METRO_PORT:=8081}" echo "export RCT_METRO_PORT=${RCT_METRO_PORT}" > "${SRCROOT}/../../../node_modules/react-native/scripts/.packager.env" if [ -z "${RCT_NO_LAUNCH_PACKAGER+xxx}" ] ; then if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then if ! curl -s "http://localhost:${RCT_METRO_PORT}/status" | grep -q "packager-status:running" ; then echo "Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly" exit 2 fi else open "$SRCROOT/../../../node_modules/react-native/scripts/launchPackager.command" || echo "Can't start packager automatically" fi fi
- AppDelegate.m 相关配置:
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"packages/app/index" fallbackResource:nil]; #else return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif }
- metro.config.js 配置:
const {getDefaultConfig} = require("metro-config"); const path = require('path'); module.exports = (async () => { const { resolver: {sourceExts, assetExts} } = await getDefaultConfig(); return { projectRoot: path.resolve(__dirname, '../../'), transformer: { babelTransformerPath: require.resolve("react-native-svg-transformer"), getTransformOptions: async () => ({ transform: { experimentalImportSupport: false, inlineRequires: true, }, }), }, resolver: { assetExts: assetExts.filter(ext => ext !== "svg"), sourceExts: [...sourceExts, "svg"] } }; })();
- 环境信息:
- React Native 版本:0.67.4
- 系统:macOS 12.3.1,M1芯片
- Node版本:16.14.2,Yarn版本:1.22.19
- Xcode版本:13.4
- 未安装Watchman
排查修复步骤
1. 修复Xcode打包脚本的路径错误
现有Bundle脚本存在两个问题:
- 重复传入入口参数:既在
EXTRA_PACKAGER_ARGS里指定了entry-file,又给脚本传了位置参数,会导致参数冲突 - 相对路径解析错误:脚本执行时工作目录是iOS项目目录,直接传
packages/app/index.js会找不到对应文件,且没有指定NODE_PATH,monorepo下的依赖容易解析失败
替换脚本内容为:
set -e export NODE_BINARY=node # 切到monorepo根目录执行,避免相对路径错误 cd ../../../ ./node_modules/react-native/scripts/react-native-xcode.sh \ --entry-file packages/app/index.js \ --reset-cache
2. 补全Metro配置,解决共享包漏打问题
现有Metro配置只指定了projectRoot,没有声明watchFolders和node_modules路径,release模式下静态打包时会漏打入common包的代码,导致bundle执行早期报错,触发AppRegistry未注册的崩溃。
替换metro.config.js内容为:
const {getDefaultConfig} = require("metro-config"); const path = require('path'); const projectRoot = __dirname; const monorepoRoot = path.resolve(projectRoot, '../../'); module.exports = (async () => { const { resolver: {sourceExts, assetExts} } = await getDefaultConfig(projectRoot); return { projectRoot: monorepoRoot, // 显式声明需要监听的目录,确保共享包代码被打入bundle watchFolders: [ path.resolve(monorepoRoot, 'packages/common'), path.resolve(projectRoot, 'node_modules') ], transformer: { babelTransformerPath: require.resolve("react-native-svg-transformer"), getTransformOptions: async () => ({ transform: { experimentalImportSupport: false, inlineRequires: true, }, }), }, resolver: { assetExts: assetExts.filter(ext => ext !== "svg"), sourceExts: [...sourceExts, "svg"], // 明确指定node_modules路径,避免依赖解析错误 nodeModulesPaths: [path.resolve(monorepoRoot, 'node_modules')] } }; })();
3. 安装缺失依赖、清理缓存
本地未安装Watchman会导致Metro文件遍历异常,执行以下命令修复:
brew install watchman # 清理Metro缓存、Xcode构建缓存 watchman watch-del-all rm -rf node_modules/.cache cd packages/app/ios xcodebuild clean
4. 手动验证bundle生成
先在monorepo根目录手动执行bundle打包命令,确认bundle生成正常,再通过Xcode打包:
npx react-native bundle \ --entry-file packages/app/index.js \ --platform ios \ --dev false \ --bundle-output packages/app/ios/main.jsbundle \ --assets-dest packages/app/ios
- 如果命令执行过程中报模块找不到的错误,对应调整Metro的resolver配置即可
- 生成的main.jsbundle大小正常(一般几百KB到数MB)说明bundle本身无损坏
5. 检查构建阶段资源配置
确认Xcode Build Phases > Copy Bundle Resources列表中包含main.jsbundle,避免打包时bundle文件没有被拷贝进App安装包,导致启动时读不到正确的JS代码。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

