关于 macos-auto-update
这项技能用于为原生 macOS 应用添加 Sparkle 自动更新,介绍依赖配置、更新管理器、应用配置及更新界面集成。
主要功能
- 01通过 SPM 添加 Sparkle
- 02指导实现 UpdaterManager
- 03配置 Info.plist 更新项
- 04设置与菜单中的更新控件
适用场景
- 01为 macOS 应用添加更新检查
- 02配置 Sparkle 更新源和签名密钥
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ fayazara/ macos-app-skills --skill macos-auto-update安装后在 Claude Code 中输入/macos-auto-update确认已加载。
附带文件references/UpdaterManager.swift
详细介绍
macOS 应用的 Sparkle 自动更新
此技能可为原生 macOS 应用添加 Sparkle 自动更新支持。Sparkle 是 Mac App Store 之外用于 macOS 应用更新的标准开源框架。
概述
实现分为 4 个部分:
- SPM 依赖项 -- 将 Sparkle 软件包添加到 Xcode 项目
- UpdaterManager.swift -- 封装
SPUStandardUpdaterController的单例 - Info.plist 键 --
SUFeedURL和SUPublicEDKey - UI 集成 -- 设置中或菜单栏中的“检查更新”按钮
步骤 1:通过 SPM 添加 Sparkle
在 Xcode 中,依次选择 File > Add Package Dependencies > 输入:
https://github.com/sparkle-project/Sparkle
使用“Up to Next Major Version”规则,并指定 2.0.0。将 Sparkle 框架添加到应用目标。
如果项目使用 Package.swift,也可以将其添加到该文件中:
.package(url: "https://github.com/sparkle-project/Sparkle", from: "2.0.0")
步骤 2:创建 UpdaterManager.swift
将 references/UpdaterManager.swift 复制到项目中。这是一个单例,它会:
- 提前创建
SPUStandardUpdaterController(在applicationDidFinishLaunching返回之前) - 发布
canCheckForUpdates,以便 UI 绑定 - 提供
automaticallyChecksForUpdates开关 - 在 DEBUG 构建中跳过所有更新逻辑(这样开发期间就不会收到更新提示)
- 对于仅使用菜单栏的应用:在显示更新窗口前,临时将激活策略切换为
.regular
该文件中的关键设计决策:
- 初始化器中设置
startingUpdater: false,然后在applicationDidFinishLaunching中显式调用start()。这样可以控制调用时机。 - 在
start()和checkForUpdates()中使用 DEBUG 守卫。Sparkle 绝不应在调试构建中运行,否则它会尝试用发行版构建更新调试版应用。 - 使用带有
@Published的ObservableObject(而不是@Observable),因为我们需要通过 Combine 的publisher(for:)桥接 Sparkle 的 KVO。
步骤 3:配置 Info.plist
将以下键添加到应用的 Info.plist 中:
<key>SUFeedURL</key>
<string>https://raw.githubusercontent.com/OWNER/REPO/main/appcast.xml</string>
<key>SUPublicEDKey</key>
<string>YOUR_PUBLIC_EDDSA_KEY_HERE</string>
<key>SUEnableInstallerLauncherService</key>
<true/>
生成 EdDSA 密钥
Sparkle 使用 EdDSA(Ed25519)签名。生成密钥对:
# Find generate_keys in your DerivedData after building the project with Sparkle
find ~/Library/Developer/Xcode/DerivedData -name "generate_keys" -type f 2>/dev/null | head -1
运行:
/path/to/generate_keys
此命令会输出公钥,并将私钥存储在钥匙串中。将公钥填入 Info.plist 中的 SUPublicEDKey。私钥保留在钥匙串中,供发行时的 sign_update 使用。
步骤 4:接入应用
App Delegate
final class AppDelegate: NSObject, NSApplicationDelegate {
private let updaterManager = UpdaterManager.shared
func applicationDidFinishLaunching(_ notification: Notification) {
updaterManager.start()
}
}
必须尽早访问 UpdaterManager.shared 属性,以便在应用完成启动前创建 SPUStandardUpdaterController。在 AppDelegate 属性中引用它即可确保这一点。
设置 UI(关于面板)
struct AboutSettingsPane: View {
@ObservedObject private var updaterManager = UpdaterManager.shared
var body: some View {
Form {
Section("Updates") {
Toggle(isOn: Binding(
get: { updaterManager.automaticallyChecksForUpdates },
set: { updaterManager.automaticallyChecksForUpdates = $0 }
)) {
Text("Automatically check for updates")
}
Button("Check for Updates...") {
updaterManager.checkForUpdates()
}
.disabled(!updaterManager.canCheckForUpdates)
}
}
.formStyle(.grouped)
.scrollContentBackground(.hidden)
}
}
菜单栏(可选)
Button {
updaterManager.checkForUpdates()
} label: {
Label("Check for Updates...", systemImage: "arrow.down.circle")
}
.disabled(!updaterManager.canCheckForUpdates)
步骤 5:创建初始 Appcast
在代码仓库根目录创建 appcast.xml。该文件初始为空,并由发行流程填充:
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>YourApp</title>
<description>Most recent changes for YourApp.</description>
<language>en</language>
</channel>
</rss>
将此文件托管在 GitHub(原始文件 URL)或任意静态文件主机上。该 URL 必须与 Info.plist 中的 SUFeedURL 一致。
仅使用菜单栏的应用
如果应用以 .accessory 运行(没有 Dock 图标),除非临时切换到 .regular,否则 Sparkle 的更新窗口不会显示。参考实现 UpdaterManager 会在 checkForUpdates() 中处理这一点:
func checkForUpdates() {
NSApp.setActivationPolicy(.regular)
NSApp.activate(ignoringOtherApps: true)
controller.checkForUpdates(nil)
}
更新窗口关闭时,应用会恢复为 .accessory(由现有的激活策略管理器处理)。
Hardened Runtime 权限
如果应用使用 Hardened Runtime(公证所必需),则不需要 Sparkle 专用权限。Sparkle 2.x 可在标准的 Hardened Runtime 配置下运行。
Appcast 条目格式
appcast 中的每个发行版本格式如下(构建发行工具时可供参考):
<item>
<title>Version 1.2 (Build 5)</title>
<pubDate>Mon, 26 May 2026 12:00:00 +0000</pubDate>
<sparkle:version>5</sparkle:version>
<sparkle:shortVersionString>1.2</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>14.0</sparkle:minimumSystemVersion>
<description><![CDATA[<ul><li>New feature</li><li>Bug fix</li></ul>]]></description>
<enclosure url="https://github.com/OWNER/REPO/releases/download/v1.2/YourApp.dmg"
type="application/octet-stream"
sparkle:edSignature="BASE64_EDDSA_SIGNATURE"
length="FILE_SIZE_BYTES" />
</item>
sparkle:version=CFBundleVersion(构建号)sparkle:shortVersionString=CFBundleShortVersionString(营销版本号)sparkle:edSignature=sign_update YourApp.dmg的输出length= 文件大小(字节)