DOC-302 iOS XCFramework 链接失败:UniFFI 0.28 UDL模式下 extern"C" 分发函数缺失 #29
Loading…
x
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
问题
zx_document.swift+ZxDocumentRuntime.xcframework集成到 iOS 项目后,编译阶段全部通过(0 错误),但链接阶段报找不到符号。缺失的符号
共 12 个 API 分发函数 + 12 个 checksum 函数全部缺失。
根因
UniFFI 0.28 的 UDL 模式(
uniffi::generate_scaffolding+uniffi::setup_scaffolding!())只生成:#[no_mangle],C 链接器看不到)不生成实际的
#[no_mangle] extern "C"API 分发函数。这些需要#[uniffi::export]proc macro 来生成。但
#[uniffi::export]要求被标注的函数的参数/返回值实现 UniFFI 内部 traits(LowerReturn、LowerError等),而这些 traits 在当前纯 UDL 模式下不会被实现。换句话说:UniFFI 0.28 期望 UDL 定义类型 + proc macro 标注函数 的混合模式,纯 UDL 模式已不被完整支持。
已验证的尝试
#[uniffi::export]@_silgen_name桥接可能的解决方案
方案 A:升级/切换到 proc-macro 模式(推荐但工作量大)
#[derive(uniffi::Enum)]、#[derive(uniffi::Record)]等标注所有 Rust 类型#[uniffi::export]标注所有函数uniffi-bindgen library模式)优点:UniFFI 官方推荐方式,长期维护性好
缺点:需要重构整个 FFI 层,类型定义方式完全改变
方案 B:降级 UniFFI 到 0.27 或更早版本
优点:改动最小
缺点:依赖旧版本,后续升级困难
方案 C:手写 extern "C" 桥接层
crates/zx_document_ffi/src/bridge.rs手动写#[no_mangle] extern "C"函数RustBuffer,内部调现有的 Rust 函数优点:完全可控,不依赖 UniFFI 版本
缺点:需要手写序列化/反序列化逻辑(~400 行),维护成本高
方案 D:用
uniffi-bindgen从编译产物生成(可能最快)#[uniffi::export]uniffi-bindgen library --swift-sources libzx_document_ffi.dylib生成 Swift优点:不需要重构类型定义
缺点:需要验证是否与 UDL 定义的类型兼容
需要你决策
额外发现
在修复过程中还处理了以下兼容性问题(已修复):
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor与 UniFFI 生成的原始指针类型冲突 → 已移除Data.init(bytesNoCopy:count:deallocator:)在 iOS 26 SDK 中被移除 → 改用UnsafeRawBufferPointerRustBuffer/ForeignBytes类型未定义(UniFFI 0.28 library mode 期望外部导入)→ 手动内联定义#[no_mangle](UniFFI 0.28 变更)→ 移除运行时 checksum 验证ObservableObject需要import Combine(项目设置变更)→ 已补KnowledgeBase.name→KnowledgeBase.title→ 已修决策:方案 A — 迁移到 proc-macro 模式
根因补充
文档(README / architecture / ios-integration)描述的纯 UDL 工作流在 UniFFI 0.27 及之前有效,但项目使用 0.28,该版本不再为 UDL 生成 extern "C" 分发函数。文档与依赖版本不匹配。
方案 A 概述
#[derive(uniffi::Enum)]、#[derive(uniffi::Record)]、#[derive(uniffi::Error)]替代 UDL 的[Enum] interface、dictionary、[Error] enum#[uniffi::export]替代 UDL namespace 中的函数声明uniffi-bindgen-swift --swift-sources xx.udl改为uniffi-bindgen library --swift-sources libzx_document_ffi.dylib(从编译产物读取元数据)文档已更新
拆解子任务
见下方 M3 milestone 新建的 5 个子 issue。
补充约束(2026-06-03)
1. 明确禁止 UDL / proc-macro 混用
迁移完成后,
zx_document_ffi不允许同时存在include_scaffolding!()和setup_scaffolding!()。proc-macro 模式下的正确组合:
uniffi::setup_scaffolding!()#[uniffi::export]#[derive(uniffi::Record / Enum / Error)]旧 UDL 生成链路(
generate_scaffolding + include_scaffolding)只保留为历史说明,不参与主构建流程。半迁移状态最容易继续报 undefined symbol。2. 明确清理旧产物
已补充到 DOC-302c。必须清理:
bindings/ios/generatedbindings/ios/ZxDocumentRuntime.xcframework否则 Rust 已改 proc-macro,Swift 还在用旧 binding,Xcode 还在链接旧 XCFramework。
3. 新增符号检查任务
已创建 DOC-302f。对最终
libzx_document_ffi.a执行nm检查,必须看到 C ABI 符号:_uniffi_zx_document_ffi_fn_func_detect_material_type_uniffi_zx_document_ffi_fn_func_parse_markdown_uniffi_zx_document_ffi_fn_func_export_pending_events符号不存在 → 构建脚本直接失败。能在进 Xcode 之前就发现问题。
4. 明确 FFI DTO 边界
已补充到 DOC-302a。优先只给 FFI 边界类型添加 UniFFI derive。如果 core 类型包含 UniFFI 不支持的字段、泛型、复杂嵌套,必须在 zx_document_ffi 中定义 FFI DTO,再和 core 类型互相转换,不要让 UniFFI 的约束反向污染 core 类型设计。
关闭
DOC-302 已修复 — XCFramework 正常构建