From 89688582115664b2a76268b0a1084a754d8e8213 Mon Sep 17 00:00:00 2001 From: wangdl Date: Thu, 18 Jun 2026 14:34:59 +0800 Subject: [PATCH] docs: add iOS FFI integration guide (DOC-FULL-027) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Architecture: Swift→uniffi C-ABI→Rust FFI→core - Type mapping: Rust struct/enum ↔ Swift Record/Enum - Core API reference: document parsing, search, anchors, V2 sessions - Error handling, thread safety, performance recommendations Co-Authored-By: Claude Opus 4.7 --- docs/ios-ffi-integration-guide.md | 221 ++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 docs/ios-ffi-integration-guide.md diff --git a/docs/ios-ffi-integration-guide.md b/docs/ios-ffi-integration-guide.md new file mode 100644 index 0000000..d5a7c6c --- /dev/null +++ b/docs/ios-ffi-integration-guide.md @@ -0,0 +1,221 @@ +# Document Runtime iOS FFI 调用指南 + +## 1. 概述 + +Document Runtime(zx_document_core)通过 UniFFI 生成 C-ABI 兼容的 Swift 绑定,嵌入 iOS 应用作为 `ZxDocumentRuntime.xcframework`。 + +Swift 层通过 uniffi 生成的 `zx_document.swift` 调用所有 Rust 功能。 + +--- + +## 2. 架构 + +``` +┌──────────────────────────────────────┐ +│ iOS Swift │ +│ zx_document.swift (uniffi 生成) │ +│ ├─ buildDocumentInfo() │ +│ ├─ parseMarkdown() │ +│ ├─ extractPdfText() │ +│ ├─ extractEpubChapterTexts() │ +│ ├─ searchBlocks() / searchText() │ +│ ├─ resolveNoteAnchors() │ +│ ├─ startReadingSessionV2() │ +│ ├─ pushHeartbeatV2() │ +│ ├─ exportPending() / ackExported() │ +│ └─ ... │ +└──────────────┬───────────────────────┘ + │ C-ABI (uniffi) +┌──────────────▼───────────────────────┐ +│ Rust: zx_document_ffi │ +│ lib.rs — #[uniffi::export] fns │ +└──────────────┬───────────────────────┘ + │ +┌──────────────▼───────────────────────┐ +│ Rust: zx_document_core │ +│ document / markdown / pdf / epub / │ +│ search / anchors / events_v2 / ... │ +└──────────────────────────────────────┘ +``` + +--- + +## 3. 类型映射 + +### 3.1 基础类型 + +| Rust | Swift | +|------|-------| +| `String` | `String` | +| `u32` / `u64` | `UInt32` / `UInt64` | +| `i64` | `Int64` | +| `bool` | `Bool` | +| `Vec` | `[T]` | +| `Option` | `T?` | +| `Result` | `throws` | + +### 3.2 结构体 → Record + +```rust +#[derive(uniffi::Record)] +pub struct DocumentInfo { ... } +``` +```swift +// uniffi 生成 +public struct DocumentInfo { + public var materialId: String + public var title: String + public var materialType: MaterialType + // ... +} +``` + +### 3.3 枚举 → Enum + +```rust +#[derive(uniffi::Enum)] +pub enum MaterialType { Markdown, Text, Pdf, Image, Epub, Word, Excel, PowerPoint, Unknown } +``` +```swift +public enum MaterialType { + case markdown, text, pdf, image, epub, word, excel, powerPoint, unknown +} +``` + +--- + +## 4. 核心 API + +### 4.1 文档解析 + +```swift +// 构建文档信息(文件类型检测 + 元数据提取) +let info = try buildDocumentInfo( + filePath: "/path/to/file.md", + materialId: "mat-001", + title: "My Document" +) +// → DocumentInfo { materialType, pageCount, wordCount, epubChapterCount, ... } + +// Markdown 解析为块 +let blocks = try parseMarkdown(markdown: "# Title\n\nContent") +// → [DocumentBlock] + +// PDF 元数据 +let pdfMeta = try readPdfMetadata(filePath: "/path/to/book.pdf") +// → PdfMetadata { pageCount, title, author } + +// PDF 文本提取 +let pages = try extractPdfText(filePath: "/path/to/book.pdf") +// → [PdfPageText { pageNumber, text }] + +// EPUB 元数据 +let epubMeta = try readEpubMetadata(filePath: "/path/to/book.epub") +// → EpubMetadata { title, author, chapterCount } + +// EPUB 章节文本提取(HTML strip) +let chapters = try extractEpubChapterTexts(filePath: "/path/to/book.epub") +// → [EpubChapterText { chapterId, text }] +``` + +### 4.2 搜索 + +```swift +// 搜索 Markdown 块 +let results = searchBlocks(blocks: blocks, query: "keyword") +// → [SearchResult { blockId, snippet, matchStart, matchEnd }] + +// 搜索纯文本 +let textResults = searchText(content: text, query: "keyword") +// → [SearchResult { lineNumber, ... }] +``` + +### 4.3 锚点 + +```swift +let anchors = try resolveNoteAnchors( + blocks: blocks, + anchorHints: ["#heading-1", "#paragraph-2"] +) +// → [NoteAnchor { anchorId, blockId, ... }] +``` + +### 4.4 V2 阅读会话 + +```swift +// 开始会话 +let sessionId = try startReadingSessionV2( + material: ReadingMaterialRefV2(materialId: "mat-001"), + timestampMs: Int64(Date().timeIntervalSince1970 * 1000) +) + +// 推送位置变更事件 +try pushPositionChangedV2( + sessionId: sessionId, + position: ReadingPosition.markdown( + blockId: "b1", + scrollProgress: 0.5 + ), + timestampMs: nowMs() +) + +// 导出待上传事件 +let (events, state) = exportPendingV2() +// → ([BufferedReadingEventV2], EventBufferStateV2) + +// ACK 已上传事件 +ackExportedV2(count: UInt32(events.count)) + +// 关闭会话 +try closeReadingSessionV2(sessionId: sessionId, timestampMs: nowMs()) +``` + +--- + +## 5. 错误处理 + +所有 FFI 函数返回 `Result`(UniFFI 映射为 Swift `throws`): + +```swift +do { + let info = try buildDocumentInfo(filePath: path, materialId: id, title: title) + // 使用 info +} catch { + print("Document Runtime error: \(error)") + // error 是 Rust DocumentError 的 Display 字符串 +} +``` + +常见错误: +- `IoError` — 文件不存在/无权限 +- `ParseError` — 格式损坏/不支持的格式 +- `MarkdownParseError` — Markdown 语法错误 + +--- + +## 6. 线程安全 + +- Rust 层使用 `Mutex>` 保护全局状态(EventBuffer) +- 所有 FFI 函数可从任意线程调用 +- Swift 层应在主线程调用 UI 更新,后台线程调用 FFI + +--- + +## 7. 性能建议 + +| 操作 | 建议 | +|------|------| +| `buildDocumentInfo` | 主线程调用(< 50ms),缓存结果 | +| `extractPdfText` | 后台线程(可能耗时数秒) | +| `parseMarkdown` | 任意线程(< 10ms) | +| `searchBlocks` | 任意线程(< 10ms) | +| `exportPendingV2` | 后台定时器(30s),主线程调用也可 | + +--- + +## 8. 相关文档 + +- [V2 事件 Pipeline 架构](../ios-projects/docs/v2-event-pipeline.md)(ios-projects 仓库) +- [Rust FFI 绑定更新流程](../ios-projects/docs/rust-ffi-update.md)(ios-projects 仓库) +- [UniFFI UDL 编写规范](./uniffi-udl-spec.md) +- [文件格式支持矩阵](./file-format-support-matrix.md)