🎯 Goal
Add a non-intrusive auto-update check that pulls release DMGs from this repository's GitHub Releases. When a new version is available, the app shows a confirmation dialog with release notes; nothing is downloaded or installed without explicit user approval.
📋 UX flow
- On launch + once every 24 h the app calls
https://api.github.com/repos/VasenevEA/FastWord/releases/latest and compares the returned tag_name against the installed CFBundleShortVersionString.
- If a newer version exists → menu-bar status changes to
Update available: v0.X.Y (visible only in the trey menu, no popup). Also a new menu item «Install v0.X.Y…».
- Clicking that menu item opens a window with:
- Version number
- Release notes (parsed from the GitHub Release
body markdown)
- Download & install primary button
- Skip this version secondary
- Remind me later dismiss
- On Download & install:
- Download the
.dmg asset to ~/Library/Caches/fastword/updates/
- Verify the DMG's Developer ID signature (
spctl -a) against the expected Team ID LPZJYDRZ2K
- Mount, copy the new
.app into a temp location, run a helper that replaces /Applications/FastWord.app and relaunches
- The user re-enters their hotkey session; settings are preserved
- On Skip this version: remember
v0.X.Y in UserDefaults so we don't notify about that version again. New versions still notify.
🔒 Privacy considerations
The single privacy-sensitive thing about an auto-updater is what telemetry it sends to the update server. Sparkle's default setup sends OS version, app version, and a UUID. We want strictly none of that.
Our checker will:
- Only call the public GitHub API endpoint for the releases JSON. No custom server.
- Include no User-Agent identifier beyond a literal
FastWord (GitHub requires some UA). No version, no OS, no machine ID.
- Use no cookies, no auth headers, no PII.
- Be togglable off in Settings → General → «Check for updates automatically» (default: on).
- Be explicit in the about/Settings: "FastWord checks https://api.github.com once a day for new releases. No other telemetry is sent. Source: GitHub-released DMGs only."
⚖️ Implementation options
Option A — Sparkle framework
- Industry-standard, used by VS Code, IINA, Transmission, etc.
- Handles signature verification, delta updates, EdDSA signing.
- Cons: requires us to host an
appcast.xml (could use GitHub Pages, but adds infra), needs an EdDSA key setup, ~3 MB of binary in the app.
Option B — Hand-rolled GitHub Releases checker (recommended)
- ~150 lines of Swift: URLSession, JSON decode, NSWorkspace.
- Verifies DMG via
spctl after download (Apple's Gatekeeper does the heavy lifting since we're already Developer-ID-notarised).
- No appcast hosting, no extra signing infra, no extra dependencies in the binary.
- Fits the "no extra moving parts" ethos of the project.
Recommendation: Option B. We already publish notarised DMGs to GitHub Releases as part of the release script; the updater simply reuses them. Gatekeeper is sufficient signature verification.
✅ Definition of done
🛠 Technical notes
- GitHub API rate limit unauthenticated: 60 req/h per IP, well above our 1 req/day. Need no token.
- The "skipped versions" list is just a
Set<String> in UserDefaults.
- Replacing a running
.app from itself requires a small helper script extracted to /tmp/; standard trick used by Sparkle and ~every other macOS updater.
- Signature check: shell out to
codesign --verify --strict on the downloaded .app and require TeamIdentifier=LPZJYDRZ2K.
🎯 Goal
Add a non-intrusive auto-update check that pulls release DMGs from this repository's GitHub Releases. When a new version is available, the app shows a confirmation dialog with release notes; nothing is downloaded or installed without explicit user approval.
📋 UX flow
https://api.github.com/repos/VasenevEA/FastWord/releases/latestand compares the returnedtag_nameagainst the installedCFBundleShortVersionString.Update available: v0.X.Y(visible only in the trey menu, no popup). Also a new menu item «Install v0.X.Y…».bodymarkdown).dmgasset to~/Library/Caches/fastword/updates/spctl -a) against the expected Team IDLPZJYDRZ2K.appinto a temp location, run a helper that replaces/Applications/FastWord.appand relaunchesv0.X.Yin UserDefaults so we don't notify about that version again. New versions still notify.🔒 Privacy considerations
The single privacy-sensitive thing about an auto-updater is what telemetry it sends to the update server. Sparkle's default setup sends OS version, app version, and a UUID. We want strictly none of that.
Our checker will:
FastWord(GitHub requires some UA). No version, no OS, no machine ID.⚖️ Implementation options
Option A — Sparkle framework
appcast.xml(could use GitHub Pages, but adds infra), needs an EdDSA key setup, ~3 MB of binary in the app.Option B — Hand-rolled GitHub Releases checker (recommended)
spctlafter download (Apple's Gatekeeper does the heavy lifting since we're already Developer-ID-notarised).Recommendation: Option B. We already publish notarised DMGs to GitHub Releases as part of the release script; the updater simply reuses them. Gatekeeper is sufficient signature verification.
✅ Definition of done
UpdateChecker.swiftpolling latest release once a day + on launch.LPZJYDRZ2K.🛠 Technical notes
Set<String>in UserDefaults..appfrom itself requires a small helper script extracted to/tmp/; standard trick used by Sparkle and ~every other macOS updater.codesign --verify --stricton the downloaded.appand requireTeamIdentifier=LPZJYDRZ2K.