129 lines
4.0 KiB
Markdown
129 lines
4.0 KiB
Markdown
# FFI Troubleshooting
|
||
|
||
## UniFFI 版本
|
||
|
||
本项目使用 UniFFI 0.31,采用 **UDL + proc-macro 混合模式**:
|
||
- UDL(`zx_document.udl`):定义类型和函数签名
|
||
- `#[uniffi::export]` proc-macro:生成 C ABI 分发符号
|
||
- `uniffi-bindgen`:生成 Swift/Kotlin 绑定代码
|
||
|
||
## 常见问题
|
||
|
||
### `No such module 'ZxDocumentRuntime'`
|
||
|
||
**原因**:XCFramework 未正确添加到 Xcode 项目。
|
||
|
||
**解决**:
|
||
1. 确认 `bindings/ios/ZxDocumentRuntime.xcframework` 已生成
|
||
2. 在 Xcode → Target → General → Frameworks 中添加
|
||
3. 确保 Embed 设置为 `Do Not Embed`(静态库)
|
||
|
||
### `Undefined symbol: _ffi_zx_document_ffi_*`
|
||
|
||
**原因**:C ABI 符号未导出。proc-macro 生成的符号名称可能与链接器期望不一致。
|
||
|
||
**解决**:
|
||
1. 确认 `crates/zx_document_ffi/src/lib.rs` 中 `#[no_mangle] pub extern "C"` 函数存在
|
||
2. 检查 Cargo.toml `crate-type = ["lib", "staticlib", "cdylib"]`
|
||
3. 运行 `nm target/aarch64-apple-ios/release/libzx_document_ffi.a | grep ffi_` 验证符号
|
||
4. 确认 UDL 中声明的函数与 `#[uniffi::export]` 函数名称一致
|
||
|
||
### `Library not found for -lzx_document_ffi`
|
||
|
||
**原因**:Library Search Paths 未设置。
|
||
|
||
**解决**:
|
||
1. Build Settings → Library Search Paths → 添加 `$(PROJECT_DIR)/bindings/ios`
|
||
2. 或使用绝对路径指向 `.xcframework` 所在目录
|
||
|
||
### UniFFI checksum mismatch
|
||
|
||
**原因**:UDL 文件更新后未重新生成 Swift binding。
|
||
|
||
**解决**:
|
||
```bash
|
||
uniffi-bindgen generate \
|
||
--language swift \
|
||
--out-dir bindings/ios/generated \
|
||
crates/zx_document_ffi/src/zx_document.udl
|
||
```
|
||
|
||
### `setup_scaffolding!()` not called
|
||
|
||
**原因**:`uniffi::setup_scaffolding!()` 必须在 lib.rs 的第一行调用。
|
||
|
||
**解决**:确认 `crates/zx_document_core/src/lib.rs` 第一行是 `uniffi::setup_scaffolding!();`
|
||
|
||
### 新增类型/函数后 Swift 不可见
|
||
|
||
**原因**:只在 Rust 侧添加了类型/函数,但未更新:
|
||
1. UDL 文件声明
|
||
2. FFI crate `pub use` 重导出
|
||
3. Swift binding 重新生成
|
||
|
||
**完整的 FFI 接入步骤**:
|
||
```bash
|
||
# 1. Rust 侧添加类型 + 函数 + UDL 声明
|
||
# 2. FFI crate: pub use 重导出 + #[uniffi::export] 包装
|
||
# 3. 重新生成 Swift
|
||
uniffi-bindgen generate --language swift \
|
||
--out-dir bindings/ios/generated \
|
||
crates/zx_document_ffi/src/zx_document.udl
|
||
# 4. 验证编译
|
||
cargo build --release --target aarch64-apple-ios -p zx_document_ffi
|
||
```
|
||
|
||
### `cargo build` 目标平台不匹配
|
||
|
||
**原因**:macOS 上构建 iOS 目标需要 Apple Silicon target。
|
||
|
||
**解决**:
|
||
```bash
|
||
rustup target add aarch64-apple-ios aarch64-apple-ios-sim
|
||
```
|
||
|
||
### `infer` / `comrak` / `zip` crate 编译失败
|
||
|
||
**原因**:某些 crate 在 iOS target 下有 C 依赖。
|
||
|
||
**解决**:
|
||
- `comrak`:需要启用 `--features` 中的 `onig` 或使用 `syntect` 后端
|
||
- `zip`:纯 Rust,无特殊要求
|
||
- 确保 `CC` 环境变量指向 Xcode toolchain
|
||
|
||
### XCFramework 构建注意事项
|
||
|
||
1. 必须分别编译 device 和 simulator 目标
|
||
2. `lipo` 不需要(两个 .a 文件各自是 fat binary)
|
||
3. header 文件由 `uniffi-bindgen` 生成:`bindings/ios/generated/zx_documentFFI.h`
|
||
4. modulemap 文件创建:
|
||
```
|
||
framework module ZxDocumentRuntime {
|
||
header "zx_documentFFI.h"
|
||
export *
|
||
}
|
||
```
|
||
|
||
### UDL 类型不支持
|
||
|
||
**问题类型**:
|
||
- `Vec<(String, &str)>` — 不支持 tuple
|
||
- 泛型函数 — 不支持
|
||
- 生命周期参数 — 不支持
|
||
|
||
**解决**:在 FFI 层创建包装类型。例如 `search_pdf_text(&[(u32, &str)])` → FFI 包装为 `search_pdf_pages(Vec<u32>, Vec<String>)`。
|
||
|
||
### Rust 与 iOS 持有引用
|
||
|
||
UniFFI 生成的对象是 **值类型**(序列化跨 FFI 边界传递),不是引用类型。Rust 侧的 `Mutex` 保护全局状态,iOS 侧通过函数调用读写状态。
|
||
|
||
### 验证清单
|
||
|
||
- [ ] `cargo build --release --target aarch64-apple-ios` 通过
|
||
- [ ] `cargo build --release --target aarch64-apple-ios-sim` 通过
|
||
- [ ] `nm` 验证 C ABI 符号存在
|
||
- [ ] Swift binding 生成并编译通过
|
||
- [ ] XCFramework 包含 device + simulator slices
|
||
- [ ] header + modulemap 正确
|
||
- [ ] demo App 链接成功
|