macOS Development Guide

macOS App Auto-Update with Sparkle via SPM: Complete Setup Guide

Adding auto-update to a macOS app sounds daunting until you actually do it. Sparkle via Swift Package Manager is roughly 20 lines of integration code, a hosted XML file, and a signing key. This guide walks through the entire process from SPM dependency to working update checks, including the parts that trip people up like code signing and appcast generation.

1. Why Sparkle for macOS Auto-Updates

If you distribute a macOS app outside the Mac App Store, you need an auto-update mechanism. Without it, users are stuck on old versions and you have no way to push bug fixes or security patches.

Sparkle is the standard solution. It has been around since 2006, powers thousands of Mac apps, and is actively maintained. The framework handles checking for updates, downloading them, verifying signatures, and applying them - all with minimal code on your end.

FeatureSparkleMac App StoreCustom Solution
Setup effort~1 hourApple review processDays to weeks
Update speedInstant after push1-7 day reviewInstant
Distribution controlFullApple controlsFull
Delta updatesYesYesBuild it yourself
MaintenanceNear zeroReview complianceOngoing

2. Adding Sparkle via Swift Package Manager

In Xcode, go to File > Add Package Dependencies and add the Sparkle repository URL. Select the Sparkle library target. That is it for the dependency.

SPM is the cleanest integration path. CocoaPods and Carthage work too, but SPM requires no additional tooling and is built into Xcode. The package resolves in seconds and adds no build complexity.

Make sure to set your minimum deployment target to macOS 11.0 or later. Sparkle 2.x requires this. If you are targeting older macOS versions, you will need Sparkle 1.x which has a different API.

3. Integration Code (About 20 Lines)

The actual code to integrate Sparkle is surprisingly minimal. You need an SPUUpdater instance, a controller to manage it, and a "Check for Updates" menu item. In SwiftUI this means:

  • - Create an SPUStandardUpdaterController in your App struct
  • - Add a menu item that calls checkForUpdates()
  • - Set the SUFeedURL in Info.plist to your appcast URL
  • - Optionally configure automatic check intervals

Sparkle handles all the UI - the update dialog, download progress, and restart prompt. You do not need to build any update UI yourself.

4. Code Signing with Sparkle's EdDSA Keys

Sparkle uses EdDSA (Ed25519) signatures to verify that updates are authentic. You generate a key pair using Sparkle's generate_keys tool that ships with the framework.

Run the generate_keys tool once. It outputs a public key that goes into your Info.plist (SUPublicEDKey) and stores the private key in your keychain. When you publish an update, you sign the archive with the private key. Sparkle verifies the signature using the public key embedded in your app.

This is separate from Apple code signing. You need both: Apple code signing for Gatekeeper, and Sparkle EdDSA signing for update verification. The Sparkle signing is simpler - just run the sign_update tool on your .dmg or .zip before uploading.

5. Hosting Your Appcast XML

The appcast is an XML file that tells Sparkle what the latest version is and where to download it. Think of it as an RSS feed for your app updates.

GitHub Pages is the simplest free hosting option. Create a repository (or use your existing project repo), add an appcast.xml file, enable GitHub Pages, and point SUFeedURL to it. Sparkle checks this URL on launch and compares the version number against the running app.

Sparkle ships with a generate_appcast tool that creates the XML from a folder of release archives. Drop your signed .dmg files into a folder, run the tool, and it generates the appcast with all the metadata and signatures.

6. Common Issues and Fixes

  • Update check works but download fails: Usually a URL issue in the appcast. Make sure the enclosure URL points to a direct download link, not a GitHub release page.
  • Signature verification fails: The most common cause is signing with a different key than the one in Info.plist. Regenerate and make sure generate_keys and sign_update use the same keychain entry.
  • Sandboxed app cannot update: Sparkle 2.x supports sandboxed apps but requires the XPC service bundle. Check the Sparkle docs for the sandboxing setup.
  • Updates not detected: Version comparison uses CFBundleVersion (build number), not CFBundleShortVersionString (marketing version). Make sure the appcast version matches your build number scheme.

7. Auto-Update Alternatives Compared

Sparkle is the standard but not the only option. Here is how alternatives compare for indie macOS developers:

SolutionCostBest ForDrawback
SparkleFree (open source)Any non-MAS appSelf-hosted appcast
Mac App Store30% revenue shareConsumer appsReview delays, sandboxing
Electron autoUpdaterFreeElectron appsElectron only
Custom HTTP checkFreeFull control neededBuild everything yourself

For native macOS apps built with Swift and SwiftUI, Sparkle is the clear winner. Several open-source macOS apps use it, including tools like Fazm (an AI desktop automation agent built with SwiftUI) that ships updates via Sparkle to users who download directly from the website.

Building a macOS app? Check out Fazm for an example of a production SwiftUI app with Sparkle integration, accessibility API usage, and native macOS patterns.

View on GitHub