zhixi-document-runtime/docs/ios-ffi-integration-guide.md
wangdl 8968858211 docs: add iOS FFI integration guide (DOC-FULL-027)
- 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 <noreply@anthropic.com>
2026-06-18 14:34:59 +08:00

6.2 KiB
Raw Blame History

Document Runtime iOS FFI 调用指南

1. 概述

Document Runtimezx_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> [T]
Option<T> T?
Result<T, E> throws

3.2 结构体 → Record

#[derive(uniffi::Record)]
pub struct DocumentInfo { ... }
// uniffi 生成
public struct DocumentInfo {
    public var materialId: String
    public var title: String
    public var materialType: MaterialType
    // ...
}

3.3 枚举 → Enum

#[derive(uniffi::Enum)]
pub enum MaterialType { Markdown, Text, Pdf, Image, Epub, Word, Excel, PowerPoint, Unknown }
public enum MaterialType {
    case markdown, text, pdf, image, epub, word, excel, powerPoint, unknown
}

4. 核心 API

4.1 文档解析

// 构建文档信息(文件类型检测 + 元数据提取)
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 搜索

// 搜索 Markdown 块
let results = searchBlocks(blocks: blocks, query: "keyword")
// → [SearchResult { blockId, snippet, matchStart, matchEnd }]

// 搜索纯文本
let textResults = searchText(content: text, query: "keyword")
// → [SearchResult { lineNumber, ... }]

4.3 锚点

let anchors = try resolveNoteAnchors(
    blocks: blocks,
    anchorHints: ["#heading-1", "#paragraph-2"]
)
// → [NoteAnchor { anchorId, blockId, ... }]

4.4 V2 阅读会话

// 开始会话
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<T, String>UniFFI 映射为 Swift throws

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<Vec<T>> 保护全局状态EventBuffer
  • 所有 FFI 函数可从任意线程调用
  • Swift 层应在主线程调用 UI 更新,后台线程调用 FFI

7. 性能建议

操作 建议
buildDocumentInfo 主线程调用(< 50ms缓存结果
extractPdfText 后台线程(可能耗时数秒)
parseMarkdown 任意线程(< 10ms
searchBlocks 任意线程(< 10ms
exportPendingV2 后台定时器30s主线程调用也可

8. 相关文档