Sparkle Integration
Configure Sparkle to use Shipunk for updates
Sparkle Integration
This guide covers how to integrate Sparkle with Shipunk in your Mac app.
Prerequisites
- Xcode project with Sparkle framework installed
- A Shipunk account with an app created
- Your app's EdDSA public key (from Shipunk dashboard)
Adding Sparkle to your app
If you haven't already, add Sparkle using Swift Package Manager:
- In Xcode, go to File → Add Package Dependencies
- Enter:
https://github.com/sparkle-project/Sparkle - Select version 2.x
Configuring Info.plist
Add these keys to your Info.plist:
<!-- Appcast URL from Shipunk -->
<key>SUFeedURL</key>
<string>https://shipunk.dev/appcast/YOUR_APP_SLUG.xml</string>
<!-- EdDSA public key from Shipunk dashboard -->
<key>SUPublicEDKey</key>
<string>YOUR_EDDSA_PUBLIC_KEY</string>
<!-- Optional: Allow automatic updates -->
<key>SUAutomaticallyUpdate</key>
<true/>
<!-- Optional: Check for updates on launch -->
<key>SUEnableAutomaticChecks</key>
<true/>Finding your public key
- Go to shipunk.dev/dashboard
- Click on your app
- Copy the EdDSA Public Key from the settings
Code setup
SwiftUI
import SwiftUI
import Sparkle
@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)
}
}
}
}
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)
}
}
final class CheckForUpdatesViewModel: ObservableObject {
@Published var canCheckForUpdates = false
init(updater: SPUUpdater) {
updater.publisher(for: \.canCheckForUpdates)
.assign(to: &$canCheckForUpdates)
}
}AppKit
import Cocoa
import Sparkle
@main
class AppDelegate: NSObject, NSApplicationDelegate {
let updaterController = SPUStandardUpdaterController(
startingUpdater: true,
updaterDelegate: nil,
userDriverDelegate: nil
)
@IBAction func checkForUpdates(_ sender: Any) {
updaterController.checkForUpdates(sender)
}
}Testing updates
- Build your app with version
1.0.0 - Upload version
1.1.0to Shipunk - Run the
1.0.0build and check for updates
The update dialog should appear with download options.
Troubleshooting
"No updates available" even though there is one
- Check that the version in your
Info.plist(CFBundleShortVersionString) is lower than the uploaded version - Verify your appcast URL is correct
- Check the Sparkle console logs
Signature verification failed
- Make sure the
SUPublicEDKeyin your app matches the key in Shipunk - Re-download your public key from the dashboard
Update downloads but won't install
- Ensure your app is code-signed
- Check that the downloaded file isn't corrupted
Release notes
Shipunk supports HTML release notes. When uploading, you can include:
shipunk release upload MyApp.dmg \
-a myapp \
-v 1.1.0 \
-n "<h2>What's New</h2><ul><li>New feature</li><li>Bug fix</li></ul>"Or use a markdown file (converted to HTML):
shipunk release upload MyApp.dmg \
-a myapp \
-v 1.1.0 \
--notes-file CHANGELOG.md