A native macOS desktop app for the Škoda Connect Public API (B2C) — monitor and control your Škoda (e.g. Enyaq Coupe) from your Mac: battery & charging, climate (A/C, auxiliary heating, active ventilation), doors/windows/ lights status, odometer, and last known parking position.
Built with SwiftUI + Swift Package Manager, no Xcode project required. Not affiliated with Škoda Auto; this is an unofficial client for the public API documented at https://public.api.connect.skoda-auto.cz/docs/swagger-ui/index.html.
![]() |
![]() |
| Overview | Charging |
![]() |
![]() |
| Charging profiles | Climate |
![]() |
|
| Location |
Implements every operation exposed by the Public API (v1.0.0):
- Vehicle overview — name, license plate, render image, doors/windows/ lights status, odometer, fuel status (combustion/hybrid).
- Charging — live battery %, range, charge power/rate, state, start/stop charging, charging settings (target SoC, care mode, max AC current, ...), editable target state of charge and charge mode, and saved charging profiles (with preferred times and timers) editable per-profile.
- Climate — start/stop air conditioning with target temperature, start/ stop auxiliary heating (requires your vehicle's S-PIN), start/stop active ventilation, window heating state.
- Location — last known parking position on an interactive map, or "in motion" state.
- Multi-vehicle garage — add any number of VINs your API key covers.
- Auto-refresh with a configurable interval, rate-limit visibility, and API key expiry tracking (the API exposes both via response headers).
Prebuilt Apple Silicon (arm64) builds are attached to each
GitHub Release as yourSkoda-<version>-macos-arm64.dmg.
- Download the
.dmg, open it, and dragyourSkoda.appinto theApplicationsshortcut inside. - Releases are code-signed with a real Developer ID Application certificate
and notarized by Apple (see Cutting a release), so it
should open normally with no Gatekeeper warning. If a given release was
published before notarization was set up, or notarization failed for that
build, you may briefly see "yourSkoda" Not Opened with no bypass option
— in that case, run this once in Terminal, then open it normally:
xattr -cr /Applications/yourSkoda.app
- Requires macOS 14 (Sonoma) or later, Apple Silicon.
The Public API authenticates with a simple API key header (X-API-Key), not
OAuth. Create one from the MyŠkoda app or https://go.skoda.eu/api-keys — keys
are scoped to the vehicles you select and they expire, so you'll need to
rotate them periodically. Paste the key into the app's Settings window
(⌘,); it's stored in the macOS Keychain, never in plain text on disk.
If you plan to use auxiliary heating, also set your vehicle's S-PIN in Settings — the API requires it for that specific operation.
Requires Xcode 15+ / Swift 5.10+ command line tools on macOS 14 or later.
# Run directly (debug, launches in Terminal's window session)
swift run
# Or build a proper double-clickable .app bundle into ./build
./scripts/build_app.sh
# ...and install it to /Applications
./scripts/build_app.sh --installscripts/build_app.sh builds a release binary, packages it with the
generated icon (Resources/AppIcon.icns) and Info.plist into
build/yourSkoda.app, and code-signs it — ad-hoc by default (so Keychain
access works consistently between launches), or with a real identity if
CODESIGN_IDENTITY is set in the environment (see
Cutting a release).
To regenerate the app icon (scripts/generate_icon.swift, pure AppKit/Core
Graphics, no external assets):
swift scripts/generate_icon.swift
iconutil -c icns Resources/AppIcon.iconset -o Resources/AppIcon.icnsReleases are built by
.github/workflows/release.yml on a
self-hosted runner (registered on a Mac that holds the signing
certificate) whenever a tag matching v*.*.* is pushed:
git tag v1.0.0
git push origin v1.0.0The workflow builds the arm64 .app, code-signs it with a real
Developer ID Application certificate from the runner's Keychain,
packages it as a .dmg (via scripts/make_dmg.sh), submits it to Apple's
notary service, staples the notarization ticket, and publishes it as a
Release asset with auto-generated notes.
One-time setup on the runner:
- Import your Developer ID Application certificate + private key into the Keychain the runner process uses (typically the login keychain of the logged-in session that started the runner), and make sure it's unlocked.
- Find the exact identity string:
security find-identity -v -p codesigning
- In the GitHub repo, add a repository variable (Settings → Secrets and
variables → Actions → Variables) named
CODESIGN_IDENTITYwith that exact string, e.g.Developer ID Application: Your Name (TEAMID). - Generate an app-specific password at
appleid.apple.com → Sign-In and Security →
App-Specific Passwords, then store a
notarytoolcredential profile in the runner's Keychain (this is a one-time, local-only step — nothing is sent to GitHub):(An App Store Connect API key works too — seexcrun notarytool store-credentials "yourskoda-notary" \ --apple-id "you@example.com" \ --team-id "TEAMID" \ --password "the-app-specific-password"
scripts/make_dmg.shfor the--key/--key-id/--issuerform.) If you use a profile name other thanyourskoda-notary, also add a repository variableNOTARY_PROFILEwith that name.
The workflow fails fast if CODESIGN_IDENTITY is unset or not found in the
runner's Keychain. Notarization is best-effort: if the credential profile is
missing or Apple's notary service rejects the submission, the build still
publishes a signed-but-not-notarized .dmg rather than failing the release
(see Download for what that means for end users).
Stapling caveat on corporate networks: notarization itself talks to
Apple's notary API, but stapling the ticket (so the .dmg verifies fully
offline) talks to api.apple-cloudkit.com, which TLS-inspecting corporate
proxies commonly block with a certificate trust error. If that happens the
script retries a few times, then continues without stapling — the release is
still notarized server-side and Gatekeeper will verify it online on first
launch (an internet connection is required at that point, same as any
first-run Gatekeeper check). If your runner is behind such a proxy, either
allowlist api.apple-cloudkit.com, or run the runner/build off that network.
To build the same artifact locally instead (e.g. to test before tagging):
# Ad-hoc signed (default, matches local dev builds, no notarization)
./scripts/make_dmg.sh 1.0.0
# Or signed + notarized, same as CI:
CODESIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)" \
./scripts/make_dmg.sh 1.0.0
# -> build/dist/yourSkoda-1.0.0-macos-arm64.dmg (+ .sha256)Sources/YourSkoda/
Models/ Codable types mirroring the OpenAPI schema (Vehicle, Charging, ...)
Networking/ SkodaAPIClient — async/await URLSession client, error mapping
Persistence/ Keychain wrapper + UserDefaults-backed garage/settings store
Store/ AppStore — the app's observable state, polling, actions
Views/ SwiftUI screens (sidebar, overview, climate, charging, map, profiles, settings)
scripts/ Icon generator + .app bundler
screenshots/ PNGs used in this README
- There is no "list my vehicles" endpoint in the Public API, so vehicles are added by VIN manually (the app remembers them).
- Control actions (
/charging/start,/air-conditioning/start, etc.) return202 Acceptedimmediately — the app polls the vehicle a couple of seconds later to reflect the new state. - Not every vehicle supports every field/operation; unsupported or currently disabled ones are surfaced as a dismissible "data unavailable" banner rather than an error.




