releaseflow
PricingDocsLog in

Sparkle (macOS)

Docs

  • Overview
  • Quickstart
  • Sparkle (macOS)
  • Electron
  • License keys
  • Channels
  • API reference

For native macOS apps using Sparkle 2. Create the app with type Sparkle; the appcast returns 400 for other app types.

Feed URL

Set SUFeedURL in Info.plist to the app’s appcast, and SUPublicEDKey to your EdDSA public key:

Info.plist
<key>SUFeedURL</key>
<string>https://releaseflow.net/{username}/{slug}/sparkle/appcast.xml</string>
<key>SUPublicEDKey</key>
<string>YOUR_BASE64_PUBLIC_KEY</string>

The appcast at https://releaseflow.net/{username}/{slug}/sparkle/appcast.xml is RSS 2.0 with Sparkle extensions. It lists every enabled, non-draft release in the requested channel that has a macOS asset, newest version first. Each item carries the release notes, sparkle:minimumSystemVersion when you set one on the app, and an enclosure pointing at https://releaseflow.net/{username}/{slug}/updates/download/{version}/{fileName}, which streams the asset from GitHub (private repositories included).

Query parameters:

  • ?channel=beta or ?channel=alpha: include pre-releases, see Channels.
  • ?sync=1: start a GitHub sync now instead of waiting for the hourly refresh.

Release assets

Attach a .zip of the app bundle to the GitHub release. When a release has both a .zip and a .dmg, the appcast uses the .zip; the .dmg is still offered on the download page. A .zip without mac/darwin in its name is treated as macOS when a matching .zip.sig sits next to it.

Signing

ReleaseFlow never holds your private key and does not sign builds. Sign the archive with Sparkle’s sign_update and upload the signature as <asset name>.sig; on sync, ReleaseFlow reads that file and writes it into the enclosure’s sparkle:edSignature.

shell
# Once: create the key pair (private key goes to the login Keychain)
./bin/generate_keys

# Every release: sign the update archive and upload the .sig next to it
./bin/sign_update -p MyApp.zip > MyApp.zip.sig
gh release upload v1.2.0 MyApp.zip MyApp.zip.sig
  • -p prints only the signature; the .sig file must contain just that base64 string.
  • Sparkle checks the signature against SUPublicEDKey in the installed app. An item without sparkle:edSignature is rejected, so upload the .sig before (or together with) publishing the release, or press Sync Releases after adding it.
  • Code-sign and notarize the app before zipping; EdDSA signs the archive, not the bundle.

Check the result:

curl -s "https://releaseflow.net/{username}/{slug}/sparkle/appcast.xml?sync=1" | head -40

Versions

Sparkle compares sparkle:version with the installed app’s CFBundleVersion. ReleaseFlow derives it from the tag, and writes the tag version to sparkle:shortVersionString. The app’s version strategy decides the format:

TagSEMVER (default)NUMERIC_BUILD
v1.2.31.2.310203
v1.3.0-beta.21.3.0b210300b2
v1.3.0-rc.11.3.0fc110300fc1

Set CFBundleVersion in the build to the same value. Use NUMERIC_BUILD if your build numbers are integers; it is set with sparkleVersionStrategy on PATCH https://releaseflow.net/api/rest/apps/{id} (see the API reference).

Beta builds

Enable the beta channel on the app, then let users opt in by returning the beta feed from your updater delegate:

Swift
import Sparkle

final class UpdaterDelegate: NSObject, SPUUpdaterDelegate {
  func feedURLString(for updater: SPUUpdater) -> String? {
    UserDefaults.standard.bool(forKey: "betaUpdates")
      ? "https://releaseflow.net/{username}/{slug}/sparkle/appcast.xml?channel=beta"
      : nil // nil = SUFeedURL from Info.plist
  }
}

Licensing

To gate the app behind a license key, add the ReleaseFlowLicensing Swift package; see License keys.

← QuickstartElectron →
releaseflow
PrivacyTermsDocsChangelogSupport