A simple app-updater for macOS, checks your GitHub releases for a binary asset and silently updates your app.
English · 简体中文
appupdater.mp4
AppUpdater is a rewrite of mxcl's AppUpdater, because I don't want to depend on the PromiseKit it uses and would prefer to implement it using async/await.
- Assets must be named:
\(name)-\(semanticVersion).ext. See Semantic Version - Only non-sandboxed apps are supported
- Implement a settings update page suitable for SwiftUI
- Full semantic versioning support: we understand alpha/beta etc.
- We check that the code-sign identity of the download matches the running app before updating. So if you don't code-sign I'm not sure what would happen.
- We support zip files or tarballs.
- We support a proxy parameter for those unable to normally access GitHub
package.dependencies.append(.package(url: "https://github.com/s1ntoneli/AppUpdater.git", from: "0.1.5"))var appUpdater = AppUpdater(owner: "s1ntoneli", repo: "AppUpdater")appUpdater.check()appUpdater.install()AppUpdater is an ObservableObject, can be used directly in SwiftUI.
See the AppUpdaterExample project:
Initialize, Listen: AppUpdaterExampleApp.swift
SwiftUI AppUpdaterSettings: AppUpdaterSettings.swift
Implement Custom Proxy: GithubProxy.swift
Proxy Implementation Reference Gist: github-api-proxy.js
- Core:
AppUpdaterchecks GitHub releases, selects a viable asset, downloads, validates code-signing, and installs. - View:
AppUpdateSettings(SwiftUI) shows current state and the release notes (MarkdownUI). - Providers: data source abstraction.
GithubReleaseProvider(default) talks to GitHub API and assets.MockReleaseProvider(offline) serves releases and assets from package resources, simulating progress and producing a minimal .app archive.
- Swap providers via initializer or at runtime:
let updater = AppUpdater(owner: "...", repo: "...", provider: GithubReleaseProvider())
// or
updater.provider = MockReleaseProvider()
updater.skipCodeSignValidation = true // recommended when using mocks- Mock data source:
- Releases JSON:
Sources/AppUpdater/Resources/Mocks/releases.mock.json - Changelog attachments:
CHANGELOG.<lang>.mdfiles in package resources - Simulated download:
MockReleaseProvider.downloadstreams progress and outputs a minimal.appinside a zip/tar as needed.
- Releases JSON:
- UI strings in
AppUpdaterSettingsare localized (English, Simplified Chinese). You may contribute more locales viaSources/AppUpdater/Resources. - Localized changelog: AppUpdater can pick a localized section from your GitHub release body depending on the user's preferred languages.
Add language-marked blocks in the release body using HTML comments:
<!-- au:lang=zh-Hans -->
- 修复若干问题
- 新增偏好设置项
<!-- au:end -->
<!-- au:lang=en -->
- Fix several issues
- Add a new preference option
<!-- au:end -->
Optionally, provide a default block used as a fallback:
<!-- au:default -->
- General improvements
<!-- au:end -->
AppUpdater selects the best match from AppUpdater.preferredChangelogLanguages (default: Locale.preferredLanguages), then falls back to default, then en, then the first block. If no blocks are found, the original release body is shown as-is.
- Alternatively, attach language-specific files to the GitHub release with names like:
CHANGELOG.en.md,CHANGELOG.zh-Hans.md,CHANGELOG.zh-Hant.md, etc. (.txtalso works)
- AppUpdater will prefer these attachments (matching the user's preferred languages), and fall back to the body-based blocks above.
- In Example/Mock, these files live under the package resources and are wired up for offline testing.
- Open
Examples/AppUpdaterExample/AppUpdaterExample.xcodeproj - General page:
- Toggle “Use Mock Data” to run fully offline
- Set “Changelog Languages” (e.g.
fr, ja, en) and Apply - Click “Check Updates” and open “Software Updates Available” to see localized notes
- The Example is configured for ad‑hoc Debug builds without a signing team.
- In the update settings screen, the “Diagnostics” section can print a step‑by‑step trace of the update flow.
- Toggle “Enable Logs” to capture logs such as fetching releases, selected release/asset, download progress, unzip results, and code‑sign validation.
- “Last Error” shows the most recent failure surfaced by
check()or installation.
- Run:
swift run AppUpdaterMockRunner --langs=fr,ja,en - Prints state transitions and the localized changelog (prefers attachments, then body blocks).
- Empty changelog in UI?
- If using GitHub provider, ensure the release body includes language blocks or attach
CHANGELOG.<lang>.mdassets. - With Mock provider, v2.0.0 includes multiple attachment files for offline tests.
- You can set
updater.preferredChangelogLanguages = ["zh-Hans", "en"]or via the Example’s input.
- If using GitHub provider, ensure the release body includes language blocks or attach
-
A built-in
MockReleaseProvideris available to test the full update flow without network:- Uses
Sources/AppUpdater/Resources/Mocks/releases.mock.jsonto feed releases. - Materializes a minimal
.appinside a zip at runtime and simulates download progress. - Set
appUpdater.skipCodeSignValidation = truefor mock installs.
- Uses
-
Quick CLI runner:
- Build & run:
swift run AppUpdaterMockRunner - Shows state transitions and completes without touching your installed app.
- Build & run:
-
Example app (macOS):
- Toggle “Use Mock Provider” in General Settings (restart example app to take effect) and click “Check Updates”.