
macOS 开发 - 使用 Sparkle 轻松实现 App 更新管理
在独立分发 macOS 应用时,你是否也为「怎么优雅地让用户自动更新」而头疼?🤯 别担心,Sparkle 框架就是解决方案!本文带你从原理到实践,手把手实现 App 的自动更新 🚀。
在 macOS 应用分发上,App Store 渠道依托平台自带的版本管理机制,开发者只需关注版本提交与审核即可。而对独立分发的应用来说,构建一个可靠的更新管理系统就显得尤为重要。
历经多个产品的实践,个人认为 Sparkle 框架是更新管理的优选方案:不仅稳定可靠,还功能完善。下面分享具体的实现过程!
0. 工作原理
开始之前有必要简单介绍 Sparkle 的工作原理,方便后面步骤理解
0.1 网络服务端
- 托管一个 Appcast XML 文件,包含最新版本信息(版本号、下载链接、更新说明等)
- 提供应用安装包的下载服务,通常托管在 GitHub Releases 或 CDN 上
Appcast 文件和应用安装包都托管到同一个路径下,推荐使用 对象存储,我选择的是 CloudFlare R2 的存储,原因:
- 全球(包括国内)都可访问
- 存储有收费限制,但访问流量无限制
- 可自定义域名,直接把 Appcast 文件和应用包上传就能通过指定域名按目录访问了。
0.2 应用客户端
- 集成 Sparkle 框架,定期检查 Appcast URL 获取版本信息
- 对比本地版本与远程版本,发现更新时提示用户
- 下载新版本安装包,验证签名后自动替换旧版本并重启应用
整个流程就是:
App 问服务器"有更新吗?" → 服务器回答"有,在这里下载" → App 自动下载安装
开发者只需维护 Appcast 文件和上传新版本,用户即可享受无缝更新体验。
1. 安装依赖
在 Xcode 打开项目,Xcode 中点击 File → Add Package Dependencies...,输入链接: https://github.com/sparkle-project/Sparkle
选择 sparkle ,点击 Add Package 按钮,然后在弹窗中勾选 Add to Target 到项目的 Target 上,继续点击 Add Package 按钮即可添加。
2. 生成 Sparkle 密钥
需要找到本地依赖安装位置。
打开 Xcode 左侧 Navigator 的 Projects 面板,最下面找到 Sparkle。
右击选择 Show in finder ,新打开的 访达 窗口中会看到 sparkle 目录,切换到上一级目录(快捷键 ⌘+↑),可以看到 artifacts 目录。
找到 artifacts/sparkle/Sparkle/bin,其中包含多个可执行工具:
- generate_keys
- generate_appcast
右键其中的 generate_keys,选择 将 generate_keys 拷贝为路径,打开终端,粘贴回车执行:
1 2 3 4 5➜ /Users/5km/Library/Developer/Xcode/DerivedData/LottieGo-bmurcqnwhuwzhsgigbtsnzkzbfvl/SourcePackages/artifacts/sparkle/Sparkle/bin/generate_keys A pre-existing signing key was found. This is how it should appear in your Info.plist: <key>SUPublicEDKey</key> <string>U0mxe9F6FlB/zDEiNcDPTkNRIFxxxxxxxxxxxxxxxx</string>
复制其中 <string>标签内的内容,即为密钥:U0mxe9F6FlB/zDEiNcDPTkNRIFxxxxxxxxxxxxxxxx
同一台 Mac 上所有 Sparkle 应用的 SUPublicEDKey都相同,这是 Sparkle 的设计特性,无需担心。。generate_appcast工具路径用同样方式获取,后续会用到。
3. 配置 info
找到 info 配置的地方,需要按照下面配置 4 个选项:
1 2 3 4 5 6 7 8<key>SUEnableDownloaderService</key> <true/> <key>SUEnableInstallerLauncherService</key> <true/> <key>SUFeedURL</key> <string>https://xxx.com/lottiego/appcast.xml</string> <key>SUPublicEDKey</key> <string>U0mxe9F6FlB/zDEiNcDPTkNRIFxxxxxxxxxxxxxxxx</string>
其中 URL 与公钥需替换为你自己的项目路径和步骤 2 生成的密钥。
如:将文件托管在 CloudFlare R2 的 releases/lottiego 目录,则 Appcast 路径为 https://xxx.com/lottiego/appcast.xml。
4. 配置权限文件
找到项目中的 .entitlements 文件,添加以下内容:
1 2 3 4 5<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>
可直接复制使用。
5. 沙盒开启网络(可选)
如果未开启 App Sandbox,可跳过此步骤。
勾选 Network 两个选项即可:
- Incoming Connetions(Server)
- Outgoing Connections(Client)
6. 添加 Sparkle 代码
6.1 实现菜单检查更新的按钮视图
在 Xcode 中新建 CheckForUpdatesView.swift 文件,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32import Sparkle import SwiftUI import Combine final class CheckForUpdatesViewModel: ObservableObject { @Published var canCheckForUpdates = false init(updater: SPUUpdater) { updater.publisher(for: \.canCheckForUpdates) .assign(to: &$canCheckForUpdates) } } struct CheckForUpdatesView: View { @ObservedObject private var checkForUpdatesViewModel: CheckForUpdatesViewModel private let updater: SPUUpdater init(updater: SPUUpdater) { self.updater = updater self.checkForUpdatesViewModel = CheckForUpdatesViewModel(updater: updater) } var body: some View { Button( action: updater.checkForUpdates, label: { Label("Check for Updates", systemImage: "arrow.clockwise.circle") } ) .disabled(!checkForUpdatesViewModel.canCheckForUpdates) } }
这里只是一种实现方式,同理可以任意实现 UI 上的交互。
6.2 初始化更新控制器并添加菜单按钮
在主入口 @main 的 App 文件中添加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30import SwiftUI import Sparkle @main struct LottieGoApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate private let updaterController: SPUStandardUpdaterController init() { updaterController = SPUStandardUpdaterController( startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil ) } var body: some Scene { Window("LottieGo", id: "LottieGo") { ContentView() // 此处省略其它代码 } // 此处省略其它代码 .commands { CommandGroup(after: .appInfo) { CheckForUpdatesView(updater: updaterController.updater) } } } }
核心在于 updaterController 的初始化与 .commands 中添加的菜单项。
7. 打包
至此 Sparkle 的集成已完成,应用已具备检查和更新功能。接下来执行常规打包,具体步骤可参考我之前的文章:
8. 生成 appcast.xml
打包文件我通常的命名规则:[App名称]-v[版本号].dmg,如 LottieGo-v0.1.0.dmg。
同时准备一个同名的 HTML 文件作为更新日志(支持多语言),如 LottieGo-v0.1.0.en.html / LottieGo-v0.1.0.zh.html。
比如 LottieGo-v0.1.0.en.html 内容:
1 2 3 4 5 6 7 8 9 10 11<p>LottieGo v0.1.0 — our first public preview.</p> <h2>🎉 New Features</h2> <ul> <li>JSON optimization: remove hidden layers/unused props, prune keyframes, precision control, and compression.</li> <li>Instant live preview: drag & drop to see looped, processed animations.</li> <li>Backup & restore: automatic backups when replacing originals; context menu and shortcuts.</li> <li>File insights: original vs processed size, compression ratio, and processing time.</li> <li>Custom URL scheme: lottiego:// to open and process local JSON with flexible parameters.</li> <li>Shortcuts integration: App Intents for Siri and Shortcuts automation.</li> </ul>
使用步骤 2 中获取到的 generate_appcast 工具,在打包好的 DMG 和 日志 HTML 文件所在目录下执行:
1/Users/.../sparkle/Sparkle/bin/generate_appcast .
比如我在 LottieGo 的执行过程(注意下面不只是命令,还有执行命令中的所有显示文本):
1 2 3 4 5 6 7 8 9➜ cd ~/Dev/Releases/lottiego Dev/Releases/lottiego ➜ ls LottieGo-v0.1.0.dmg LottieGo-v0.1.0.html Dev/Releases/lottiego ➜ /Users/5km/Library/Developer/Xcode/DerivedData/LottieGo-bmurcqnwhuwzhsgigbtsnzkzbfvl/SourcePackages/artifacts/sparkle/Sparkle/bin/generate_appcast . Wrote 1 new update, updated 0 existing updates, and removed 0 old updates in appcast.xml
会生成 appcast.xml 文件,自动解析 DMG 与 HTML 文件并写入更新信息,如果目录中存在以往版本的 DMG,会生成跨版本之间的增量更新的 delta 包,相应信息也会出现在 appcast 中。
Sparkle 通过 build 号作为版本标识符,自动生成 delta 差异包。
9. 发布相关文件
上面我们整个过程中的文件,只需要发布:
- LottieGo-v0.1.0.en.html
- LottieGo-v0.1.0.zh.html
- LottieGo-v0.1.0.dmg
- appcast.xml
当目录下存在多个版本时,Sparkle 会自动生成 delta 包(增量更新),可显著减少用户更新下载量。记得将 delta 文件一并上传到对象存储中。
10. 测试
如果需要进行测试,请按以下步骤操作
- 临时将项目的 version 和 build 号各自递增 1
- 重复执行步骤 7、8、9
- 打开本地上一个版本的应用
- 在菜单栏点击 检查更新
如无异常,您将看到可用更新,新版本号应与您临时修改的版本一致。
参考
- Sparkle Guide - Enabling auto-update on SwiftUI Mac apps released as a DMG outside the Mac App Store
- Sparkle 开源仓库
如果喜欢关于 macOS 开发的内容,欢迎关注我的 twitter 帖子,当然也欢迎 Follow 我😉。
什么?你也对文中提到的 LottieGo 感兴趣,这是一款简洁漂亮也有实力的 Lottie 文件体积优化工具,下面👇是传送门🚪



