# iOS (Swift)

## SDK Installation

{% hint style="danger" %}
**RealTime streaming on iOS is only available to those registered on the** [**Apple Developer Program**](https://developer.apple.com/programs/enroll/)
{% endhint %}

{% embed url="<https://app.arcade.software/share/FYXtrn9eKX2WqnmrTmNU>" %}

#### Summary:

1. Add <https://github.com/tryterra/TerraRTiOS> as a package dependency
2. In your info.plist, add:
   1. `Privacy - Bluetooth Always Usage Description`: Justification for BLE usage (shown to the user when requesting permission)
   2. `Privacy - Motion Usage Description`: Justification for phone motion sensor usage (shown to the user when requesting permission - only add if using phone sensors for streaming)

When the app is launched for the first time, these permissions will be requested from the user.

***

## **Connection setup/management**

The SDK revolves around the `TerraRT` class, which manages Bluetooth connections and real-time data streaming.

### SDK Initialization

{% hint style="info" %}
Always initialize the `TerraRT` class to begin using the SDK.

Do so every time the app is opened or brought into the foreground.
{% endhint %}

```swift
import TerraRTiOS

let developerId = "yourDeveloperId"
let referenceId: String? = "yourReferenceId"

let terraRT = TerraRT(devId: developerId, referenceId: referenceId) { success in
    if success {
        print("TerraRT initialization successful!")
    } else {
        print("TerraRT initialization failed.")
    }
}
```

### **Initializing a Connection**

Register the [phone](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/core-concepts#phone) by calling [`initConnection`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#initconnection).

This connects the [phone](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/core-concepts#phone) to Terra, allows you to use other SDK functions, and allows it to be a [producer](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/core-concepts#producer) once a [wearable](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/core-concepts#device) is connected later on.

```swift
let token = "yourAuthToken"  // Generated from your backend

terraRT.initConnection(token: token) { success in
    if success {
        print("Connection initialized successfully!")
    } else {
        print("Failed to initialize connection.")
    }
}
```

To generate the **token**, make the below call **from your backend**

{% openapi src="<https://raw.githubusercontent.com/tryterra/openapi/refs/heads/master/v5.yml>" path="/auth/generateAuthToken" method="post" %}
<https://raw.githubusercontent.com/tryterra/openapi/refs/heads/master/v5.yml>
{% endopenapi %}

### **Connecting a** [device](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/core-concepts#device)

iOS devices support connection via BLE or Apple Watch's WatchConnectivity protocol. For Apple Watch, refer to [the WatchOS integration guide below](#watchos-integration).

The [`startBluetoothScan`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#startbluetoothscan-with-built-in-ui) function returns a SwiftUI `TerraBLEWidget` view for device selection:

```swift
import SwiftUI
import TerraRTiOS

struct BluetoothScanView: View {
    @State private var scanWidget: TerraBLEWidget?
    @State private var connected = false

    var body: some View {
        VStack {
            if let widget = scanWidget {
                widget
            }

            if connected {
                Text("Device connected!")
                    .foregroundColor(.green)
            }

            Button("Start Bluetooth Scan") {
                scanWidget = terraRT.startBluetoothScan(type: .BLE) { success in
                    connected = success
                }
            }
        }
        .padding()
    }
}
```

***

## Streaming Real-Time Data

Once the [phone is registered](#initializing-a-connection) and you've [connected a wearable](#connecting-a-device), you can stream data from the wearable to your mobile app.

#### **Start Streaming**

Call [`startRealtime`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#startrealtime-local-only) to stream data to your app locally (without sending to Terra's server):

```swift
import TerraRTiOS

let connectionType: Connections = .BLE
let dataTypes: Set<DataTypes> = [.HEART_RATE, .STEPS]

terraRT.startRealtime(type: connectionType, dataType: dataTypes) { update in
    if let value = update.val {
        print("\(update.type ?? ""): \(value)")
    }
    if let dataArray = update.d {
        print("\(update.type ?? ""): \(dataArray)")
    }
}
```

To also send data to Terra's server, see the [Your App → Terra](https://docs.tryterra.co/streaming-api/your-app-greater-than-terra/ios-swift) guide.

#### Stop streaming and disconnect

```swift
terraRT.stopRealtime(type: .BLE)
terraRT.disconnect(type: .BLE)
```

***

## WatchOS Integration

{% hint style="info" %}
To receive a data stream from an Apple Watch, the watch must be instructed to initiate a data stream to the iOS [phone](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/core-concepts#phone) it is paired with.

For that, you'll need to have a WatchOS companion app running on the watch, which runs [`startStream`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#startstream) or [`startExercise`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#startexercise)
{% endhint %}

#### Setup

1. Create a WatchOS target within your iOS project:

   File -> New -> Target -> Watch App for iOS App.
2. Enable **HealthKit** and **Background Modes** for the WatchOS app, and add the following privacy keys to your `Info.plist`:
   1. `Privacy - Health Share Usage Description`
   2. `Privacy - Health Update Usage Description`
   3. `Privacy - Health Records Usage Description`

### Start Streaming from WatchOS

{% hint style="info" %}
Outside of a workout session, data recording frequency may be reduced. In order to capture data at the highest possible frequency, you'll want to [start a workout session](#streaming-workouts-from-watchos)
{% endhint %}

On the iOS app, listen for updates from the WatchOS app using `startRealtime` with `.WATCH_OS`:

{% tabs %}
{% tab title="WatchOS app" %}

```swift
import WatchKit
import TerraRTiOS

class InterfaceController: WKInterfaceController {
    var terra: Terra?

    override func awake(withContext context: Any?) {
        super.awake(withContext: context)

        do {
            terra = try Terra()
            terra?.connect()
            print("Terra SDK initialized on watchOS")
            startStreamingData()
        } catch {
            print("Failed to initialize Terra SDK: \(error)")
        }
    }

    func startStreamingData() {
        let dataTypes: Set<ReadTypes> = [.HEART_RATE, .STEPS]
        terra?.startStream(forDataTypes: dataTypes) { success, error in
            if success {
                print("Streaming started")
            } else {
                print("Failed: \(error?.localizedDescription ?? "Unknown error")")
            }
        }
    }

    func stopStreamingData() {
        terra?.stopStream()
    }
}
```

{% endtab %}

{% tab title="iOS app" %}

```swift
import TerraRTiOS
import UIKit

class ViewController: UIViewController {
    var terraRT: TerraRT?

    override func viewDidLoad() {
        super.viewDidLoad()

        terraRT = TerraRT(devId: "your-dev-id", referenceId: "your-reference-id") { success in
            if success {
                print("Terra SDK initialized")
                self.setupWatchOSConnection()
            }
        }
    }

    func setupWatchOSConnection() {
        try? terraRT?.connectWithWatchOS()
        listenForDataFromWatch()
    }

    func listenForDataFromWatch() {
        let dataTypes: Set<DataTypes> = [.HEART_RATE, .STEPS]
        terraRT?.startRealtime(type: .WATCH_OS, dataType: dataTypes) { update in
            print("Watch update: \(update.type ?? ""): \(update.val ?? 0)")
        }
    }

    func stopRealtimeData() {
        terraRT?.stopRealtime(type: .WATCH_OS)
    }

    func disconnectWatchOS() {
        terraRT?.disconnect(type: .WATCH_OS)
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        stopRealtimeData()
        disconnectWatchOS()
    }
}
```

{% endtab %}
{% endtabs %}

***

### Streaming Workouts from WatchOS

The Apple Watch app can also manage a workout session, during which recording frequency is enhanced.

From the Watch: [`startExercise`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#startexercise), [`stopExercise`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#stopexercise), [`pauseExercise`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#pauseexercise), [`resumeExercise`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#resumeexercise)

From the iOS app: [`pauseWatchOSWorkout`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#pausewatchosworkout--resumewatchosworkout--stopwatchosworkout), [`resumeWatchOSWorkout`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#pausewatchosworkout--resumewatchosworkout--stopwatchosworkout), [`stopWatchOSWorkout`](https://app.gitbook.com/s/eJJpVMsUARUJq9lYmL6t/streaming-api/reference/ios-swift#pausewatchosworkout--resumewatchosworkout--stopwatchosworkout)

```swift
import WatchKit
import TerraRTiOS

class InterfaceController: WKInterfaceController {
    var terra: Terra?

    override func awake(withContext context: Any?) {
        super.awake(withContext: context)

        do {
            terra = try Terra()
            terra?.connect()
        } catch {
            print("Failed to initialize: \(error)")
        }
    }

    @IBAction func startExerciseTapped() {
        terra?.startExercise(forType: .RUNNING) { success, error in
            if success {
                print("Running exercise started")
            } else {
                print("Failed: \(error?.localizedDescription ?? "Unknown")")
            }
        }
    }

    @IBAction func pauseExerciseTapped() {
        terra?.pauseExercise()
    }

    @IBAction func resumeExerciseTapped() {
        terra?.resumeExercise()
    }

    @IBAction func stopExerciseTapped() {
        terra?.stopExercise { success, error in
            if success {
                print("Exercise stopped")
            } else {
                print("Failed: \(error?.localizedDescription ?? "Unknown")")
            }
        }
    }
}
```
