Overview
The Synheart Wear Swift SDK provides native iOS support for streaming biometric data from Apple Watch via HealthKit, vendor cloud APIs (WHOOP, Garmin, Fitbit, Oura), direct BLE heart-rate sensors, and offline Apple Healthexport.xml backfill.
Installation
Swift Package Manager
Add to yourPackage.swift:
- File → Add Packages…
- Enter:
https://github.com/synheart-ai/synheart-wear-swift.git
Requirements
- iOS 13.0+, watchOS 8+, macOS 13+
- Swift 5.9+
- Xcode 15.0+
Quick Start
Configure HealthKit
Add toInfo.plist:
- Target → Signing & Capabilities → + Capability → HealthKit
Initialize SDK
Initialize, Request Permissions & Read Data
Callinitialize() first: every other call throws
SynheartWearError.notInitialized until it has run.
HealthKit never tells an app whether read access was granted. The map
returned by
requestPermissions(_:) and getPermissionStatus() reflects
HealthKit’s sharing (write) status, so for read-only access it reports false
even when the user allowed reading. Don’t gate on it: read, and treat an empty
result as “no data or no access”.Real-Time Streaming
streamHR(interval:) returns a Combine AnyPublisher<WearMetrics, Error>. Use streamHRAsync(interval:) if you prefer AsyncStream.
BLE Heart Rate Monitor
Connect directly to any standard Bluetooth LE heart rate monitor for real-time HR streaming.Setup
Add toInfo.plist:
Usage
SynheartWearError.bleHrm(code, message), where code is a
BleHrmErrorCode (permissionDenied, bluetoothOff, deviceNotFound,
subscribeFailed, disconnected).
Supported Devices
Works with any BLE device implementing the standard Heart Rate Profile (0x180D):- WHOOP (Broadcast HR mode)
- Polar H10, OH1
- Wahoo TICKR
- Garmin HRM-Pro / HRM-Dual
Cloud providers (WHOOP / Garmin / Fitbit / Oura)
Cloud providers exist only when the config carries anappId and lists the
vendor in enabledAdapters:
getProvider(_:) throws SynheartWearError.apiError when the vendor isn’t
enabled or appId is missing.
OAuth is mediated by the Synheart Wear API; the device never sees vendor access tokens. Call provider.connect() to launch the system browser at the authorize URL, then provider.connectWithCode(code:state:redirectUri:) from your deep-link handler. For Fitbit and Oura, the cloud delivers a vendor_user_id directly via deep link — pass that as the code argument.
Sleep and overnight history
HealthKitHistoryReader returns per-night summaries:
SleepNightSummary carries totalAsleepMinutes, deepMinutes and
remMinutes; OvernightPhysiologySummary carries hrvSdnnMs, hrvRmssdMs
and restingHrBpm. Both maps are keyed by the night’s date. HealthKit records
HRV as SDNN, so hrvSdnnMs holds it and hrvRmssdMs is nil (before 0.4.3
the SDNN value was put in hrvRmssdMs).
Apple Health XML Backfill
AppleHealthXmlImport (part of the SynheartWear module) ingests an Apple Health export.xml (the file produced by the Health app’s “Export All Health Data” feature) and converts it into a stream of AppleHealthSample records. The streaming SAX parser stays under a 200 MB working-set budget regardless of file size, so multi-hundred-MB exports are safe.
AppleXmlIngestSink implements open(importId:),
insertBatch(_:) -> (inserted: Int, skipped: Int) and finalize() -> ImportResult.
Failures throw AppleHealthXmlError.
Each sample’s stable identity is computed by IdempotencyKey (SHA-256 over a canonical subset of fields), so re-running the import on the same file produces the same keys and downstream sinks can dedupe safely.
Garmin Health SDK (Native RTS)
TheGarminHealth facade provides native Garmin device integration for scanning, pairing, and real-time streaming using generic SDK-owned types.
Important: The Garmin Health SDK Real-Time Streaming (RTS) capability requires a separate license from Garmin. This facade is available on demand for licensed integrations. The underlying Garmin Health SDK code is proprietary to Garmin and is not distributed as open source.
GarminProvider instead.
Local cache
enableLocalCaching (default true) keeps recent readings on the device;
getCachedSessions(), getCacheStats() and clearOldCache(maxAge:) manage it,
and purgeAllData() clears the cache and revokes consents.
With enableEncryption set (0.4.3+), each session is sealed with AES-256-GCM
under a key kept in the Keychain (after first unlock, this device only) and
written with file protection on iOS and watchOS. A tampered record is dropped.
If the key can’t be read, the session isn’t cached rather than stored
unencrypted. Plain files left by earlier versions are deleted.
Errors
SDK calls throwSynheartWearError: notInitialized, healthKitNotAvailable,
healthKitTypeNotAvailable, permissionDenied, invalidData, cacheError,
network cases (noConnection, timeout, hostUnreachable, invalidResponse),
cloud cases (notConnected, authenticationFailed, tokenExpired, apiError,
rateLimitExceeded, serverError) and bleHrm(code, message).
Resources
- Repository: synheart-wear-swift
- Full README: View on GitHub
- Issues: GitHub Issues