【免费下载链接】flux-markdown
Markdown Preview Enhanced for macOS QuickLook
FluxMarkdown 是一款运行在 macOS 上的 Markdown 快速预览工具(QuickLook 扩展 + 独立 App),它的"检查更新"能力完全由 Sparkle 框架驱动。本文结合 FluxMarkdown 的真实代码,讲清楚 Sparkle 自动更新的完整落地流程:从依赖接入、appcast.xml更新源、沙盒权限配置,到 EdDSA 签名与密钥轮换的避坑经验。
Sparkle 是什么,为什么适合 FluxMarkdown
Sparkle 是 macOS 生态中最成熟的自动更新框架,负责:
- 定时/手动检查远端更新源(
appcast.xml) - 下载新版安装包并校验EdDSA 数字签名
- 通过安装器进程(Installer)替换
/Applications下的应用并重启
对于 FluxMarkdown 这样的开源项目(未使用 Mac App Store 分发渠道),Sparkle 是实现"应用内一键更新"的标准选择。
FluxMarkdown 的 QuickLook 预览效果如下,用户可以直观看到 Markdown 文档在 Finder 中的渲染结果:
三步接入:依赖、控制器、更新源
第 1 步:用 Swift Package 引入 Sparkle
在 Package.swift 中声明依赖,并锁定版本(Xcode 工程侧由 project.yml 同步声明):
.package(url: "https://github.com/sparkle-project/Sparkle", exact: "2.8.1")锁定精确版本很关键:Sparkle 2.x 的 XPC 服务名、权限模型与 1.x 完全不同,随意升级可能导致已安装用户无法更新。
第 2 步:初始化 SPUStandardUpdaterController
在 MarkdownApp.swift 的AppDelegate中,只需几行代码即可启动更新器:
let updaterController = SPUStandardUpdaterController( startingUpdater: true, updaterDelegate: UpdateDelegate.shared, userDriverDelegate: nil )Sparkle 2.x 对 SwiftUI 应用推荐的正是SPUStandardUpdaterController(1.x 时代的SUUpdater手动组装已被官方放弃)。菜单栏中的"检查更新"按钮通过NSApp.sendAction(#selector(SPUStandardUpdaterController.checkForUpdates(_:)))触发同一套逻辑。
第 3 步:配置 Info.plist 更新源
所有 Sparkle 配置集中在 Info.plist 中,关键字段一览:
| 键 | 作用 |
|---|---|
SUFeedURL | appcast.xml的托管地址(必须指向返回 XML 原文的 URL) |
SUPublicEDKey | 当前公钥,用于校验更新包签名 |
SUPublicEDKeyChain | 新旧公钥链,支持密钥轮换过渡 |
SUEnableAutomaticChecks | 开启自动检查 |
SUScheduledCheckInterval | 检查间隔(本项目为 86400 秒,即每天一次) |
SUEnableInstallerLauncherService | 沙盒应用必需的 XPC 安装服务 |
appcast.xml:更新的"发布清单"
Sparkle 检查更新时拉取的 appcast.xml 是一个 RSS 文件,每个<item>对应一个版本,核心是<enclosure>节点:
<enclosure url=".../FluxMarkdown.dmg" sparkle:edSignature="hu1PmNe+X3dpse5+...==" length="36738769" type="application/octet-stream" />其中sparkle:edSignature是用私钥对 DMG 生成的 EdDSA 签名。应用端用SUPublicEDKey公钥验签,签名不匹配的安装包会被直接拒绝——这就是为什么"下载更新"不等于"信任更新"。
📌 常见坑:SUFeedURL若指向网页(HTML)而非 XML 原文,Sparkle 会解析失败。本项目使用 raw 文件地址直接返回 XML,避免了这个问题。
发布时的更新包安装窗口如下,用户只需将应用拖到 Applications,后续版本交给 Sparkle:
沙盒应用的关键:entitlements 权限例外
FluxMarkdown 启用了 App Sandbox,而 Sparkle 的安装器是独立进程,必须显式放行才能工作。相关配置在 Markdown.entitlements 与 MarkdownRelease.entitlements 中:
<!-- 允许 Sparkle XPC 服务通信 --> <key>com.apple.security.temporary-exception.mach-lookup.global-name</key> <array> <string>$(PRODUCT_BUNDLE_IDENTIFIER)-spks</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)-spki</string> </array> <!-- 允许写入 /Applications 完成安装 --> <key>com.apple.security.temporary-exception.files.absolute-path.read-write</key> <array> <string>/Applications</string> </array>⚠️ 注意服务名必须使用$(PRODUCT_BUNDLE_IDENTIFIER)-spks/-spki的标准格式。本项目早期曾用过自定义服务名com.xykong.Markdown.Installer,结果更新时报错An error occurred while running the updater——Sparkle 2.x 只认标准后缀。这个教训完整记录在 LESSONS_SPARKLE_AUTO_UPDATE.md 中。
发布流程:签名、生成 appcast、发布
一次完整的版本发布围绕三个脚本:
- 生成密钥:generate-sparkle-keys.sh 生成 Ed25519 密钥对,私钥严禁提交到仓库;
- 签名并更新 appcast:generate-appcast.sh 调用 Sparkle 的
sign_update工具对 DMG 签名,自动把版本号、签名、文件大小写入 appcast.xml; - 可恢复发布:publish-release.sh 校验清单与公证、生成签名后的 appcast 并创建 Release,失败可断点重跑,流程细节见 RELEASE_PROCESS.md。
用户侧的体验由 UpdateDelegate.swift 的回调串联:发现新版本、用户点击下载、即将安装、更新失败……每个状态都有对应的 delegate 方法,便于打日志和埋点。
细节体验:更新后自动恢复上次打开的文件
自动更新常打断用户工作流,FluxMarkdown 的解法在 UpdateRestorationManager.swift 中:
- 打开文档时,把文件路径写入
UserDefaults; - Sparkle 回调触发下载/安装前,
markUpdatePending()打一个"更新进行中"标记; - 更新完成、应用重启后检测到该标记,自动重新打开上次的 Markdown 文件(0.5 秒延迟确保窗口就绪);
- 文件已被删除等异常情况会优雅降级,并清理标记。
这套机制让"重启安装更新"对用户几乎无感。
避坑清单:从 5 轮调试总结的教训
以下问题都来自 LESSONS_SPARKLE_AUTO_UPDATE.md 的真实排障记录,建议对照自查:
| 现象 | 根因 | 解决 |
|---|---|---|
An error occurred while launching the installer | 沙盒缺少安装器启动服务 | 开启SUEnableInstallerLauncherService+ entitlements 例外 |
An error occurred while running the updater | Mach 服务名不是 Sparkle 标准格式 | 改用-spks/-spki后缀 |
The update is improperly signed | 重新生成密钥后,旧版本内嵌旧公钥 | 用SUPublicEDKeyChain同时信任新旧公钥,完成渐进式迁移 |
| 检查更新解析报错 | SUFeedURL指向 HTML 页面 | 改为返回 XML 原文的 raw 地址 |
两条最重要的经验:
- 密钥是单点故障:私钥丢失 = 所有旧用户永久无法更新,务必备份到密码管理器或加密存储;
- 一定要测"旧版本 → 新版本"路径:项目提供了 test-update-from-old-version.sh、test-update-locally.sh 等脚本,模拟真实升级场景,只测"最新版覆盖安装"会漏掉大量兼容性问题。
总结:一套可复用的 Sparkle 落地清单
把 FluxMarkdown 的做法抽象出来,就是五步:
- SPM 引入固定版本的 Sparkle 2.x;
SPUStandardUpdaterController一行启动,delegate 接管状态回调;Info.plist配好SUFeedURL、公钥(必要时SUPublicEDKeyChain)、检查间隔;entitlements放行-spks/-spki服务与/Applications写入;- 发布时用
sign_update签名 DMG,脚本化生成appcast.xml并做旧版本升级回归。
更多细节可查阅 AUTO_UPDATE.md 的官方说明与 CHANGELOG.md 中的历次更新记录。按这套清单执行,你的 macOS 应用也能拥有稳定、安全、可追溯的自动更新体验。
【免费下载链接】flux-markdown
Markdown Preview Enhanced for macOS QuickLook
相关推荐
实时更新模型不再难!飞桨流处理系统中的在线更新方案
实时更新模型不再难!飞桨流处理系统中的在线更新方案 你是否还在为流处理系统中模型更新导致服务中断而烦恼?是否因数据分布变化使模型性能持续下降?本文将介绍基于飞桨
人工智能深度学习计算机视觉NLP语音Sparkle与Xcode集成:macOS应用自动更新的终极指南
Sparkle与Xcode集成:macOS应用自动更新的终极指南 Sparkle是一款专为macOS应用设计的自动更新框架,它能帮助开发者轻松实现应用的版本检查
桌面应用终极指南:10分钟为你的macOS应用集成Sparkle自动更新
想让你的macOS应用拥有像专业软件一样的自动更新功能吗? Sparkle 是一个专为macOS设计的软件更新框架,让你的应用能够自动检测、下载和安装新版本。这
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考