FEATURED · 精选文章

Expo 预编译体系:将第三方 React Native 包转换为预构建 XCFramework 的完整实操流程

发布时间 / 2026/9/10 15:33:12
来源 / 创域科博编辑部
栏目 / 资讯中心
Expo 预编译体系:将第三方 React Native 包转换为预构建 XCFramework 的完整实操流程 Expo 预编译体系将第三方 React Native 包转换为预构建 XCFramework 的完整实操流程【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo本文基于 Expo 仓库内的技能文档 convert-external-package.md讲解如何把一个外部 React Native 包接入 Expo iOS precompile 系统、以 Swift Package Manager 预构建为 XCFramework。读完后你将掌握五步转换流程分析包结构、修复头文件导入、包裹 podspec、编写 spm.config.json、生成 patch以及try_link_with_prebuilt_xcframework在 CocoaPods 集成层的真实实现逻辑并能在仓库中找到全部可对照的参考配置。一、这套流程在整个预编译体系中的位置et prebuild是 Expo 仓库内的 iOS 预编译工具链其职责是从spm.config.json定义出发构建 XCFramework 产物并支持可选的校验与签名。核心工具链位于 tools/src/prebuilds 目录整体构建流程为发现并校验目标包与构建选项解析版本与本地 tarball 输入按 flavorDebug/Release解析产物缓存对每个package/productflavor单元生成源码与Package.swift→ 构建 framework → 合成 XCFramework → 校验产物打印摘要必要时写错误日志。这一流程在 tools/src/prebuilds/README.md 中有完整定义。外部包第三方 React Native 包与 Expo 自家包共用同一条构建管线ExternalPackage类实现了SPMPackageSource接口与 Expo 的Package类地位对等见 tools/src/prebuilds/ExternalPackage.ts。因此“把外部包转换为可预构建形态”本质上就是为它补全三份输入一份spm.config.json配置放在packages/expo-modules-autolinking/external-configs/ios/PACKAGE/spm.config.json对包 podspec 的修改包源码编译逻辑被try_link_with_prebuilt_xcframework条件包裹对包源码中头文件导入问题的修复。而 convert-external-package.md 这份文档正是规定“这三份输入如何产出”的标准化作业流程。当前仓库中已落地 7 个外部包配置可作为对照样本react-native-async-storage/async-storage、shopify/react-native-skia、react-native-reanimated、react-native-safe-area-context、react-native-screens、react-native-svg、react-native-worklets完整清单见 external-configs/ios/README.md。二、任务规则允许做什么禁止做什么文档为转换流程设定了明确的边界约束执行时必须遵守只能创建/编辑三类文件packages/expo-modules-autolinking/external-configs/ios/PACKAGE/spm.config.json、node_modules/中的 podspec、以及node_modules/中需要修复头文件的源文件永远不要手工编写 patch 文件。所有对node_modules/的修改必须通过npx patch-package PACKAGE生成补丁永远不要构建或测试产物。不要运行et prebuild-packages或pod install输出保持最小化——只输出简短的 CLI 风格状态信息在npx patch-package成功运行后立即停止那是最后一步。这些规则的意图很清晰转换流程本身只负责“准备好可预构建的输入”真正构建、验证交给统一的et prebuild管线其产物输出到packages/precompile/.build/package-name/output/flavor/xcframeworks/依赖缓存在packages/precompile/.cache/下避免转换者各自构建导致的环境不一致。三、第一步分析包结构读取包的 podspec 与package.json确定以下关键信息Pod 名称、codegen 名称、源码目录、语言混合情况Swift/ObjC/ObjC/C、依赖的框架检查头文件导入问题是否存在#import RCTFabricComponentsPlugins.h、是否存在相对路径../导入。这些判断直接决定后续步骤的工作量判断项检查方式影响Pod 名称podspec 中的s.name决定 product 的podName字段Codegen 名称package.json的codegenConfig.name决定是否需要 codegen targets 及moduleName取值语言混合统计.swift/.m/.mm/.cpp文件决定 SPM 拆分为多少个 targetSPM 要求不同语言使用独立 target头文件导入问题搜索RCTFabricComponentsPlugins.h、../导入决定是否需要修改node_modules/源码并生成 patch四、第二步修复头文件导入如需要直接编辑node_modules/中的源文件。文档给出三类标准修复模式模式 A—#import RCTFabricComponentsPlugins.h改为模块化导入#import React/RCTFabricComponentsPlugins.h原因是预构建的 XCFramework 中 React 是独立模块包内源码不能再以裸文件名方式引用 React 私有头文件。模式 B— 相对父级导入加__has_include保护#if __has_include(../Foo.h) #import ../Foo.h #else #import Foo.h #endif这样同一份源码在 CocoaPods 源码编译保留相对目录结构与 SPM 预构建头文件被重新组织两种形态下都能编译。模式 C— 跨模块边界被 ObjC 使用的 Swift 类/方法需要open/publicopen class MyView: RCTView { override public func view() - UIView! {CocoaPods 中所有源文件编译进同一个静态库internal即可互见而 SPM 预构建把包切成多个 target/模块后跨模块可见性必须显式提升。五、第三步用 try_link_with_prebuilt_xcframework 包裹 podspec编辑node_modules/中的 podspec把源码编译相关配置包进条件块# Expo prebuilt xcframework support if !Expo::PackagesConfig.instance.try_link_with_prebuilt_xcframework(s) # Build from source s.source_files ... s.exclude_files ... s.pod_target_xcconfig { ... } s.dependency ... s.subspec ... do |ss| ... end end条件块内部只能放源码编译相关属性s.source_files、s.exclude_files、s.pod_target_xcconfig、s.xcconfig、s.dependency、subspec、s.resource_bundles。条件块外部保留install_modules_dependencies(s)、s.requires_arc、s.swift_version、s.platforms、s.source、s.license、s.author、s.homepage。这一模式的运行语义可以从 CocoaPods 集成层源码得到印证。Expo::PackagesConfig.try_link_with_prebuilt_xcframework只是委托调用见 packages_config.rb真正实现位于 precompiled_modules.rb当预构建产物可用时它会返回true并把spec.source指到本地 tarball URI、设置vendored_frameworks指向预构建产物、为被跳过的依赖补充FRAMEWORK_SEARCH_PATHS、注入prepare_command与构建期 debug/release 切换脚本——此时条件块内部的源码编译配置全部被跳过当产物不可用时返回falsePod 回落到块内原有的源码编译路径。这正是“预构建优先、源码兜底”双形态机制的核心。六、第四步创建 spm.config.json在packages/expo-modules-autolinking/external-configs/ios/PACKAGE/spm.config.json写入配置。该文件的字段级定义见 JSON Schemaspm.config.schema.json。由于配置文件位于ios/PACKAGE/两层子目录下实际落库配置中$schema使用五级相对路径指向 Schema可对照 react-native-screens 的配置$schema: ../../../../../tools/src/prebuilds/schemas/spm.config.schema.json6.1 Product 层字段name产品名 pod 名即最终 XCFramework 名称podName必须与 podspec 中s.name完全一致codegenName包使用 codegen 时必填取package.json的codegenConfig.nameplatforms通常为[iOS(.v15)]Schema 的枚举值还包括iOS(.v16)、iOS(16.4)、macOS(.v11)、tvOS(.v15)、macCatalyst(.v15)externalDependencies常规取[ReactNativeDependencies, React, Hermes]凡含 Fabric 组件或 TurboModules 的包必须包含HermesJSI 符号来自 Hermes。6.2 Codegen targets 的路径约定当package.json中存在codegenConfig时codegen 产物固定放在两个位置C 组件.build/codegen/build/generated/ios/ReactCodegen/react/renderer/components/codegenNametarget 的moduleName必须等于codegenNameObjC 模块.build/codegen/build/generated/ios/ReactCodegen/codegenName同样设置moduleName。所有 codegen 相关 target 必须带moduleName字段其作用是告知构建系统当 XCFramework 被使用时把这部分源码从 ReactCodegen 中排除避免重复编译。6.3 target 类型与依赖规则类型适用源文件标准依赖cpp.cpp/.c[React, ReactNativeDependencies]objc.m/.mm[Hermes, React, ReactNativeDependencies]swift.swift[Hermes, React, ReactNativeDependencies, expo-modules-core/ExpoModulesCore]配套的两个通用约定ObjC 的常用编译标志[-include, Foundation/Foundation.h]Schema 中compilerFlags还支持{ common: [...], debug: [...], release: [...] }结构化写法以及按c/cxx语言分别指定标志常用排除项[**/*.macos.*, PodName.xcodeproj/**]。6.4 真实配置对照react-native-screensSwift ObjC C codegen 混合react-native-screens/spm.config.json 展示了混合语言包的完整 target 拓扑RNScreens_codegen_componentscpp、RNScreens_codegen_modulesobjc、RNScreens_common_cppcpp、RNScreens_cppcpp四个子 target 全部声明了moduleNamernscreens或rnscreens_turbo主 targetRNScreensobjcpattern: **/*.{m,mm}依赖上面全部 target 并声明linkedFrameworks: [Foundation, UIKit, QuartzCore, CoreGraphics]。它同时演示了两个进阶字段fileMapping把react/renderer/components/rnscreens/*.h等头文件重新映射到rnscreens/目录并用type: symlink建立react/renderer/components/rnscreens目录软链让源码中按原路径的#import继续可用moduleMapContent为 C 头文件提供自定义 module map把全部头标记为textual header这些头依赖 JSI 宏与包含它的编译单元不能被独立预编译。产品层还通过excludeFromUmbrella排除了Swift-Bridging.h、RNScreens-Bridging-Header.h等桥接头——因为 SPM 不支持 CocoaPods 风格的 bridging header这些文件不能进入自动生成的 umbrella header。6.5 真实配置对照react-native-reanimated跨包依赖与结构化编译标志react-native-reanimated/spm.config.json 展示了两个典型场景跨包依赖externalDependencies中加入了RNWorkletsreact-native-worklets 的 product 名而非 npm 包名主 target 与 C target 的dependencies也引用它。预编译时这类依赖会被解析为对应包的 xcframework 头文件路径而非源码路径结构化 compilerFlagsRNReanimated_cpptarget 使用{ common: { c: [...], cxx: [...] }, debug: [...] }形式其中 cxx 侧单独加了-fno-cxx-modules规避 C 模块化的system_clock报错debug 侧加了-DHERMES_ENABLE_DEBUGGER1另外REANIMATED_FEATURE_FLAGS这类含引号宏在 JSON 中写作\\\...\\\转义后进入生成的Package.swift。七、第五步运行 patch-package 并停止对node_modules/中 podspec 与源文件的所有修改最后统一生成补丁npx patch-package PACKAGE补丁文件落盘在仓库patches/package-nameversion.patch仓库已有 react-native-reanimated、shopify/react-native-skia 等外部包补丁可参考。此处即为流程终点不构建、不测试、不继续。构建与验证由et prebuild管线统一承担例如见 external-configs/ios/README.md# 构建外部包的 XCFramework--include-external 表示纳入外部包 et prebuild --include-external react-native-screens # 检查产物 ls -la packages/precompile/.build/react-native-screens/output/debug/xcframeworks/若修改了exclude模式或 target 结构后出现旧符号残留可用et prebuild --clean --include-external package-name清理旧构建输出——SPM 生成器只创建新符号链接不会移除之前被包含、现已被排除的文件。八、参考包与关键源码索引文档建议以已转换包作为目标结构参照其中 ObjC-only codegenreact-native-gesture-handler、ObjC 自定义 C shadow nodesreact-native-svg、混合 Swift ObjC SPM 远程依赖lottie-react-native、混合 Swift ObjC C codegenreact-native-screens、ObjC C codegenreact-native-safe-area-context五类结构覆盖了常见形态。需要说明的是从当前仓库结构看external-configs/ios/下实际落盘的配置为前文列出的 7 个包文档提到的 lottie-react-native、react-native-gesture-handler 配置未包含在本快照该目录中研究目标结构时建议以在库配置为准lottie 场景Swift/ObjC 拆分 spmPackages远程依赖的完整字段说明可在 external-configs/ios/README.md 的示例 3 中找到。深入理解该流程时建议按以下顺序阅读源码关注点文件外部包发现与解析tools/src/prebuilds/ExternalPackage.tsPackage.swift生成tools/src/prebuilds/SPMPackage.tsCodegen 处理tools/src/prebuilds/Codegen.ts管线入口与模块职责tools/src/prebuilds/README.mdpodspec 预构建链接实现packages/expo-modules-autolinking/scripts/ios/precompiled_modules.rb配置字段级定义tools/src/prebuilds/schemas/spm.config.schema.json九、转换完成前的自检清单结合文档规则与 Schema 约束一份合格的转换产物应满足spm.config.json位于packages/expo-modules-autolinking/external-configs/ios/PACKAGE/$schema指向 tools/src/prebuilds/schemas/spm.config.schema.jsonproduct 的podName与 podspecs.name一致codegenName与package.json的codegenConfig.name一致所有 codegen target 的moduleName等于 codegen 名target 的type与源文件语言匹配cpp/objc/swiftpathpattern精确选中目标源文件exclude过滤掉 xcodeproj 与 macOS 专属文件podspec 已用try_link_with_prebuilt_xcframework条件包裹源码编译属性且块外保留s.platforms、s.source等元信息头文件问题已按模式 A/B/C 修复Swift 跨模块类已提升为open/public补丁由npx patch-package PACKAGE生成而非手写且流程在补丁生成后即终止。满足以上各点后该包即可交由et prebuild管线统一预构建与 Expo 自家模块以完全相同的流程产出 XCFramework 并进入packages/precompile/.cache/的共享依赖缓存体系。【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻