# iOS (Swift)

## Overview

This guide will walk you through the necessary steps to integrate the Terra Mobile SDK with Apple Health in your iOS app. It covers everything from SDK initialization, user connection, permission handling, and background data updates.

The `TerraiOS` SDK supports the following integrations:

* Apple Health

## 1. Install the SDK

{% @supademo/embed url="<https://app.supademo.com/demo/cm23anelu2a4c13b3fg1gd9pu>" demoId="cm23anelu2a4c13b3fg1gd9pu" %}

1. Add `https://github.com/tryterra/TerraiOS` as a package dependency to your Xcode project.
2. **Add&#x20;*****Capabilities*****:**
   1. Healthkit > Healthkit Background Delivery
   2. Background Modes > Background processing
   3. Background modes > Background fetch
3. **In your&#x20;*****info.plist,*****&#x20;add the following:**

<table><thead><tr><th width="348.9085693359375">Key</th><th width="371.258056640625">Value</th></tr></thead><tbody><tr><td>Privacy - Health Share Usage Description</td><td><p>Description of how Health data is used</p><p><strong>(Min 3 words)</strong></p></td></tr><tr><td>Privacy - Health Records Usage Description</td><td><p>Description of how Health data is used</p><p><strong>(Min 3 words)</strong></p></td></tr><tr><td>Privacy - Health Update Usage Description</td><td><p>Description of how Health data is used</p><p><strong>(Min 3 words)</strong></p></td></tr><tr><td>Permitted background task scheduler</td><td><code>co.tryterra.data.post.request</code></td></tr></tbody></table>

***

## 2. Initialize the SDK

The first step is to initialize the Terra SDK. This is done by creating a `TerraManager` instance which allows you to interact with the Terra SDK.

The initialization only needs to be done **once** **on** **app** **start**, (e.g. in your `ContentView.swift` or an equivalent file), and **ideally**, whenever the app is brought into the foreground. This ensures that the SDK is properly set up and ready to be used.

#### **Step 1: Import TerraiOS**

In your Xcode project, you should now be able to import Terra SDK

{% code title="ContentView\.swift" lineNumbers="true" %}

```swift
import TerraiOS
```

{% endcode %}

#### **Step 2: Get a `TerraManager` Instance**

In order to interact with the SDK, you need a **`TerraManager`** instance.

Call `Terra.instance()` with the following arguments:

* `devId`: Your **Developer ID** provided by Terra.
* `referenceId`: An **ID of your choice** to identify your app user.
* `requestPermissions` **(optional, defaults to `true`)**: Whether to automatically trigger the HealthKit permission popup when an existing Apple Health user is reconnected on launch. Set to `false` if you want returning users to be recognized silently and want the popup to be deferred to an explicit user action — see [Controlling when the HealthKit popup appears](#controlling-when-the-healthkit-popup-appears) below.
* `completion` : Callback with a `TerraManager` or an instance `TerraError`

Find more details in the SDK Reference: [Terra manager initialization function](/reference/health-and-fitness-api/sdk-references/ios-swift.md#initialization-of-terra-manager)

{% code title="ContentView\.swift" lineNumbers="true" %}

```swift
import TerraiOS

var terra: TerraManager?

Terra.instance(devId: "<YOUR_DEV_ID>", referenceId: "<REFERENCE_ID>") { manager, error in
    guard let manager = manager, error == nil else {
        fatalError("🤯: \(error?.localizedDescription ?? "unknown error")")
    }

    // TerraManager instance to access the SDK functions
    terra = manager
}
```

{% endcode %}

The `TerraManager` instance is thereafter ready to be used to call SDK functions!

(**N.B** This call is asynchronous, please ensure this is complete before using the manager).

***

## 3. Connect an Apple Health user

Once you’ve initialized the SDK, you can connect the Apple Health user. This is done by calling the [`initConnection()`](/reference/health-and-fitness-api/sdk-references/ios-swift.md#initconnection) method provided by the `TerraManager` instance that you created above.

#### 1. Call [`initConnection()`](/reference/health-and-fitness-api/sdk-references/ios-swift.md#initconnection)

This method triggers the **Apple Health permission popup** and establishes the connection.

From `TerraManager`, call [`initConnection()`](/reference/health-and-fitness-api/sdk-references/ios-swift.md#initconnection) with the following arguments:

* `type`: Specify the connection type as `.APPLE_HEALTH` to connect the Apple Health account.
* `token`: A **one-time authentication token** generated from your **backend** server using the /auth/generateAuthToken endpoint. This ensures secure communication between your app and the Terra servers.
* `customReadTypes`: A **set of permissions** that define what data you want to request from Apple Health (e.g., heart rate, steps). If empty, it defaults to all available permissions.
* `schedulerOn`: This parameter has no effect. Background delivery is controlled by calling `Terra.setUpBackgroundDelivery()` in your AppDelegate — see [Step 5](#id-5.-enable-background-delivery) below.

To learn more about these parameters and their different options, check the [iOS SDK Reference.](/reference/health-and-fitness-api/sdk-references/ios-swift.md)

{% code title="UserConnectionManager.swift" lineNumbers="true" %}

```swift
// Prepare the arguments
let token = "<AUTH_TOKEN>" // replace with the token generated from your backend with Terra
let customPermissions: Set<CustomPermissions> = [] // customize permissions if needed
let schedulerOn = true // no effect on iOS — see setUpBackgroundDelivery() below

// Start a connection to Apple Health
terra.initConnection(
    type: .APPLE_HEALTH,
    token: token,
    customReadTypes: customPermissions,
    schedulerOn: schedulerOn
) { success, error in
    guard success, error == nil else {
        fatalError("🤯: \(error?.localizedDescription ?? "unknown error")")
    }
    // Connection successful — Apple Health permission popup was shown
}
```

{% endcode %}

After a successful call of `initConnection()` with a valid token, an Apple Permission screen will pop up!

{% hint style="info" %}

### Apple Health Kit Permission Screen: `initConnection()`

Apple Health only shows the permission popup **once**, so calling `initConnection()` multiple times won’t trigger the popup again unless:

a. you call initConnection with **an expanded set of** `customPermissions`

b. the **app is deleted & reinstalled.**
{% endhint %}

{% hint style="info" %}

### **Apple Health Kit Permission Screen: WebViews** 🚧

* Apple HealthKit implements the permissions popup as a WebView.
* If your app is also based on a WebView, you will need to interrupt your WebView, call `initConnection`, then upon completion re-open your WebView.
  {% endhint %}

To be able to call the `initConnection()` method, you need to pass a `token` as an argument.

This `token` is a **single-use token** created to ensure the authentication endpoint for creating a connection (and connecting the SDK to Terra's servers) does not get abused.

Generate the token with this endpoint <mark style="color:orange;">`POST`</mark>`https://api.tryterra.co/v2/auth/generateAuthToken` . Make sure to call it with your Terra `x-api-key` and `dev-id` in the headers from **your backend server.** After you generate the token, provide the response to your client side using your own logic.

Go to the **SDK Reference** to find more details on the [Generate the Mobile SDK Auth Token **API Endpoint**](/reference/health-and-fitness-api/sdk-references.md)**.**

{% hint style="warning" %}

* During the development phase, it it acceptable to place this call on the **client** side, exposing your API key in your mobile app.
* For a production implementation, **DO NOT** expose your API key this way, and make sure to **only** make this call from your **backend.**
  {% endhint %}

## Controlling when the HealthKit popup appears

By default, the HealthKit permission popup is triggered in two situations:

1. When your app calls [`initConnection()`](/reference/health-and-fitness-api/sdk-references/ios-swift.md#initconnection) to establish a new connection.
2. When `Terra.instance(...)` reconnects a returning Apple Health user **and** iOS has no stored authorization for your app (for example: the app was reinstalled and iOS cleared the authorization, or the user revoked permissions in Settings).

For most integrations this is the desired behavior. However, some apps want the popup to appear **only** in response to an explicit user action — e.g. the user tapping "Connect Apple Health" inside your app UI. Two common reasons:

* **Multi-device:** the same user opens your app on a second device (e.g. an iPad) where they have not yet opted into HealthKit. You don't want the popup to appear on that device until they actively choose to connect.
* **Reinstall:** the user previously granted HealthKit in a prior install, deleted the app, and reinstalled. You don't want the popup to appear the moment they relaunch; you want it gated on their explicit action.

### Deferred prompting pattern

Pass `requestPermissions: false` to `Terra.instance(...)` so that returning users are recognized silently without triggering the popup. Then call `requestHealthKitPermissions(...)` when the user explicitly opts in.

{% code title="DeferredPrompt.swift" lineNumbers="true" %}

```swift
// On app launch — recognize the returning user silently; no popup.
Terra.instance(devId: "<DEV_ID>", referenceId: "<REFERENCE_ID>", requestPermissions: false) { manager, error in
    guard let manager = manager, error == nil else { return }
    self.terra = manager
    // `manager` is ready. If this referenceId already had an Apple Health connection,
    // it's reconnected silently. No HealthKit popup has been shown.
}

// Later, when the user taps "Connect Apple Health" in your UI:
func userTappedConnectAppleHealth() {
    terra?.requestHealthKitPermissions { success, error in
        // If iOS hasn't already determined permissions for this install, the popup
        // is shown here. If it has (e.g. the user previously granted and the install
        // hasn't changed), the call returns synchronously with success.
    }
}
```

{% endcode %}

{% hint style="info" %}
`requestPermissions: false` only materially changes behavior when `Terra.instance` finds an existing Apple Health connection for this device + `referenceId` that needs re-authorization. For a first-time user, a new device, or a reinstall where the device identifier changed, `Terra.instance` does not auto-prompt regardless of the flag — the popup is only ever triggered by your `initConnection()` or `requestHealthKitPermissions()` call.
{% endhint %}

## Cross-device and reinstall behavior

If the same user (same `referenceId`) connects Apple Health from multiple devices, or reconnects after a reinstall that reset the device's vendor identifier, **each device produces a distinct Terra `user_id`**. The Terra backend keys Apple Health connections on the tuple `(deviceId, devId, referenceId)` — if the device identifier changes, a new connection is created.

### Scenario matrix

| Scenario                                                                                                                      | HealthKit popup on launch?                                                                                                                                                                      | Resulting Terra `user_id`                          | What you should do                                                                                                                                                                                                                    |
| ----------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Returning user, same device and `referenceId` (vendor ID unchanged)                                                           | Only if iOS cleared authorization — e.g. the user revoked permissions in Settings, or a reinstall where the vendor ID persisted because another app from your App Store team is still installed | Same as before                                     | Nothing — `Terra.instance` handles it. If you want to suppress that edge-case popup on launch, see [Controlling when the HealthKit popup appears](#controlling-when-the-healthkit-popup-appears) and use `requestPermissions: false`. |
| Same device, different `referenceId` (e.g. user logout/login on the same device)                                              | No                                                                                                                                                                                              | A **new** `user_id` when you call `initConnection` | Call `initConnection` when the new user opts in                                                                                                                                                                                       |
| Different device (e.g. iPhone → iPad), same `referenceId`                                                                     | No                                                                                                                                                                                              | A **new** `user_id` when you call `initConnection` | Dedupe webhooks on `reference_id`, or gate `initConnection` to a single primary device per user                                                                                                                                       |
| Reinstall where the vendor ID changed (the common case — user deleted every app from your App Store team before reinstalling) | No                                                                                                                                                                                              | A **new** `user_id` when you call `initConnection` | Treat as a new connection; dedupe on `reference_id`                                                                                                                                                                                   |

{% hint style="info" %}
Apple generates a new `identifierForVendor` for your app only when the user deletes **every** app from your App Store team from the device and then reinstalls one. If your team only ships one app, a reinstall almost always resets the vendor ID — producing a new Terra `user_id`. If your team ships multiple apps that users tend to keep installed, the vendor ID often persists across reinstalls of any one of them.
{% endhint %}

### Webhook payload example

This means data from the same real person can arrive at your webhook under **different `user_id`s** sharing the **same `reference_id`**:

```json
// From iPhone connection
{ "user": { "user_id": "<uid-A>", "reference_id": "user-42" }, ... }

// From iPad connection (same person, same reference_id)
{ "user": { "user_id": "<uid-B>", "reference_id": "user-42" }, ... }
```

### Guidance

* Deduplicate data on your side by grouping webhook events on `reference_id`.
* Terra does not automatically link Apple Health connections that share a `reference_id` but differ on device identifier — this is a deliberate design decision because HealthKit data is device-local and may legitimately differ per device.
* If you only want one device to be the data source, gate your `initConnection()` call on your own app's "primary device" logic and do not call it on other devices for the same user.

## 4. Validate the Connection

To ensure a connection is still valid on the client side, make sure to use the [`getUserid()`](/reference/health-and-fitness-api/sdk-references/ios-swift.md#getuserid) from `TerraManager` **every** **time** **you** **reinitialize** **the** **SDK.** This function is synchronous and returns a `user_id` right away if a user is connected or nil if none exists.

{% hint style="info" %}

## Always validate the connection before using the SDK

Check if a `user_id` exists right after **initializing the Terra SDK** to see if the connection still exists.

1. **Check if the User is Connected**
   1. If the function returns a user ID, the user is still connected, 🎉 keep vibing along!
   2. If the function returns <mark style="color:red;">`nil`</mark>, the user needs to reconnect.
2. **Re-connect if Needed**\
   If the connection is lost, you can call `terra.initConnection()` again to re-establish the connection.

Calling `terra.initConnection()` when the user is already connected or just needs a reconnection will not trigger any permission screens from Apple Health, and the user flow will remain uninterrupted. The connection will be re-established if necessary.
{% endhint %}

***

## 5. Enable Background Delivery

1. Go to your **AppDelegate**'s `didFinishLaunchingWithOptions` function.
2. Add [`Terra.setUpBackgroundDelivery()`](/reference/health-and-fitness-api/sdk-references/ios-swift.md#setupbackgrounddelivery)

This will ensure you get updates for the user's Apple Health data automatically sent to your destination.

{% code title="AppDelegate.swift" lineNumbers="true" %}

```swift
import UIKit
import TerraiOS

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(
      _ application: UIApplication, 
      didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        Terra.setUpBackgroundDelivery()
        return true
    }
}
```

{% endcode %}

{% hint style="success" %}

## iOS Background Delivery Behaviour:

* Data will still be triggered if the **app** **is** **killed** (with much lower frequency)
* Data will only be triggered when **phone** **is** **unlocked**
* Data can only be triggered where there is **network connection**
* Only enabled **Data** **Types** from [the Terra Dashboard](https://dashboard.tryterra.co) will be triggered by background delivery.
* If you are using **CustomPermissions,** please ensure the following permissions are enabled for Background Delivery to work correctly:
  * For **Daily: CustomPermissions.STEPS**
  * For **Sleep: CustomPermissions.SLEEP\_ANALYSIS**
  * For **Body: CustomPermissions.BMI, CustomPermissions.HEART\_RATE**
  * For **Activity: CustomPermissions.WORKOUT\_TYPE**
  * For **Nutrition: CustomPermissions.NUTRITION\_CALORIES**
    {% endhint %}

***

## 6. Filtering by source app (optional)

Apple Health aggregates data from every health app on the user's device. If a user has both a cloud-based Terra connection (e.g. WHOOP, Garmin) and the same provider's companion app syncing into Apple Health, you'll receive duplicate data — once from the cloud API and once from the Apple Health SDK.

Use `setIgnoredSources` to tell the SDK to skip data from specific apps in HealthKit. Call this **once on every app launch**, after `Terra.instance()` completes. It is not persisted across app restarts.

```swift
// After Terra.instance() completes:
Terra.setIgnoredSources(["com.whoop.app", "com.garmin.connect.mobile"])
```

Common bundle identifiers:

| App            | Bundle identifier           |
| -------------- | --------------------------- |
| WHOOP          | `com.whoop.app`             |
| Garmin Connect | `com.garmin.connect.mobile` |
| Fitbit         | `com.fitbit.FitbitMobile`   |
| Oura           | `com.ouraring.oura`         |

{% hint style="info" %}
To find a specific app's bundle identifier, have the user check **Settings → Health → Data Access & Devices** on their iPhone.
{% endhint %}

***

Now you'll start receiving health data events **automatically** to your Data Destination (e.g. webhook)!

You can also request historical data to backfill, to verify that data exists, or as a fallback.

Check out the [iOS SDK reference](/reference/health-and-fitness-api/sdk-references/ios-swift.md) for details about all the functions in the SDK.

***

## 📱 App Example

1. Go to the main entry point of your app, and initialize the TerraiOS SDK.

{% code title="ContentView\.swift" lineNumbers="true" %}

```swift
import SwiftUI
import TerraiOS

// Main entry point for the app
struct ContentView: View {
    
    @State private var terra: TerraManager?

    // Function to initialise the Terra SDK once here for the whole project (async)
    private func initializeTerraSDK(_ completion: @escaping () -> Void) {
        Terra.instance(devId: "YOUR_DEV_ID", referenceId: "<YOUR REFERENCE ID>") { manager, error in
            guard let manager = manager, error == nil  else {
                fatalError("Error initialising Terra SDK: \(error?.localizedDescription)")
            }
            terra = manager   // TerraManager initialised!
            completion() 
        }
    }

    // Function to ensure the user is connected to Apple Health or restablish it.
    private func ensureUserConnection() {
        guard let terra = terra else { return }
        connectUser(terra, "AUTH TOKEN") {
            // User connection ensured!
            // Continue here if more processing needed...
        }
    }

    var body: some View {
        Text("Welcome to my App!")
            .onAppear {
                // initialise SDK and ensure user is connected
                initializeTerraSDK(ensureUserConnection)
            }
    }
}
```

{% endcode %}

2. Define the core functions of the SDK that you need to use, e.g. `getUserId()`, `intiConnection()`

{% code title="UserConnectionManager.swift" lineNumbers="true" %}

```swift
import TerraiOS

// connect the user to Apple Health (triggers permission popup if not already connected)
func connectUser(_ terra: TerraManager, _ token: String, _ completion: @escaping () -> Void) {
    // check if the user is already connected (synchronous)
    guard terra.getUserId(type: .APPLE_HEALTH) == nil else {
        // User is connected, we can move forward
        completion()
        return
    }
    
    // trigger the permission screen if user is not connected (asynchronous)
    terra.initConnection(type: .APPLE_HEALTH, token: token) {success, error in
        guard success && error == nil else {
            fatalError("🤯: \(error?.localizedDescription)")
        }
        // Apple Health user connected!
        completion()
    }
}
```

{% endcode %}

***

## Disconnecting a user

In order to disconnect an Apple Health user, you may use [the same endpoint as for Web API-based integrations](/reference/health-and-fitness-api/readme.md#delete-auth-deauthenticateuser), called from your **backend**.

***

You can request for historical data using one of the [data retrieval functions](/reference/health-and-fitness-api/sdk-references/ios-swift.md#data-retrieval). You may set `toWebhook` to false if you wish for the callback function to return the data payload on the client side.

{% code title="Swift" lineNumbers="true" %}

```swift
terra.getDaily(type: .APPLE_HEALTH, startDate: startDate, endDate: endDate, toWebhook: false) {
    success, payload, err in
    guard success, err == nil else {
        fatalError("🤯: \(err?.localizedDescription ?? "unknown error")")
    }
    // Data is stored in `payload`
    print(payload)
}
```

{% endcode %}

The getter functions are asynchronous, so include a callback as in the example above.

***

## Writing Data

* [postActivity](/reference/health-and-fitness-api/sdk-references/ios-swift.md#postactivity): allows you to write a **completed** **activity** to a user's Apple Health
* [postNutrition](/reference/health-and-fitness-api/sdk-references/ios-swift.md#postnutrition): allows you to write a **nutrition** **log** to a user's Apple Health
* [postBody](/reference/health-and-fitness-api/sdk-references/ios-swift.md#postbody): allows you to write a **body measurement** to a user's Apple Health

{% tabs %}
{% tab title="postActivity" %}
In order to write a **completed** **activity** to a user's Apple Health, use the [postActivity](/reference/health-and-fitness-api/sdk-references/ios-swift.md#postactivity) function as below.

{% hint style="warning" %}
`device_data` must be passed in for postActivity to succeed. Requires iOS 14+.
{% endhint %}

{% code title="Swift" lineNumbers="true" %}

```swift
import TerraiOS

func postActivityData(terra: TerraManager) {
    let activityData = TerraActivityData(
        heart_rate_data: TerraHeartRateData(
            summary: TerraHeartRateSummaryData(avg_hr_bpm: 140.0, max_hr_bpm: 180.0)
        ),
        metadata: TerraActivityMetaData(
            type: TerraActivityType.RUNNING.rawValue,
            end_time: Date().terraString,
            start_time: Date().addingTimeInterval(-3600).terraString,
            name: "Morning Run"
        ),
        device_data: TerraDeviceData(name: "Terra"),
        distance_data: TerraActivityDistanceData(
            summary: TerraDistanceSummaryData(distance_meters: 5000.0)
        ),
        calories_data: TerraCaloriesData(total_burned_calories: 600.0)
    )

    terra.postActivity(type: .APPLE_HEALTH, payload: activityData) { success, error in
        guard success, error == nil else {
            print("Error: \(error?.localizedDescription ?? "unknown")")
            return
        }
        print("Activity posted successfully!")
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="postBody" %}
In order to write **Body Measurement** **data** to a user's Apple Health, use the [postBody](/reference/health-and-fitness-api/sdk-references/ios-swift.md#postbody) function as below.

{% code title="Swift" lineNumbers="true" %}

```swift
import TerraiOS

func postBodyData(terra: TerraManager) {
    let bodyData = TerraBodyData(
        metadata: TerraBodyMetaData(
            start_time: Date().terraString,
            end_time: Date().terraString
        ),
        oxygen_data: TerraOxygenData(avg_saturation_percentage: 98.0),
        glucose_data: TerraGlucoseData(day_avg_blood_glucose_mg_per_dL: 90.0),
        heart_data: TerraBodyHeartData(
            heart_rate_data: TerraHeartRateData(summary: TerraHeartRateSummaryData(
                avg_hr_bpm: 70, resting_hr_bpm: 60
            ))
        ),
        hydration_data: TerraHydrationData(day_total_water_consumption_ml: 2500),
        measurements_data: TerraMeasurementData(measurements: [
            MeasurementDataSample(weight_kg: 70.0)
        ])
    )

    terra.postBody(type: .APPLE_HEALTH, payload: bodyData) { success in
        guard success else {
            print("Error posting body data")
            return
        }
        print("Body data posted successfully!")
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="postNutrition" %}
In order to write **Nutrition** **data** to a user's Apple Health, use the [postNutrition](/reference/health-and-fitness-api/sdk-references/ios-swift.md#postnutrition) function as below.

{% code title="Swift" lineNumbers="true" %}

```swift
import TerraiOS

func postNutritionData(terra: TerraManager) {
    let meal = TerraMealData(
        quantity: TerraQuantityModel(unit: 0, amount: 1.0),
        name: "Breakfast Cereal",
        macros: TerraMacrosModel(protein_g: 30.0, carbohydrates_g: 50.0, fat_g: 15.0, calories: 450.0),
        micros: TerraMicrosModel(vitamin_C_mg: 60.0, iron_mg: 8.0)
    )

    let nutritionData = TerraNutritionData(
        meals: [meal],
        summary: TerraNutritonSummary(
            macros: TerraMacrosModel(protein_g: 100.0, carbohydrates_g: 300.0, fat_g: 50.0, calories: 2500.0),
            water_ml: 2000.0
        )
    )

    terra.postNutrition(type: .APPLE_HEALTH, payload: nutritionData) { success in
        guard success else {
            print("Error posting nutrition data")
            return
        }
        print("Nutrition data posted successfully!")
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

### Planned Workout Sync

* `enablePlannedWorkoutBackendSync`: enables or disables automatic syncing of planned workouts from Terra's backend to Apple Watch via WorkoutKit.
* `syncPlannedWorkoutsFromBackend`: manually triggers a sync of all pending planned workout actions.
* `isPlannedWorkoutBackendSyncEnabled`: returns whether automatic planned workout syncing is currently enabled.

Planned workouts must first be created using [Terra's REST API ](broken://pages/lfyVN1d83BMF6I0Nqpaq)on your backend. Once created, the iOS SDK automatically fetches and syncs them to Apple Watch via WorkoutKit.

To automatically sync planned workouts from Terra to Apple Watch, call `enablePlannedWorkoutBackendSync` after successfully connecting Apple Health.

```
import TerraiOS
import Foundation

func enablePlannedWorkoutSync(terra: TerraManager) {
    guard #available(iOS 17, *) else {
        print("Planned workouts require iOS 17 or later.")
        return
    }

    terra.enablePlannedWorkoutBackendSync(enabled: true)
    print("Planned workout sync enabled!")
}

func syncPlannedWorkouts(terra: TerraManager) {
    guard #available(iOS 17, *) else {
        print("Planned workouts require iOS 17 or later.")
        return
    }

    terra.syncPlannedWorkoutsFromBackend(type: .APPLE_HEALTH)
}

func observePlannedWorkoutSync() {
    NotificationCenter.default.addObserver(
        forName: .terraPlannedWorkoutSynced,
        object: nil,
        queue: .main
    ) { notification in
        guard #available(iOS 17, *),
              let event = notification.object as? WorkoutSyncEvent else {
            return
        }

        print("Synced workout: \(event.displayName ?? "Workout")")
        print("Action: \(event.actionType)")
    }

    NotificationCenter.default.addObserver(
        forName: .terraPlannedWorkoutSyncCompleted,
        object: nil,
        queue: .main
    ) { notification in
        guard #available(iOS 17, *),
              let result = notification.object as? PlannedWorkoutSyncResult else {
            return
        }

        print("Sync complete.")
        print("Successfully synced: \(result.syncedCount)")
        print("Failed: \(result.failedCount)")
    }
}
```

***

{% hint style="warning" %}

## Data Sources requiring the Terra **Mobile-SDK**

The **Mobile SDK** is <mark style="color:red;">**ONLY**</mark> used to connect to the following integrations:

1. <img src="/files/HWBv7PWMxBEc0WMNdWrW" alt="" data-size="line">**Apple Health -** iOS
2. <img src="/files/qO7xuPhRY1YEKm096Aq2" alt="" data-size="line"> **Samsung Health -** Android
3. <img src="/files/zhkGsxfmF1M2jUWRUXRA" alt="" data-size="line"> **Google Fit & Health Connect**- Android.

Note: the [Health & Fitness API](/health-and-fitness-api/getting-started.md) is the preferred way to connect to Google Fit due to better reliability.

For **ALL** other integrations, please refer to Integration Setup the [Health & Fitness API](/health-and-fitness-api/getting-started.md).
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tryterra.co/health-and-fitness-api/mobile-only-sources/ios-swift.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
