Some checks failed
CI/CD Pipeline / Lint (push) Has been cancelled
CI/CD Pipeline / Test (push) Has been cancelled
CI/CD Pipeline / Build (push) Has been cancelled
CI/CD Pipeline / Release (macos-15, aarch64-apple-ios, aarch64-apple-ios) (push) Has been cancelled
CI/CD Pipeline / Release (macos-15, aarch64-apple-ios-sim, aarch64-apple-ios-sim) (push) Has been cancelled
CI/CD Pipeline / Package xcframework (push) Has been cancelled
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
6.4 KiB
6.4 KiB
UniFFI UDL 编写规范 + 代码生成流程
1. 概述
UniFFI(Universal Foreign Function Interface)是 Mozilla 开发的跨语言绑定生成工具。本项目使用 UniFFI 0.31 从 Rust 生成 Swift 绑定。
UDL(UniFFI Definition Language)文件定义跨语言接口。
2. UDL 语法规范
2.1 命名空间
namespace zx_document {
// 导出函数
};
命名空间名即 Swift 模块名(zx_document)。
2.2 函数声明
// 基础函数
MaterialType detect_material_type([ByRef] string file_path);
// 可抛异常
[Throws=DocumentError]
DocumentInfo build_document_info([ByRef] string file_path, string material_id, string title);
// 无返回值
void push_reading_event(ReadingEvent event);
参数修饰符
| 修饰符 | 适用类型 | 说明 |
|---|---|---|
[ByRef] |
string |
传递引用而非所有权转移(性能优化) |
| 无修饰符 | 所有类型 | 所有权转移给被调用方 |
2.3 数据类型
Dictionary(Record → Swift struct)
dictionary DocumentInfo {
string material_id;
string title;
MaterialType material_type;
u32? page_count; // Optional
string? image_format; // Optional
};
// Rust 侧: #[derive(uniffi::Record)] pub struct DocumentInfo { ... }
// Swift 侧: public struct DocumentInfo { public var materialId: String, ... }
命名规则:
- UDL:
snake_case→ Swift:camelCase - 示例:
material_id→materialId
Enum(Rust enum → Swift enum)
[Enum]
interface MaterialType {
Markdown();
Text();
Pdf();
Image();
Epub();
Word();
Excel();
PowerPoint();
Unknown();
};
带数据的枚举:
[Enum]
interface ReadingPosition {
Markdown(string block_id, f32 scroll_progress);
Text(u32 line_number, f32 scroll_progress);
Pdf(u32 page_number, f32 page_progress, f32 overall_progress);
Unknown(); // 无数据变体
};
Error(Rust enum → Swift Error)
[Error]
enum DocumentError {
"FileNotFound",
"UnsupportedFormat",
"ParseError",
"InvalidEncoding",
"IoError",
};
对应 Rust:
#[derive(Debug, thiserror::Error, uniffi::Error)]
pub enum DocumentError {
#[error("File not found")] FileNotFound,
#[error("Unsupported format")] UnsupportedFormat,
// ...
}
2.4 类型映射表
| UDL | Rust | Swift |
|---|---|---|
string |
String |
String |
u32 |
u32 |
UInt32 |
u64 |
u64 |
UInt64 |
i64 |
i64 |
Int64 |
f32 |
f32 |
Float |
boolean |
bool |
Bool |
T? |
Option<T> |
T? |
sequence<T> |
Vec<T> |
[T] |
dictionary |
#[derive(Record)] struct |
struct |
[Enum] interface |
#[derive(Enum)] enum |
enum |
[Error] enum |
#[derive(Error)] enum |
Error |
3. 新增类型 Checklist
3.1 Rust 侧
- 在
zx_document_core/src/定义类型#[derive(Debug, Clone, Serialize, Deserialize, uniffi::Record)] pub struct NewType { ... } - 在
zx_document_core/src/lib.rs添加pub mod new_module; - 在
zx_document_ffi/src/lib.rs添加pub use zx_document_core::new_module::NewType;
3.2 UDL 侧
- 在
zx_document_ffi/src/zx_document.udl末尾添加类型定义 - 如新增函数,在
namespace zx_document { }内声明
3.3 代码生成
# 从 workspace root 执行
cd zhixi-document-runtime
# 生成 Swift 绑定
uniffi-bindgen generate \
crates/zx_document_ffi/src/zx_document.udl \
--language swift \
--out-dir ../ios-projects/AIStudyApp/AIStudyApp/Core/
# 检查生成结果
ls ../ios-projects/AIStudyApp/AIStudyApp/Core/zx_document.swift
3.4 验证
# Rust 编译
cargo build --release -p zx_document_ffi
# Rust 测试
cargo test -p zx_document_core
# iOS 编译(在 ios-projects 目录)
xcodebuild build -project AIStudyApp.xcodeproj -scheme AIStudyApp -destination 'platform=iOS Simulator,name=iPhone 16'
4. 常见陷阱
4.1 类型未导出
症状:error: cannot find type X in this scope
原因:Rust 类型未从 zx_document_ffi 重新导出
// zx_document_ffi/src/lib.rs — 必须添加
pub use zx_document_core::new_module::NewType;
4.2 UDL 字段名不一致
症状:uniffi-bindgen 报 field name mismatch
原因:UDL 字段名必须与 Rust struct 字段名一致(Rust 用 snake_case → UDL 也用 snake_case)
dictionary Foo {
string bar_baz; // ✅ 对应 Rust foo.bar_baz
string barBaz; // ❌ 不匹配
};
4.3 UniFFI 版本兼容
| UniFFI 版本 | Rust crate | CLI |
|---|---|---|
| 0.31 | uniffi = "0.31" |
uniffi-bindgen 0.31 |
版本必须一致。检查:
cargo tree -p zx_document_ffi | grep uniffi
uniffi-bindgen --version
4.4 Optional 类型
// UDL: T? = Rust: Option<T>
u32? page_count; // → Swift: public var pageCount: UInt32?
sequence<string>? items; // → Swift: public var items: [String]?
5. 生成代码结构
// zx_document.swift (auto-generated by uniffi-bindgen)
// 1. Type definitions
public struct DocumentInfo { ... }
public enum MaterialType { ... }
// 2. Protocol + FFI callback
private protocol zx_documentProtocol { ... }
// 3. Top-level functions
public func detectMaterialType(filePath: String) throws -> MaterialType { ... }
public func buildDocumentInfo(filePath: String, materialId: String, title: String) throws -> DocumentInfo { ... }
// 4. Internal FFI bridge
private var ffi_zx_document_ffi_*: ...;
不要手动修改 zx_document.swift。所有修改在 Rust 侧完成后重新生成。
6. 当前接口统计
| 类别 | 数量 | 示例 |
|---|---|---|
| 顶层函数 | 28 | detect_material_type, parse_markdown |
| Record(struct) | 10 | DocumentInfo, SearchResult |
| Enum | 7 | MaterialType, ReadingPosition |
| Error 变体 | 5 | FileNotFound, ParseError |
| V2 Session 函数 | 6 | start_reading_session_v2 |
| V2 Event 函数 | 5 | push_heartbeat_v2 |
| V2 Buffer 函数 | 4 | export_pending_events_v2 |
7. 相关文档
- iOS FFI 调用指南
- 文件格式支持矩阵
- Rust FFI 绑定更新流程(ios-projects 仓库)
- UniFFI 官方文档