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:

  1. In Xcode, go to File > Add Package Dependencies
  2. Enter the repository URL: https://github.com/sonar-health/ios-sdk
  3. 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:

  1. HealthKit — required for Apple Health access
  2. Background Modes — enable Background fetch and Background processing

Update Info.plist

Add these keys to your Info.plist:

xml
<!-- 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.

Initialize(token)Connect(Apple Health)BackgroundSyncDisconnect Initialize(token)Connect(Apple Health)BackgroundSyncDisconnect

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.

swift
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.

swift
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:

swift
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:

swift
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:

swift
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:

swift
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