This guide walks you through adding the Sonar iOS SDK to your Swift app. By the end, your app will request HealthKit permissions, sync health data to the Sonar platform in the background, and keep the connection alive across app launches.
Requirements
| iOS | 14.0 or later |
| Xcode | 15+ |
| Sonar account | With an API key (sign up here) |
| Apple Developer account | Required for HealthKit entitlement |
Install the SDK
Add the Sonar iOS SDK via Swift Package Manager:
- In Xcode, go to File > Add Package Dependencies
- Enter the repository URL:
https://github.com/sonar-health/ios-sdk - Select the latest version and add SonarSDK to your target
Configure Your Project
Enable Capabilities
In your Xcode project, enable the following capabilities under Signing & Capabilities:
- HealthKit — required for Apple Health access
- Background Modes — enable Background fetch and Background processing
Update Info.plist
Add these keys to your Info.plist:
<!-- Required: explain why your app reads health data -->
<key>NSHealthShareUsageDescription</key>
<string>This app reads your health data to provide personalized insights.</string>
<!-- Required: explain why your app writes health data (if applicable) -->
<key>NSHealthUpdateUsageDescription</key>
<string>This app saves workout data to Apple Health.</string>
<!-- Required for background sync -->
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>co.sonar.health.background</string>
</array>
Integration Lifecycle
The SDK follows a linear lifecycle: initialize, connect, sync, disconnect.
Step 1 — Initialize the SDK
Create a SonarManager instance at app launch. The initializer is asynchronous — wait for the completion callback before using the manager.
import SonarSDK
class AppState: ObservableObject {
@Published var sonar: SonarManager?
func initialize(token: String) {
Sonar.initialize(token: token) { [weak self] manager, error in
if let error = error {
print("SDK init failed: \(error.localizedDescription)")
return
}
self?.sonar = manager
}
}
}
Step 2 — Connect to Apple Health
Once initialized, connect the user to Apple Health. This triggers the system HealthKit permission dialog and links the device to your Sonar user.
guard let sonar = sonar else { return }
sonar.connect(
source: .appleHealth,
permissions: [], // Empty = request all available types
backgroundSync: true // Enable background delivery
) { success, error in
if success {
print("Connected to Apple Health")
} else {
print("Connection failed: \(error?.localizedDescription ?? "unknown")")
}
}
Step 3 — Enable Background Delivery
Background delivery lets the SDK sync new health data to Sonar even when your app isn't in the foreground. Add this to your AppDelegate:
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Sonar.setUpBackgroundDelivery()
return true
}
Step 4 — Validate Connection
Check whether a user is still connected before taking actions that depend on an active connection:
if let userId = sonar?.getUserId(source: .appleHealth) {
print("Connected — user ID: \(userId)")
} else {
print("Not connected — call connect()")
}
Disconnect
To stop syncing and remove the connection:
sonar?.disconnect(source: .appleHealth) { success in
print("Disconnected: \(success)")
}
This stops background delivery and removes local connection state. Historical data remains available in the Sonar platform through the REST API.
Full Example
A minimal SwiftUI app showing the complete lifecycle:
import SwiftUI
import SonarSDK
@main
struct HealthApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
HealthView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
Sonar.setUpBackgroundDelivery()
return true
}
}
struct HealthView: View {
@State private var sonar: SonarManager?
@State private var connected = false
var body: some View {
VStack(spacing: 20) {
Text(connected ? "Connected to Apple Health" : "Not connected")
if !connected {
Button("Connect") { connect() }
}
}
.padding()
.task { await initialize() }
.onReceive(NotificationCenter.default.publisher(
for: UIApplication.willEnterForegroundNotification
)) { _ in
Task { await initialize() }
}
}
func initialize() async {
let token = await fetchTokenFromBackend()
Sonar.initialize(token: token) { manager, error in
guard let manager = manager else { return }
self.sonar = manager
self.connected = manager.getUserId(source: .appleHealth) != nil
}
}
func connect() {
guard let sonar = sonar else { return }
sonar.connect(
source: .appleHealth,
permissions: [],
backgroundSync: true
) { success, _ in
self.connected = success
}
}
func fetchTokenFromBackend() async -> String {
// Call your backend to generate a mobile token
// See the Authentication Flow section in the SDK overview
return "token_from_your_backend"
}
}
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Permission dialog doesn't appear | HealthKit entitlement missing or Info.plist keys not set | Verify both capabilities and all three Info.plist keys |
| Background sync not working | Background Modes not enabled or BGTaskSchedulerPermittedIdentifiers missing |
Check Signing & Capabilities and Info.plist |
getUserId() returns nil |
SDK not initialized or connection expired | Re-initialize with a fresh token and call connect() |
| Data appears incomplete | User denied some HealthKit permissions | Check Settings > Health > Data Access on the user's device |
| App Review rejection | Privacy descriptions too generic | Write specific, 3+ word descriptions explaining your app's use of health data |
Go Deeper
- Mobile SDK Overview — Architecture, auth flow, and choosing the right SDK
- Data Catalog — Full metric reference with Apple Health availability
- Data Delivery — Get notified when synced data is ready
- Authentication — Mobile token lifecycle and API key management
Sonar