A Swift package for the Porsche Connect API. Reimplementation of pyporscheconnectapi in native Swift.
Works on macOS 13+, iOS 16+, watchOS 9+, and tvOS 16+. No external dependencies beyond Foundation and CryptoKit.
dependencies: [
.package(url: "https://github.com/peerb/SwiftPorscheConnect.git", from: "0.1.0"),
]import SwiftPorscheConnect
let tokenStore = FileTokenStore(path: "\(NSHomeDirectory())/.porsche/tokens.json")
let api = PorscheConnectAPI(email: "you@example.com", password: "***", tokenStore: tokenStore)
let data = try await api.fetchVehicleData()
print(data.selectedVehicle.modelName) // "Taycan Sport Turismo"
print(data.batteryLevel) // 87
print(data.rangeKm) // 312
print(data.isLocked) // true- Swift 5.9+
- async/await (Swift concurrency)
- Apple platforms (uses Foundation networking and CryptoKit)
// List all vehicles
let vehicles = try await api.getVehicles()
// Vehicle pictures (side, front, rear, top)
let pictures = try await api.getPictures(vin: "WP0...")
// Cached vehicle status (doesn't wake the car)
let status = try await api.getStoredOverview(vin: "WP0...")
// Live vehicle status (wakes the car)
let live = try await api.getCurrentOverview(vin: "WP0...")
// Trip statistics
let trips = try await api.getTripStatistics(vin: "WP0...")
// All data in one call (vehicles + pictures + status)
let data = try await api.fetchVehicleData(selectedVin: "WP0...")let remote = api.remoteServices(vin: "WP0...")
try await remote.lockVehicle()
try await remote.unlockVehicle(pin: "1234") // SHA512 challenge-response
try await remote.flashIndicators()
try await remote.honkAndFlash()
try await remote.climatizeOn(targetTemperature: 293.15) // 20°C in Kelvin
try await remote.climatizeOff()
try await remote.directChargeOn()
try await remote.directChargeOff()
try await remote.setTargetSOC(80) // 25–100%Remote commands are sent via POST and automatically polled until completion (up to 4 minutes).
The Porsche auth flow may require a captcha. The library throws PorscheConnectError.captchaRequired with the captcha image. Display it to the user, then resume:
do {
try await api.auth.ensureValidToken()
} catch let error as PorscheConnectError {
if case .captchaRequired(let captcha) = error {
let code = showCaptchaUI(captcha.image) // your UI
try await api.auth.loginWithCaptcha(code: code, state: captcha.state)
}
}Tokens are persisted via the PorscheTokenStore protocol:
public protocol PorscheTokenStore {
func load() -> PorscheToken?
func save(_ token: PorscheToken)
func delete()
}A FileTokenStore is included for file-based storage (JSON, chmod 600). Implement the protocol for Keychain, CoreData, or any other backend.
The vehicle status response contains measurements keyed by name:
| Key | Value Fields | Description |
|---|---|---|
BATTERY_LEVEL |
percent (Int) |
High-voltage battery percentage |
E_RANGE |
kilometers (Int) |
Electric range |
MILEAGE |
kilometers (Int) |
Odometer reading |
LOCK_STATE_VEHICLE |
isLocked (Bool) |
Lock status |
GPS_LOCATION |
location (String), direction (Int) |
Lat,lon as string |
TIRE_PRESSURE |
Per-tire currentBar, differenceBar |
Tire pressures |
CLIMATIZER_STATE |
isOn (Bool) |
Climate control status |
CHARGING_SUMMARY |
mode, minSoC |
Charging info |
CHARGING_RATE |
chargingRate, chargingPower |
Current charge rate |
Access values via the typed accessors:
let percent = measurement.value?.int(forKey: "percent")
let isLocked = measurement.value?.bool(forKey: "isLocked")
let location = measurement.value?.string(forKey: "location")This library is not thread-safe. Avoid calling API methods concurrently from multiple tasks. If you need concurrent access, serialize calls through an actor or serial queue.
Swift reimplementation based on the protocol and API knowledge from pyporscheconnectapi by Johan Isacsson, licensed under MIT.
This project is not affiliated with, endorsed by, or connected to Porsche AG.
MIT — see LICENSE.