sparkle-mac
SKILL.md
Sparkle macOS Auto-Update Framework (v2.x)
You are an expert at integrating the Sparkle framework for macOS application auto-updates. This skill references the Sparkle 2.x official documentation.
Core Principles
- Security first — Always use HTTPS for appcast feeds. Sign updates with EdDSA (ed25519). Notarize and code-sign apps via Apple Developer ID. Never store signing keys on the update server.
- Minimal API surface —
SPUStandardUpdaterControllerhandles most use cases. Only drop toSPUUpdaterdirectly when you need custom UI or non-app-bundle updates. - Info.plist for defaults — Set initial configuration (
SUFeedURL,SUPublicEDKey, check intervals) in Info.plist. Only use runtime APIs when responding to user setting changes. - Appcast-driven — Updates are described via RSS-based appcast XML. Use
generate_appcastto automate appcast creation, delta generation, and signing. - Sandbox-aware — Sandboxed apps require XPC Services (Installer, optionally Downloader) and specific entitlements. Non-sandboxed apps can optionally strip XPC Services to save space.
How to Use This Skill
Before generating code, load the relevant reference file(s):
references/setup-and-integration.md— Adding Sparkle via SPM/Carthage/CocoaPods/manual, creating updater objects (Cocoa, SwiftUI, Qt, Catalyst), core APIsreferences/customization.md— All Info.plist settings (general, security, sandboxing), App Transport Security, system profiling (server-side backends), runtime API expectationsreferences/publishing.md— Archiving apps, signing updates, appcast XML format, delta updates, channels, versioning, release notes (HTML/markdown/plain text/localized/adaptive), phased rollouts, critical/major/informational updates, extending appcast with custom XMLreferences/sandboxing.md— XPC Services setup, entitlements, code signing for sandboxed apps, removing XPC Servicesreferences/advanced.md— Gentle update reminders, preferences UI (Cocoa/SwiftUI/Qt), custom user interfaces (SPUUserDriver), sparkle-cli command-line updater, non-app bundle updates (plug-ins), package updates, upgrading from Sparkle 1
Quick Reference
Key Classes
| Class | Purpose |
|---|---|
SPUStandardUpdaterController |
Convenience controller for standard UI + main bundle updates. Use in nibs or instantiate in code. |
SPUUpdater |
Core updater engine. Use directly for custom UI or non-app-bundle updates. |
SPUStandardUserDriver |
Default UI driver. Handles update alerts, download progress, release notes. |
SUAppcastItem |
Represents a single update item from the appcast feed. |
SPUUpdaterDelegate |
Delegate for updater behavior customization (feed URL, channels, update filtering). |
SPUStandardUserDriverDelegate |
Delegate for UI customization (gentle reminders, release notes formatting). |
Essential Info.plist Keys
| Key | Type | Purpose |
|---|---|---|
SUFeedURL |
String | URL to your appcast XML feed |
SUPublicEDKey |
String | Base64-encoded public EdDSA key for update verification |
SUEnableAutomaticChecks |
Boolean | Enable/disable automatic update checks without prompting user |
SUAutomaticallyUpdate |
Boolean | Auto-download and install updates silently (default: NO) |
SUScheduledCheckInterval |
Number | Seconds between checks (default: 86400 = 1 day, min: 3600) |
Sparkle CLI Tools
| Tool | Purpose |
|---|---|
./bin/generate_keys |
Generate EdDSA keypair (stored in Keychain). Run once. |
./bin/generate_appcast |
Auto-generate appcast XML, delta updates, and signatures from a folder of archives |
./bin/sign_update |
Manually sign an update archive or release notes file with EdDSA |
BinaryDelta |
Manually create/apply binary delta patches between app versions |
Cocoa Setup (Programmatic)
import Sparkle
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet var checkForUpdatesMenuItem: NSMenuItem!
let updaterController: SPUStandardUpdaterController
override init() {
updaterController = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil)
}
func applicationDidFinishLaunching(_ notification: Notification) {
checkForUpdatesMenuItem.target = updaterController
checkForUpdatesMenuItem.action = #selector(SPUStandardUpdaterController.checkForUpdates(_:))
}
}
SwiftUI Setup
import SwiftUI
import Sparkle
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("Check for Updates...", action: updater.checkForUpdates)
.disabled(!checkForUpdatesViewModel.canCheckForUpdates)
}
}
@main
struct MyApp: App {
private let updaterController: SPUStandardUpdaterController
init() {
updaterController = SPUStandardUpdaterController(startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil)
}
var body: some Scene {
WindowGroup { ContentView() }
.commands {
CommandGroup(after: .appInfo) {
CheckForUpdatesView(updater: updaterController.updater)
}
}
}
}
Appcast Item Template
<item>
<title>Version 2.0 (2 bugs fixed; 3 new features)</title>
<link>https://myproductwebsite.com</link>
<sparkle:version>2.0</sparkle:version>
<sparkle:shortVersionString>2.0</sparkle:shortVersionString>
<sparkle:releaseNotesLink>https://example.com/release_notes/app_2.0.html</sparkle:releaseNotesLink>
<pubDate>Mon, 05 Oct 2015 19:20:11 +0000</pubDate>
<enclosure url="https://example.com/downloads/app.dmg"
sparkle:edSignature="..."
length="1623481"
type="application/octet-stream" />
</item>
Archive Commands
# Create zip (recommended for zip distribution)
ditto -c -k --sequesterRsrc --keepParent MyApp.app MyApp.zip
# Create tar.xz (high compression)
tar --no-xattrs -cJf MyApp.tar.xz MyApp.app
# Generate appcast from update archives folder
./bin/generate_appcast /path/to/updates_folder/
# Sign an update manually
./bin/sign_update MyApp.zip
Common Pitfalls
- Don't call
checkForUpdatesInBackgroundmanually — Sparkle handles scheduled checks automatically. Only call it immediately after starting the updater if the user hasn't disabled auto-checks. - Don't use
-setFeedURL:— It's deprecated. UseSPUUpdaterDelegate.feedURLString(for:)instead. - Don't use
--deepfor code signing — It breaks sandboxed XPC Services that need specific entitlements. - Don't set runtime updater properties at init — Only set
automaticallyChecksForUpdates,automaticallyDownloadsUpdates, etc. in response to user setting changes, not at startup. - CFBundleVersion must increment — Sparkle uses this to compare versions. Must be machine-readable (numeric). Use
CFBundleShortVersionStringfor human-readable display. - Preserve symlinks in archives — macOS frameworks use symlinks; breaking them invalidates code signatures.
- Test with old version — Sparkle won't show updates if the running app has the same or newer version. Decrease
CFBundleVersiontemporarily for testing.
Weekly Installs
1
Repository
zackbart/skillsFirst Seen
5 days ago
Security Audits
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1