7.1 KiB
构建问题记录
问题 1:expo prebuild 报 EEXIST 目录冲突
现象:npx expo prebuild --platform ios 报错 EEXIST: file already exists, mkdir 'ios/DuooomiBleSDK'
原因:macOS 文件系统大小写不敏感。Expo prebuild 先创建 ios/duooomiblesdk(小写),然后 expo-brownfield 插件尝试创建 ios/DuooomiBleSDK(驼峰),被视为同一目录导致 EEXIST。
修复:patch expo-brownfield 的 plugin/build/ios/utils/filesystem.js,将 mkdir 的 recursive 参数默认值改为 true:
// 修改前
const mkdir = (path, recursive = false) => {
// 修改后
const mkdir = (path, recursive = true) => {
注意:这是 node_modules 内的 patch,pnpm install 后会丢失。如需持久化,使用 pnpm patch expo-brownfield。
问题 2:CocoaPods ffi_c 加载失败
现象:pod install 报错 LoadError - cannot load such file -- ffi_c
原因:终端运行在 Rosetta2 x86_64 模拟环境下,导致 Ruby gem 的原生扩展(ffi、nkf)架构与解释器不匹配。
修复:强制使用 arm64 原生架构运行:
env /usr/bin/arch -arm64 /bin/bash --login -c 'source ~/.rvm/scripts/rvm && rvm use 2.7.5 && pod install --project-directory=ios'
问题 3:Xcode 26 编译 fmt 库失败(已解决)
现象:BUILD FAILED,错误为 call to consteval function ... is not a constant expression,出现在 fmt/format-inl.h
原因:Xcode 26 的 Apple Clang 17+ 声明支持 __cpp_consteval,fmt 11.0.2 据此启用 consteval 关键字,但编译器实现存在 bug 导致编译失败。
修复:patch ios/Pods/fmt/include/fmt/base.h,在 consteval 检测逻辑中禁用 Apple Clang 17+:
// 在 __apple_build_version__ < 14000029L 判断之后添加:
#elif defined(__apple_build_version__) && __apple_build_version__ >= 17000000L
# define FMT_USE_CONSTEVAL 0 // consteval is broken in Apple clang 17+ (Xcode 26).
注意:pod install 会重新下载 Pods,此 patch 需要在每次 pod install 后重新应用。
问题 4:签名错误(已解决)
现象:Signing for "duooomiblesdk" requires a development team
原因:Expo Brownfield 构建过程会编译宿主 App target,Xcode 对 App target 强制要求签名。
修复:xcodebuild 参数中加 CODE_SIGNING_ALLOWED=NO。
问题 5:RCTAppDependencyProvider.h 缺失(已解决)
现象:The file "RCTAppDependencyProvider.h" couldn't be opened because there is no such file
原因:React Native codegen 没有自动运行,需要手动触发。
前置依赖:
pnpm add -D @react-native-community/cli
修复:在构建前手动运行 codegen:
npx react-native codegen --platform ios --outputPath ios
问题 6:Xcode 16 不兼容 Expo SDK 55(已确认)
现象:unknown attribute 'MainActor' 等 Swift 编译错误
原因:Expo SDK 55 的 expo-modules-core 使用了 Swift 6 特性,需要 Xcode 26 的 Swift 编译器。Xcode 16 只有 Swift 5.x,无法编译。
结论:必须使用 Xcode 26,不能降级到 Xcode 16。配合问题 3 的 fmt patch 解决 C++ 兼容性。
构建完整流程(已验证通过)
# 1. 安装依赖
pnpm install
# 2. Patch expo-brownfield mkdir(每次 pnpm install 后需重做)
# 文件:node_modules/.pnpm/expo-brownfield@.../plugin/build/ios/utils/filesystem.js
# 修改:recursive = false → recursive = true
# 3. Prebuild
rm -rf ios
npx expo prebuild --platform ios
# prebuild 内部的 pod install 会因 ffi 失败,忽略
# 4. Pod install(arm64 环境)
env /usr/bin/arch -arm64 /bin/bash --login -c 'source ~/.rvm/scripts/rvm && rvm use 2.7.5 && pod install --project-directory=ios'
# 5. Patch fmt consteval(每次 pod install 后需重做)
# 文件:ios/Pods/fmt/include/fmt/base.h
# 在 __apple_build_version__ < 14000029L 判断后添加 >= 17000000L 的判断
# 6. 运行 codegen
npx react-native codegen --platform ios --outputPath ios
# 7. 构建真机 + 模拟器(使用 DEVELOPER_DIR 指定 Xcode 26,无需 sudo 切换)
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer
# 真机
xcodebuild -workspace ios/duooomiblesdk.xcworkspace -scheme DuooomiBleSDK \
-derivedDataPath ios/build -sdk iphoneos -arch arm64 \
-configuration Release CODE_SIGNING_ALLOWED=NO
# 模拟器
xcodebuild -workspace ios/duooomiblesdk.xcworkspace -scheme DuooomiBleSDK \
-derivedDataPath ios/build -sdk iphonesimulator -arch arm64 -arch x86_64 \
-configuration Release CODE_SIGNING_ALLOWED=NO
# 8. 合并为 XCFramework
xcodebuild -create-xcframework \
-framework ios/build/Build/Products/Release-iphoneos/DuooomiBleSDK.framework \
-framework ios/build/Build/Products/Release-iphonesimulator/DuooomiBleSDK.framework \
-output artifacts/DuooomiBleSDK.xcframework
# 9. 产物(约 75MB)
# artifacts/DuooomiBleSDK.xcframework (约 75MB)
# ├── ios-arm64/ ← 真机
# ├── ios-arm64_x86_64-simulator/ ← 模拟器
# └── Info.plist
问题 7:运行时 hermesvm.framework 缺失(已解决)
现象:App 启动 crash,dyld: Library not loaded: @rpath/hermesvm.framework/hermesvm
原因:DuooomiBleSDK.framework 动态链接 hermesvm.framework(Hermes JS 引擎),但构建 XCFramework 时只输出了 SDK 本身,没有包含 Hermes。
修复:将 hermesvm.xcframework 也复制到产物目录,宿主 App 需要同时嵌入两个 framework:
# 复制 hermesvm 到 artifacts
cp -R ios/Pods/hermes-engine/destroot/Library/Frameworks/universal/hermesvm.xcframework artifacts/
# 宿主 App 的 project.yml 中添加:
dependencies:
- framework: path/to/DuooomiBleSDK.xcframework
embed: true
- framework: path/to/hermesvm.xcframework
embed: true
CLAUDE.md 与实际代码的偏差
CLAUDE.md 中描述的 API 设计意图与 BleSDK.ts 实际实现存在以下差异,后续需要统一:
状态 key 不一致
CLAUDE.md 设计为单个 deviceState 对象,但实际实现使用多个独立 key:
btState— 连接状态connectedDevice— 已连接设备deviceInfo— 设备详情version— 固件版本isActivated— 是否激活transferProgress— 传输进度error— 错误信息discoveredDevices— 发现的设备列表
Action 名称不一致
| CLAUDE.md 描述 | 实际代码 |
|---|---|
init |
无此 action,SDK 在 RN mount 时自动初始化 |
syncFile |
transferFile(参数为 { fileUri, commandType }) |
| connect 内自动 bind | bind 是独立 action |
| connect 内自动 getInfo | getDeviceInfo / getVersion 是独立 action |
集成注意事项
- 必须显式调用
ReactNativeHostManager.shared.initialize(),ExpoBrownfieldAppDelegate不会自动调用 - 必须加载
ReactNativeView(即使隐藏),否则 JS 运行时不启动,sendMessage 无人接收 BrownfieldState.subscribe返回的AnyCancellable必须持有引用,否则订阅立即失效- subscribe 回调在 RN 桥接线程,更新 UI 需手动
DispatchQueue.main.async