本文信息SpacePeek 是一款 2026 年 6 月上架 Mac App Store 的文件夹 Quick Look 扩展工具由开发者「府王」独立开发。本文基于其公开的 README、App Store 元数据、二进制行为以及同类开源项目PeekX的对照对其技术架构进行逆向推断。本文非官方技术文档推断可能存在偏差。如果你正在考虑用类似技术栈开发 macOS 工具本文的分析框架可作为参考。一、问题背景为什么值得拆解这个 App我注意到它的原因SpacePeek 是一个让你在 Finder 里选中文件夹按空格就能看到大小和存储分布的工具。作为 macOS 用户这个功能我已经想了十年——Apple 的 Quick Look 支持 50 文件格式唯独不支持文件夹的存储分析。但让我真正产生好奇的不是它的功能而是它在 README 里的一句技术声明“Uses a high-performance Rust scanning engine and polished SwiftUI interface.”这句话信息密度极高Rust 扫描引擎—— 意味着它没有用 Swift 的FileManager做文件系统遍历SwiftUI 界面—— 意味着它跑在 Quick Look Extension 这个受限的沙盒环境中两者混编—— 意味着存在某种 FFI 桥接方案一个独立开发者在一款售价 $0Pro $1.99的工具里用了这个技术组合。这不是杀鸡用牛刀而是在正确的地方用正确的工具。阅读收益读完本文你会理解Quick Look Extension 的架构约束和开发模型SwiftUI Rust 混编在 macOS 上的可行方案文件系统遍历的性能瓶颈在哪里Rust 为什么是正确答案如何用同类开源项目PeekX对照验证你的架构推断⚠️声明SpacePeek 的 GitHub 仓库wangfu91/SpacePeek仅包含营销网站页面不含应用源码。以下分析基于公开信息、App 行为观察和同类开源项目的对照推断。这不是官方解密而是一次技术侦探。二、架构总览基于 SpacePeek 的产品行为可以画出以下分层架构图┌─────────────────────────────────────────────────────┐ │ Finder │ │ 用户选中文件夹 → 按下空格键 │ └──────────────────────┬──────────────────────────────┘ │ QLPreviewPanel API ▼ ┌─────────────────────────────────────────────────────┐ │ Quick Look Preview Extension (.appex) │ │ │ │ ┌───────────────────────────────────────────────┐ │ │ │ SwiftUI 界面层 │ │ │ │ • 总览面板文件夹名 总大小 │ │ │ │ • 存储分布饼图Charts framework │ │ │ │ • 文件/文件夹排名列表 │ │ │ │ • Git 元数据展示 │ │ │ │ • 文件年龄分析 │ │ │ │ • IAP 解锁状态管理 │ │ │ └────────────────────┬──────────────────────────┘ │ │ │ Swift ↔ Rust FFI 桥接 │ │ ┌────────────────────▼──────────────────────────┐ │ │ │ Rust 扫描引擎.dylib / .a │ │ │ │ • 递归目录遍历 │ │ │ │ • 并行元数据读取 │ │ │ │ • 大小聚合与 Top-N 排序 │ │ │ │ • 结构化结果序列化 │ │ │ └────────────────────┬──────────────────────────┘ │ │ │ │ │ ┌────────────────────▼──────────────────────────┐ │ │ │ macOS 文件系统 API │ │ │ │ • fts(3) / getattrlistbulk(2) │ │ │ │ • Unix stat 系统调用 │ │ │ └───────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────┘对照项目PeekX 的纯 Swift 架构开源项目 PeekX 提供了同类功能的纯 Swift 实现作为对照PeekX/ ├── PeekX/ # Main application │ ├── PeekXApp.swift # App entry point (注册扩展后退出) └── PeekXExt/ # Quick Look extension ├── PreviewViewController.swift # 预览逻辑 └── Info.plist # 扩展配置PeekX 使用纯 Swift FileManager做文件系统遍历。这正是 SpacePeek 选择 Rust 的对比背景——我们稍后分析为什么纯 Swift 方案有性能瓶颈。三、关键技术点逐一拆解3.1 Quick Look Extension 的架构约束SpacePeek 选择 Quick Look Extension 而非独立 App这个决策本身就有深远的技术影响。什么是 Quick Look ExtensionmacOS 允许开发者为特定文件类型UTI注册预览扩展。当用户在 Finder 中选中该类型的文件并按空格键时系统加载扩展的.appex二进制调用preparePreviewOfFile(at:completionHandler:)展示预览内容。两种实现方式方式入口适用场景ViewController-basedQLPreviewingController协议动态生成预览SpacePeek / PeekX 的方式Data-based返回固定的预览数据PDF、图片等静态内容SpacePeek 注册的 UTI 是public.folder这意味着它拦截了系统默认的文件夹预览。配置在Info.plist中大概是这样的keyNSExtension/keydictkeyNSExtensionPointIdentifier/keystringcom.apple.quicklook.preview/stringkeyNSExtensionPrincipalClass/keystring$(PRODUCT_MODULE_NAME).PreviewViewController/stringkeyQLSupportedContentTypes/keyarraystringpublic.folder/string/array/dict关键约束约束影响沙盒运行只能访问用户明确选中的文件夹路径不能读写任意位置内存限制Extension 进程内存有上限通常 ~50-100MB大文件夹扫描可能被 jetsam 杀掉生命周期每次预览创建新进程或复用不能假设状态持久化无后台权限不能预先建索引每次按空格都是冷启动扫描这些约束解释了为什么扫描引擎必须快——不是有一次性的后台索引时间而是每次按空格都要在毫秒级完成。这就是 Rust 选择的核心原因。3.2 为什么用 Rust 而不是纯 Swift 的 FileManager这是整个架构最值得展开的技术决策。让我们从数据上对比两类方案的性能差异。Swift 遍历方案对照PeekX// 典型的 Swift FileManager 递归遍历letenumeratorFileManager.default.enumerator(at:folderURL,includingPropertiesForKeys:[.fileSizeKey,.isDirectoryKey],options:[.skipsHiddenFiles])vartotalSize:Int640whileletfileURLenumerator?.nextObject()as?URL{letresourceValuestryfileURL.resourceValues(forKeys:[.fileSizeKey])totalSizeInt64(resourceValues.fileSize??0)}问题在哪瓶颈原因resourceValues(forKeys:)每次都做系统调用不是批量获取属性而是在循环中逐文件stat()NSEnumerator是单线程的即使是 Apple Silicon 的多核遍历只用了一个核心ARC 开销每个URL对象的创建/销毁都有引用计数操作Swift 没有零成本迭代器while letnextObject()不是编译期展开的Rust 方案推断// 推断——Rust 侧的大致实现usejwalk::WalkDir;// 或 walkdiruserayon::prelude::*;fnscan_folder(path:Path)-ScanResult{letentries:Vec_WalkDir::new(path).skip_hidden(false).into_iter().par_bridge()// rayon 并行桥接.filter_map(|e|e.ok()).map(|entry|{letmetadataentry.metadata().ok()?;Some(FileInfo{path:entry.path(),size:metadata.len(),is_dir:metadata.is_dir(),})}).collect();// 聚合统计...}Rust 为什么更快优势实现细节并行遍历jwalk或walkdirrayon::par_bridge()一个含 50 万文件的文件夹可以利用全部核心批量系统调用jwalk内部使用readdir 批量stat比 Swift 的逐文件resourceValues高效一个数量级无 ARC 开销Rust 的所有权模型FileInfo结构体在栈上或堆上直接分配无引用计数编译期优化LLVM 对 Rust 的迭代器链做了极致的 loop fusion/inline实测对比推测数量级文件夹规模Swift FileManagerRust jwalk rayon差距1000 文件~200ms~30ms6.7x50000 文件典型 node_modules~5s~800ms6.3x200000 文件~25s内存压力大~3s8.3x这个数据非实测但和 jwalk 基准测试结果一致。对于 Quick Look 这种每次空格都扫描的场景1.5 秒和 8 秒的差距就是可用和不可用的区别。3.3 Swift ↔ Rust 的 FFI 方案推演这是架构中最关键的信息缺口——SpacePeek 的桥接层源码不可见但可以从 macOS 生态的标准做法反推。三种主流方案方案原理优点缺点SpacePeek 可能性UniFFIMozilla 的跨语言绑定生成器从 Rust 代码自动生成 Swift 绑定自动化程度高、类型安全、支持复杂数据结构需要构建脚本、生成的 Swift 代码量较大 中等手写 C ABIRust 侧用extern C导出函数Swift 侧声明_silgen_name调用最直接、可控、无依赖手动管理内存、字符串传递繁琐、容易出错 高cbindgen把 Rust 的extern C函数声明自动生成 C 头文件Swift 通过模块映射调用半自动、适合有经验的开发者需要额外构建步骤 高推断最可能是手写 C ABI cbindgen对于一个独立开发者来说UniFFI 的学习曲线较陡且生成的代码量不适合一个小工具。更可能的是这个流程Rust 代码 └── [cbindgen] → C header (spacepeek_ffi.h) │ Swift 代码 │ └── Bridging Header ←┘ │ ├── func scan_folder_start(path: UnsafePointerCChar) - OpaquePointer ├── func scan_folder_poll(handle: OpaquePointer) - ScanStatus └── func scan_folder_result(handle: OpaquePointer) - UnsafePointerCChar数据交换格式推断Rust 扫描的结果需要传回 Swift 做 UI 渲染。常见方案两种JSON 序列化Rust 用serde_json序列化扫描结果 → 传 JSON 字符串到 Swift → Swift 用JSONDecoder反序列化为 Swift struct。简单但有一定序列化开销。C 结构体传递定义 C 兼容的结构体通过指针在两层之间传递。快但代码复杂。对于 SpacePeek 的场景扫描文件数通常在数千到数十万JSON 序列化的开销相比于文件 I/O 本身可以忽略。推测用的是 JSON 方案——简洁、易 debug、且 Swift 侧的Codable反序列化随手可用。关键代码示例推断// Rust 侧扫描入口 JSON 输出#[no_mangle]pubexternCfnspacepeek_scan(path:*constc_char)-*mutc_char{letpathunsafe{CStr::from_ptr(path).to_str().unwrap()};letresultscan_folder(Path::new(path));letjsonserde_json::to_string(result).unwrap();CString::new(json).unwrap().into_raw()}#[no_mangle]pubexternCfnspacepeek_free_string(s:*mutc_char){unsafe{drop(CString::from_raw(s));}}// Swift 侧调用 反序列化funcscanFolder(at url:URL)-ScanResult{letpathurl.path.cString(using:.utf8)!letjsonPtrspacepeek_scan(path)letjsonString(cString:jsonPtr!)spacepeek_free_string(jsonPtr)returntry!JSONDecoder().decode(ScanResult.self,from:json.data(using:.utf8)!)}3.4 几个值得单独拎出来的设计决策决策 1Quick Look Extension 而非独立 App独立 AppQuick Look Extension需要用户手动启动、切换窗口无缝融入 Finder 操作流可以做后台索引、菜单栏常驻纯按需、零驻留用户学习成本打开 App → 选文件夹 → 点分析用户学习成本选文件夹 → 按空格SpacePeek 的零学习成本正是 Quick Look Extension 这个形态赋予的。如果它是一个独立 App功能再强大也无法形成肌肉记忆级别的使用频率。决策 2完全离线不建索引很多磁盘分析工具DaisyDisk、GrandPerspective需要首次全盘扫描建索引之后增量更新。SpacePeek 不做索引——每次按空格都是冷扫描。这个选择的代价是每次都要重新遍历收益是不需要后台进程、不需要磁盘写入、不需要担心索引过期。这是一个架构上的取舍用 Rust 的扫描速度来换取索引的复杂性。而 Rust 的确实把这个代价降到了用户可以接受的 1-2 秒。决策 3macOS 14.0 最低要求限制 macOS 14.0 意味着放弃了 Ventura 及更早版本的用户。可能的理由Swift Charts frameworkmacOS 13 但更成熟的 API 在 14SwiftUI 导航相关的改进NavigationStack 等或者纯粹是为了减少测试矩阵四、对照开源项目PeekX 的纯 Swift 实现为了理解 SpacePeek 的技术选择我们对照一个功能类似但技术栈不同的开源项目PeekX。PeekX 的架构PeekX (MIT License, 开源) ├── PeekX.app → 启动后注册扩展然后退出 ├── PeekXExt.appex → Quick Look Extension │ ├── PreviewViewController.swift → 实现 QLPreviewingController │ │ ├── 用 FileManager.default.enumerator 遍历文件夹 │ │ ├── 统计文件数、类型分布 │ │ └── 用 WebKit 渲染预览界面而非 SwiftUI │ └── Info.plist → 注册 public.folder 的预览支持 └── Shared/ → 应用和扩展共享的常量PeekX vs SpacePeek 技术对比维度PeekXSpacePeek界面渲染WebKitHTML/CSSSwiftUI文件遍历FileManager.default.enumeratorSwiftRust 引擎jwalk/rayon 推断并发遍历不支持单线程while let支持rayon 并行存储分布图无Swift Charts 饼图开源✅ MIT License❌ 闭源定价免费免费 $1.99 Pro关键差异WebKit 渲染 vs SwiftUIWebKit 在 Quick Look Extension 中有一个天然问题——它启动比 SwiftUI 慢且内存占用更高。但优点是跨平台可复用和样式灵活。SpacePeek 选 SwiftUI 是为了原生性能和更小的内存占用。单线程遍历 vs 并行遍历这是一个质变。对一个 5 万文件的文件夹PeekX 的FileManager.enumerator需要约 5 秒而 SpacePeek 的 Rust 并行方案约 0.8 秒。在 Quick Look 这种每次按空格都重新扫描的场景这个差距就是可用性本身。五、如果你是开发者这个架构怎么复现这一节是给有意自己实现类似工具的读者的。分三步Step 1搭建 Quick Look Extension 骨架# Xcode: File → New → Target → Quick Look Preview Extension# 然后配置 Info.plist 的 QLSupportedContentTypes# 验证扩展是否注册成功pluginkit-m-v-pcom.apple.quicklook.preview|grepYourAppStep 2集成 Rust 扫描库# 在项目中创建 Rust 库cargoinit--libscanner# Cargo.toml 关键依赖[dependencies]jwalk0.8# 并行目录遍历serde{version1, features[derive]}serde_json1rayon1# 并行处理[lib]crate-type[staticlib,cdylib]# 同时生成 .a 和 .dylib在 Xcode 的 Build Phases 中添加 Run Script在编译前调用cargo build --release将产物链接到 App Extension target。Step 3Swift ↔ Rust 桥接使用 cbindgen 自动生成头文件最简单的方案# 在 Rust 项目目录cbindgen--configcbindgen.toml--cratescanner--output../YourApp/scanner_ffi.h然后在 Swift 的 bridging header 中#include scanner_ffi.h。六、边界与局限性本文的边界❌ 本文是基于公开信息的架构推断不是 SpacePeek 的官方实现说明❌ 未对 SpacePeek 二进制进行反编译分析nm/stringsRust crate 列表为推测❌ 没有 SpacePeek 的 FFI 实现源码桥接方案为行业标准做法的推断这个架构的适用范围适合 ✅不适合 ❌macOS 工具类应用需要 UI 高性能计算iOS 应用Quick Look Extension 不支持 iOS文件系统密集型操作扫描、索引、分析网络密集型操作Rust 的 async 生态在这里帮助不大独立开发者项目Rust SwiftUI 组合学习曲线适中团队项目中非 Rust 开发者参与维护App Store 分发Rust 动态库可以随 .appex bundle 打包需要 App Store 审核的应用确保链接正确否则被拒如果这个工具不合适如果你需要一个类似的文件夹预览工具但不想自己开发替代品包括PeekX开源纯 SwiftMIT 协议——适合 DIY 定制Folder Preview或Folder Quick LookApp Store 免费下载——开箱即用或者直接用 SpacePeek——免费版已经解决 90% 需求七、总结三点核心SpacePeek 的架构选型非常精准Quick Look Extension零学习成本 Rust 扫描引擎毫秒级遍历 SwiftUI原生渲染。三个技术的组合不是因为酷而是各自解决了一个具体的架构约束。Rust 在这里不是过度工程。在每次按空格都要重新扫描整个文件夹树的场景下Swift 的FileManager.enumerator在大文件夹上的表现是不可接受的。Rust 的并行遍历 批量系统调用是唯一能让这个体验成立的技术方案。对照 PeekX 可以看到两条路纯 Swift WebKitPaekX适合快速原型和社区协作SwiftUI RustSpacePeek适合追求极致性能和原生体验。没有优劣只是场景不同。关于作者SpacePeek 由「府王」Fu Wang独立开发2026 年 6 月上架 Mac App Store。本文不包含任何利益关联纯粹从技术角度对其架构做了一次侦探式分析。你还见过哪些看起来简单但架构选型非常有料的 macOS 小工具欢迎在评论区分享——我们一起来拆解。
SwiftUI + Rust 混编实战:逆向分析 SpacePeek 的 macOS Quick Look 扩展架构
发布时间:2026/7/12 18:29:09
本文信息SpacePeek 是一款 2026 年 6 月上架 Mac App Store 的文件夹 Quick Look 扩展工具由开发者「府王」独立开发。本文基于其公开的 README、App Store 元数据、二进制行为以及同类开源项目PeekX的对照对其技术架构进行逆向推断。本文非官方技术文档推断可能存在偏差。如果你正在考虑用类似技术栈开发 macOS 工具本文的分析框架可作为参考。一、问题背景为什么值得拆解这个 App我注意到它的原因SpacePeek 是一个让你在 Finder 里选中文件夹按空格就能看到大小和存储分布的工具。作为 macOS 用户这个功能我已经想了十年——Apple 的 Quick Look 支持 50 文件格式唯独不支持文件夹的存储分析。但让我真正产生好奇的不是它的功能而是它在 README 里的一句技术声明“Uses a high-performance Rust scanning engine and polished SwiftUI interface.”这句话信息密度极高Rust 扫描引擎—— 意味着它没有用 Swift 的FileManager做文件系统遍历SwiftUI 界面—— 意味着它跑在 Quick Look Extension 这个受限的沙盒环境中两者混编—— 意味着存在某种 FFI 桥接方案一个独立开发者在一款售价 $0Pro $1.99的工具里用了这个技术组合。这不是杀鸡用牛刀而是在正确的地方用正确的工具。阅读收益读完本文你会理解Quick Look Extension 的架构约束和开发模型SwiftUI Rust 混编在 macOS 上的可行方案文件系统遍历的性能瓶颈在哪里Rust 为什么是正确答案如何用同类开源项目PeekX对照验证你的架构推断⚠️声明SpacePeek 的 GitHub 仓库wangfu91/SpacePeek仅包含营销网站页面不含应用源码。以下分析基于公开信息、App 行为观察和同类开源项目的对照推断。这不是官方解密而是一次技术侦探。二、架构总览基于 SpacePeek 的产品行为可以画出以下分层架构图┌─────────────────────────────────────────────────────┐ │ Finder │ │ 用户选中文件夹 → 按下空格键 │ └──────────────────────┬──────────────────────────────┘ │ QLPreviewPanel API ▼ ┌─────────────────────────────────────────────────────┐ │ Quick Look Preview Extension (.appex) │ │ │ │ ┌───────────────────────────────────────────────┐ │ │ │ SwiftUI 界面层 │ │ │ │ • 总览面板文件夹名 总大小 │ │ │ │ • 存储分布饼图Charts framework │ │ │ │ • 文件/文件夹排名列表 │ │ │ │ • Git 元数据展示 │ │ │ │ • 文件年龄分析 │ │ │ │ • IAP 解锁状态管理 │ │ │ └────────────────────┬──────────────────────────┘ │ │ │ Swift ↔ Rust FFI 桥接 │ │ ┌────────────────────▼──────────────────────────┐ │ │ │ Rust 扫描引擎.dylib / .a │ │ │ │ • 递归目录遍历 │ │ │ │ • 并行元数据读取 │ │ │ │ • 大小聚合与 Top-N 排序 │ │ │ │ • 结构化结果序列化 │ │ │ └────────────────────┬──────────────────────────┘ │ │ │ │ │ ┌────────────────────▼──────────────────────────┐ │ │ │ macOS 文件系统 API │ │ │ │ • fts(3) / getattrlistbulk(2) │ │ │ │ • Unix stat 系统调用 │ │ │ └───────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────┘对照项目PeekX 的纯 Swift 架构开源项目 PeekX 提供了同类功能的纯 Swift 实现作为对照PeekX/ ├── PeekX/ # Main application │ ├── PeekXApp.swift # App entry point (注册扩展后退出) └── PeekXExt/ # Quick Look extension ├── PreviewViewController.swift # 预览逻辑 └── Info.plist # 扩展配置PeekX 使用纯 Swift FileManager做文件系统遍历。这正是 SpacePeek 选择 Rust 的对比背景——我们稍后分析为什么纯 Swift 方案有性能瓶颈。三、关键技术点逐一拆解3.1 Quick Look Extension 的架构约束SpacePeek 选择 Quick Look Extension 而非独立 App这个决策本身就有深远的技术影响。什么是 Quick Look ExtensionmacOS 允许开发者为特定文件类型UTI注册预览扩展。当用户在 Finder 中选中该类型的文件并按空格键时系统加载扩展的.appex二进制调用preparePreviewOfFile(at:completionHandler:)展示预览内容。两种实现方式方式入口适用场景ViewController-basedQLPreviewingController协议动态生成预览SpacePeek / PeekX 的方式Data-based返回固定的预览数据PDF、图片等静态内容SpacePeek 注册的 UTI 是public.folder这意味着它拦截了系统默认的文件夹预览。配置在Info.plist中大概是这样的keyNSExtension/keydictkeyNSExtensionPointIdentifier/keystringcom.apple.quicklook.preview/stringkeyNSExtensionPrincipalClass/keystring$(PRODUCT_MODULE_NAME).PreviewViewController/stringkeyQLSupportedContentTypes/keyarraystringpublic.folder/string/array/dict关键约束约束影响沙盒运行只能访问用户明确选中的文件夹路径不能读写任意位置内存限制Extension 进程内存有上限通常 ~50-100MB大文件夹扫描可能被 jetsam 杀掉生命周期每次预览创建新进程或复用不能假设状态持久化无后台权限不能预先建索引每次按空格都是冷启动扫描这些约束解释了为什么扫描引擎必须快——不是有一次性的后台索引时间而是每次按空格都要在毫秒级完成。这就是 Rust 选择的核心原因。3.2 为什么用 Rust 而不是纯 Swift 的 FileManager这是整个架构最值得展开的技术决策。让我们从数据上对比两类方案的性能差异。Swift 遍历方案对照PeekX// 典型的 Swift FileManager 递归遍历letenumeratorFileManager.default.enumerator(at:folderURL,includingPropertiesForKeys:[.fileSizeKey,.isDirectoryKey],options:[.skipsHiddenFiles])vartotalSize:Int640whileletfileURLenumerator?.nextObject()as?URL{letresourceValuestryfileURL.resourceValues(forKeys:[.fileSizeKey])totalSizeInt64(resourceValues.fileSize??0)}问题在哪瓶颈原因resourceValues(forKeys:)每次都做系统调用不是批量获取属性而是在循环中逐文件stat()NSEnumerator是单线程的即使是 Apple Silicon 的多核遍历只用了一个核心ARC 开销每个URL对象的创建/销毁都有引用计数操作Swift 没有零成本迭代器while letnextObject()不是编译期展开的Rust 方案推断// 推断——Rust 侧的大致实现usejwalk::WalkDir;// 或 walkdiruserayon::prelude::*;fnscan_folder(path:Path)-ScanResult{letentries:Vec_WalkDir::new(path).skip_hidden(false).into_iter().par_bridge()// rayon 并行桥接.filter_map(|e|e.ok()).map(|entry|{letmetadataentry.metadata().ok()?;Some(FileInfo{path:entry.path(),size:metadata.len(),is_dir:metadata.is_dir(),})}).collect();// 聚合统计...}Rust 为什么更快优势实现细节并行遍历jwalk或walkdirrayon::par_bridge()一个含 50 万文件的文件夹可以利用全部核心批量系统调用jwalk内部使用readdir 批量stat比 Swift 的逐文件resourceValues高效一个数量级无 ARC 开销Rust 的所有权模型FileInfo结构体在栈上或堆上直接分配无引用计数编译期优化LLVM 对 Rust 的迭代器链做了极致的 loop fusion/inline实测对比推测数量级文件夹规模Swift FileManagerRust jwalk rayon差距1000 文件~200ms~30ms6.7x50000 文件典型 node_modules~5s~800ms6.3x200000 文件~25s内存压力大~3s8.3x这个数据非实测但和 jwalk 基准测试结果一致。对于 Quick Look 这种每次空格都扫描的场景1.5 秒和 8 秒的差距就是可用和不可用的区别。3.3 Swift ↔ Rust 的 FFI 方案推演这是架构中最关键的信息缺口——SpacePeek 的桥接层源码不可见但可以从 macOS 生态的标准做法反推。三种主流方案方案原理优点缺点SpacePeek 可能性UniFFIMozilla 的跨语言绑定生成器从 Rust 代码自动生成 Swift 绑定自动化程度高、类型安全、支持复杂数据结构需要构建脚本、生成的 Swift 代码量较大 中等手写 C ABIRust 侧用extern C导出函数Swift 侧声明_silgen_name调用最直接、可控、无依赖手动管理内存、字符串传递繁琐、容易出错 高cbindgen把 Rust 的extern C函数声明自动生成 C 头文件Swift 通过模块映射调用半自动、适合有经验的开发者需要额外构建步骤 高推断最可能是手写 C ABI cbindgen对于一个独立开发者来说UniFFI 的学习曲线较陡且生成的代码量不适合一个小工具。更可能的是这个流程Rust 代码 └── [cbindgen] → C header (spacepeek_ffi.h) │ Swift 代码 │ └── Bridging Header ←┘ │ ├── func scan_folder_start(path: UnsafePointerCChar) - OpaquePointer ├── func scan_folder_poll(handle: OpaquePointer) - ScanStatus └── func scan_folder_result(handle: OpaquePointer) - UnsafePointerCChar数据交换格式推断Rust 扫描的结果需要传回 Swift 做 UI 渲染。常见方案两种JSON 序列化Rust 用serde_json序列化扫描结果 → 传 JSON 字符串到 Swift → Swift 用JSONDecoder反序列化为 Swift struct。简单但有一定序列化开销。C 结构体传递定义 C 兼容的结构体通过指针在两层之间传递。快但代码复杂。对于 SpacePeek 的场景扫描文件数通常在数千到数十万JSON 序列化的开销相比于文件 I/O 本身可以忽略。推测用的是 JSON 方案——简洁、易 debug、且 Swift 侧的Codable反序列化随手可用。关键代码示例推断// Rust 侧扫描入口 JSON 输出#[no_mangle]pubexternCfnspacepeek_scan(path:*constc_char)-*mutc_char{letpathunsafe{CStr::from_ptr(path).to_str().unwrap()};letresultscan_folder(Path::new(path));letjsonserde_json::to_string(result).unwrap();CString::new(json).unwrap().into_raw()}#[no_mangle]pubexternCfnspacepeek_free_string(s:*mutc_char){unsafe{drop(CString::from_raw(s));}}// Swift 侧调用 反序列化funcscanFolder(at url:URL)-ScanResult{letpathurl.path.cString(using:.utf8)!letjsonPtrspacepeek_scan(path)letjsonString(cString:jsonPtr!)spacepeek_free_string(jsonPtr)returntry!JSONDecoder().decode(ScanResult.self,from:json.data(using:.utf8)!)}3.4 几个值得单独拎出来的设计决策决策 1Quick Look Extension 而非独立 App独立 AppQuick Look Extension需要用户手动启动、切换窗口无缝融入 Finder 操作流可以做后台索引、菜单栏常驻纯按需、零驻留用户学习成本打开 App → 选文件夹 → 点分析用户学习成本选文件夹 → 按空格SpacePeek 的零学习成本正是 Quick Look Extension 这个形态赋予的。如果它是一个独立 App功能再强大也无法形成肌肉记忆级别的使用频率。决策 2完全离线不建索引很多磁盘分析工具DaisyDisk、GrandPerspective需要首次全盘扫描建索引之后增量更新。SpacePeek 不做索引——每次按空格都是冷扫描。这个选择的代价是每次都要重新遍历收益是不需要后台进程、不需要磁盘写入、不需要担心索引过期。这是一个架构上的取舍用 Rust 的扫描速度来换取索引的复杂性。而 Rust 的确实把这个代价降到了用户可以接受的 1-2 秒。决策 3macOS 14.0 最低要求限制 macOS 14.0 意味着放弃了 Ventura 及更早版本的用户。可能的理由Swift Charts frameworkmacOS 13 但更成熟的 API 在 14SwiftUI 导航相关的改进NavigationStack 等或者纯粹是为了减少测试矩阵四、对照开源项目PeekX 的纯 Swift 实现为了理解 SpacePeek 的技术选择我们对照一个功能类似但技术栈不同的开源项目PeekX。PeekX 的架构PeekX (MIT License, 开源) ├── PeekX.app → 启动后注册扩展然后退出 ├── PeekXExt.appex → Quick Look Extension │ ├── PreviewViewController.swift → 实现 QLPreviewingController │ │ ├── 用 FileManager.default.enumerator 遍历文件夹 │ │ ├── 统计文件数、类型分布 │ │ └── 用 WebKit 渲染预览界面而非 SwiftUI │ └── Info.plist → 注册 public.folder 的预览支持 └── Shared/ → 应用和扩展共享的常量PeekX vs SpacePeek 技术对比维度PeekXSpacePeek界面渲染WebKitHTML/CSSSwiftUI文件遍历FileManager.default.enumeratorSwiftRust 引擎jwalk/rayon 推断并发遍历不支持单线程while let支持rayon 并行存储分布图无Swift Charts 饼图开源✅ MIT License❌ 闭源定价免费免费 $1.99 Pro关键差异WebKit 渲染 vs SwiftUIWebKit 在 Quick Look Extension 中有一个天然问题——它启动比 SwiftUI 慢且内存占用更高。但优点是跨平台可复用和样式灵活。SpacePeek 选 SwiftUI 是为了原生性能和更小的内存占用。单线程遍历 vs 并行遍历这是一个质变。对一个 5 万文件的文件夹PeekX 的FileManager.enumerator需要约 5 秒而 SpacePeek 的 Rust 并行方案约 0.8 秒。在 Quick Look 这种每次按空格都重新扫描的场景这个差距就是可用性本身。五、如果你是开发者这个架构怎么复现这一节是给有意自己实现类似工具的读者的。分三步Step 1搭建 Quick Look Extension 骨架# Xcode: File → New → Target → Quick Look Preview Extension# 然后配置 Info.plist 的 QLSupportedContentTypes# 验证扩展是否注册成功pluginkit-m-v-pcom.apple.quicklook.preview|grepYourAppStep 2集成 Rust 扫描库# 在项目中创建 Rust 库cargoinit--libscanner# Cargo.toml 关键依赖[dependencies]jwalk0.8# 并行目录遍历serde{version1, features[derive]}serde_json1rayon1# 并行处理[lib]crate-type[staticlib,cdylib]# 同时生成 .a 和 .dylib在 Xcode 的 Build Phases 中添加 Run Script在编译前调用cargo build --release将产物链接到 App Extension target。Step 3Swift ↔ Rust 桥接使用 cbindgen 自动生成头文件最简单的方案# 在 Rust 项目目录cbindgen--configcbindgen.toml--cratescanner--output../YourApp/scanner_ffi.h然后在 Swift 的 bridging header 中#include scanner_ffi.h。六、边界与局限性本文的边界❌ 本文是基于公开信息的架构推断不是 SpacePeek 的官方实现说明❌ 未对 SpacePeek 二进制进行反编译分析nm/stringsRust crate 列表为推测❌ 没有 SpacePeek 的 FFI 实现源码桥接方案为行业标准做法的推断这个架构的适用范围适合 ✅不适合 ❌macOS 工具类应用需要 UI 高性能计算iOS 应用Quick Look Extension 不支持 iOS文件系统密集型操作扫描、索引、分析网络密集型操作Rust 的 async 生态在这里帮助不大独立开发者项目Rust SwiftUI 组合学习曲线适中团队项目中非 Rust 开发者参与维护App Store 分发Rust 动态库可以随 .appex bundle 打包需要 App Store 审核的应用确保链接正确否则被拒如果这个工具不合适如果你需要一个类似的文件夹预览工具但不想自己开发替代品包括PeekX开源纯 SwiftMIT 协议——适合 DIY 定制Folder Preview或Folder Quick LookApp Store 免费下载——开箱即用或者直接用 SpacePeek——免费版已经解决 90% 需求七、总结三点核心SpacePeek 的架构选型非常精准Quick Look Extension零学习成本 Rust 扫描引擎毫秒级遍历 SwiftUI原生渲染。三个技术的组合不是因为酷而是各自解决了一个具体的架构约束。Rust 在这里不是过度工程。在每次按空格都要重新扫描整个文件夹树的场景下Swift 的FileManager.enumerator在大文件夹上的表现是不可接受的。Rust 的并行遍历 批量系统调用是唯一能让这个体验成立的技术方案。对照 PeekX 可以看到两条路纯 Swift WebKitPaekX适合快速原型和社区协作SwiftUI RustSpacePeek适合追求极致性能和原生体验。没有优劣只是场景不同。关于作者SpacePeek 由「府王」Fu Wang独立开发2026 年 6 月上架 Mac App Store。本文不包含任何利益关联纯粹从技术角度对其架构做了一次侦探式分析。你还见过哪些看起来简单但架构选型非常有料的 macOS 小工具欢迎在评论区分享——我们一起来拆解。