> For the complete documentation index, see [llms.txt](https://docs.thryve.health/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.thryve.health/integrate-your-mobile-app/direct-bluetooth-device-connection.md).

# Direct bluetooth device connection

{% hint style="warning" %}
Please note that **ThryveBLE** module is in beta; Flutter support will follow.
{% endhint %}

{% hint style="warning" %}
**React Native:** only the ThryveGarminCompanion module is available. ThryveBLE (heart rate and glucose monitors) is not supported.
{% endhint %}

{% hint style="info" %}
Starting with version 5.3.0, the Thryve SDK supports [Garmin Companion](https://developer.garmin.com/health-sdk/overview/)&#x20;
{% endhint %}

With Thryve, you can connect [Garmin Companion devices](https://developer.garmin.com/health-sdk/overview/), blood glucose monitors and heart rate monitors directly via Bluetooth Low Energy (BLE). The Thryve Device module abstracts the complexity of Bluetooth integration by providing:

* **Effortless connectivity:** Automatically handles permissions, device discovery, connections, and communication.
* **Automated data access:** Retrieves health data from connected devices and uploads it to the Thryve data warehouse, ensuring your application always has up-to-date metrics.
* **Comprehensive device handling:** Manages device interactions end-to-end, from connection stability to reliable data acquisition.

<figure><img src="https://597945514-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXHZczPNWZCe1I09YetDl%2Fuploads%2FYjx41xZdtaWO1YffJh4I%2FThryve%20Connect%20Whoop%20Heart%20Rate%20Monitor%20Bluetooth.gif?alt=media&amp;token=5bd4cf2b-bfaf-46fc-9b82-51bb8f90b89d" alt="" width="188"><figcaption></figcaption></figure>

This guide explains how to let users connect and disconnect devices and includes UI integration examples. For a best-practice implementation, refer to the Thryve Sample App.

{% hint style="info" %}
The module is compatible with [Blood Glucose Monitors](https://www.bluetooth.com/specifications/specs/glp-1-0-1/) and [Heart Rate Monitors](https://www.bluetooth.com/specifications/specs/heart-rate-profile-1-0/) that adhere to open Bluetooth device profiles. Devices from manufacturers that do not follow Bluetooth standards may not function properly.
{% endhint %}

<details>

<summary>Verified Blood Glucose Monitors</summary>

Correct functionality has been verified for the following devices:

* Roche ACCU-CHEK Instant
* Roche ACCU-CHEK Guide
* FORA 6 Connect
* Contour Care

</details>

<details>

<summary>Verified Devices with real-time BLE broadcasting capabilities</summary>

Correct functionality has been verified for the following devices:

* Coros Heart Rate Monitor
* Garmin HRM-Dual
* Polar H7 Heart Rate Sensor
* Polar H9 Heart Rate Sensor
* Polar H10 Heart Rate Sensor
* Suunto Smart Heart Rate Belt
* Whoop 4.0 (requires [enabled Heart Rate Broadcast](https://support.whoop.com/s/article/Heart-Rate-Broadcast?language=en_US))
* Whoop 5.0 (requires [enabled Heart Rate Broadcast](https://support.whoop.com/s/article/Heart-Rate-Broadcast?language=en_US))
* [Garmin Companion Devices](https://developer.garmin.com/health-sdk/overview/)

</details>

## Integrate Thryve BLE with your application

To be able to  integrate `ThryveBLE` module you need to:

{% tabs %}
{% tab title="iOS" %}

1. **Integrate the `ThryveBLE`  module for** [Blood Glucose Monitors](https://www.bluetooth.com/specifications/specs/glp-1-0-1/) and [Heart Rate Monitors](https://www.bluetooth.com/specifications/specs/heart-rate-profile-1-0/) **and/or**\
   **Integrate the  `ThryveGarminCompanion` modules for** [Garmin Companion devices](https://developer.garmin.com/health-sdk/overview/)\
   Make sure to also add `ThryveCore` and `ThryveCommons` modules. Follow either the [cocoapods](/integrate-your-mobile-app/setup-thryve-sdk/ios.md#integration-via-cocoapods) or [framework files](/integrate-your-mobile-app/setup-thryve-sdk/ios.md#integration-via-frameworks) instructions in the [iOS section](/integrate-your-mobile-app/setup-thryve-sdk/ios.md).&#x20;
2. **Add Bluetooth Capability**\
   In Xcode: Navigate to *Target → Signing & Capabilities → add (+) → Bluetooth*.<br>

   <figure><img src="https://597945514-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXHZczPNWZCe1I09YetDl%2Fuploads%2FSwAOvs6UD5h3bIRFPVyQ%2FScreenshot%202026-09-23%20at%2013.03.23.png?alt=media&amp;token=2a5932dd-676f-43ce-ba91-c0bd2e6aba9a" alt=""><figcaption></figcaption></figure>
3. **Add Bluetooth Background Mode**\
   In Xcode: Navigate to *Target → Signing & Capabilities → add (+)* Background Modes → "Uses Bluetooth LE accessories" - it is needed if you want streaming to continue in the background.<br>

   <figure><img src="https://597945514-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXHZczPNWZCe1I09YetDl%2Fuploads%2FsvXqUCMFrozfEmv1uU1a%2FScreenshot%202026-08-18%20at%2014.02.52.png?alt=media&amp;token=029ad5b2-7a22-4815-baaa-3db99c1976d2" alt=""><figcaption></figcaption></figure>
4. **Add `Privacy - Bluetooth Always Usage Description` to Info.plist**\
   `<key>NSBluetoothAlwaysUsageDescription</key>`

   `<string>Bluetooth permission for Thryve</string>`
   {% endtab %}

{% tab title="Android" %}

1. **Integrate the `thryve_module_ble` module for** [Blood Glucose Monitors](https://www.bluetooth.com/specifications/specs/glp-1-0-1/) and [Heart Rate Monitors](https://www.bluetooth.com/specifications/specs/heart-rate-profile-1-0/) and/or\
   **Integrate the `thryve_module_garmin_companion` module for** [Garmin Companion devices](https://developer.garmin.com/health-sdk/overview/).\
   Make sure to also add `thryve_core_sdk` and `thryve_module_commons` modules. Follow the instructions in either  the [Thryve Repository Dependencies ](/integrate-your-mobile-app/setup-thryve-sdk/android.md#integrate-using-thryve-repository-dependencies)or  [Thryve .aar Libraries](https://docs.thryve.health/integrate-your-mobile-app/pages/gd15Xn3eNpfQhy8TrHU5#integrate-using-thryve-.aar-libraries) in the [Android section](https://open.gitbook.com/~site/site_WRtC1/mobile-apps/setup-thryve-sdk/android)
2. **Add the following `BLUETOOTH` permissions to the `AndroidManifest.xml`**

   ```
   <uses-permission android:name="android.permission.BLUETOOTH" />
   <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
   <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
   <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
   ```
3. **Add the `ACCESS_FINE_LOCATION` permission to `AndroidManifest.xml`**\
   This is required for all applications targeting Android devices running below Android 12

   ```
   <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
   ```

{% endtab %}
{% endtabs %}

### Configure the `ThryveBLE` and `ThryveGarminCompanion` modules

When initializing Thryve SDK make sure to define the `deviceTypes` and an optional `ThryveDeviceEventListener` in the `ThryveDeviceConfig` .

{% tabs %}
{% tab title="iOS" %}

```swift
import ThryveCore
import ThryveCommons

let deviceConfig = ThryveDeviceConfig(
    // Optional: restricts BLE discovery. Defaults to Set(ThryveDeviceType.allCases).
    deviceTypes: [.heartRateMonitor, .glucoseMonitor],
    garminCompanionConfig: ThryveGarminCompanionConfig(
        license: "YOUR_GARMIN_LICENSE",
        // Optional: restricts the real-time types streamed from the Garmin device. Defaults to Set(ThryveGarminCompanionDataType.allCases).
        dataTypes: [.heartRate, .stress, .steps],
        // Optional: selects the active Garmin modes. Defaults to ThryveGarminCompanionMode.allCases.
        modes: [.realTime]
    ),
    // Optional: Receives callbacks for device found/connected/disconnected/data `ThryveDeviceEventListener` protocol
    // Implements any of `onDeviceFound(response: ThryveResponse<ThryveDevice>)`, 
    // `onDeviceConnecting(response: ThryveResponse<ThryveDevice>)`, 
    // `onDeviceConnected(response: ThryveResponse<ThryveDevice>)`, 
    // `onDeviceDisconnected(response: ThryveResponse<ThryveDevice>)`, 
    // `onDeviceReadingReceived(device: ThryveBLEDevice, dataType: any ThryveDataType, response: ThryveResponse<ThryveDeviceReading>)` functions
    eventListener: self 
)

let thryveSDKConfig = ThryveSDKConfig(
    authId: "AUTH_ID",
    authSecret: "AUTH_SECRET",
    endUserAlias: "YOUR_UNIQUE_USER_IDENTIFIER",
    endUserId: nil, 
    locale: "de",
    configs: [deviceConfig],
    observability: ObservabilityConfig(tracingEnabled: true, crashReportingEnabled: true)
)
        
let thryveSDK = await ThryveSDK.getOrCreate(thryveSDKConfig)

// Optionally, when ThryveSDK is initialized from a non-async context (AppDelegate, legacy code, etc.), it is recommended to use the getOrCreate(...) callback to know when the SDK is ready.

ThryveSDK.getOrCreate(thryveSDKConfig) { initResult in
    if initResult.successful {
        Logger.i { "ThryveSDK ready, you can start calling API methods" }
    } else if let error = initResult.errors?.first {
        Logger.e { "ThryveSDK init failed: \(error.errorMessage ?? "Unknown error")" }
    }
} 
```

{% endtab %}

{% tab title="Android" %}

```kotlin
 val deviceConfig = ThryveDeviceConfig(
        deviceTypes = ThryveDeviceType.entries,
        garminCompanionConfig = ThryveGarminCompanionConfig(
            license = "YOUR_GARMIN_LICENSE",
            modes = listOf(ThryveGarminCompanionMode.REAL_TIME),
            dataTypes = ThryveGarminCompanionDataType.entries.toList(),
        ),
        eventsListener = object : ThryveDeviceEventListener {
            override fun onDeviceFound(response: ThryveResponse<ThryveDevice>) {}
            override fun onDeviceConnecting(response: ThryveResponse<ThryveDevice>) {}
            override fun onDeviceConnected(response: ThryveResponse<ThryveDevice>) {}
            override fun onDeviceDisconnected(response: ThryveResponse<ThryveDevice>) {}
            override fun onDeviceReadingReceived(device: ThryveDevice, dataType: ThryveDataType, response: ThryveResponse<ThryveDeviceReading>) {}
        }
 )

val thryveSDKConfig = ThryveSDKConfig(
    authId = "ASSIGNED_AUTH_ID",
    authSecret = "ASSIGNED_AUTH_SECRET",
    endUserAlias = "XXXXXXXXXXX",
    endUserId = null, 
    locale = "de",
    deviceConfig = deviceConfig
)
                 
val thryveSDK = ThryveSDK.getOrCreate(thryveSDKConfig, context)

val thryveSDK = ThryveSDK.getOrCreate(thryveSDKConfig, context) { thryveResponse ->
       if(thryveResponse.successful){
           //ThyveSDK initialization processes completed successfully
       } else {
           // ThryveSDK initalization process failed. process the ThryveErrors for the specific reason.
           thryveResponse.errors.map { thryveError -> Logger.e(TAG){" getOrCreate ThryveError in onCreate function $thryveError"} }
       }
  }
```

{% endtab %}

{% tab title="React Native" %}

```tsx
import {
  ThryveSDK,
  ThryveDeviceType,
  ThryveGarminCompanionMode,
  ThryveGarminCompanionDataType,
  useDeviceEventListener,
} from '@thryve/react-native-sdk';

const thryve = new ThryveSDK();

async function initialiseThryve() {
  const response = await thryve.getOrCreate({
    authId: 'AUTH_ID',
    authSecret: 'AUTH_SECRET',
    endUserAlias: 'YOUR_UNIQUE_USER_IDENTIFIER',
    endUserId: null,
    locale: 'de',
    configs: [], // health-data sources (Apple, Health Connect, ...) go here
    deviceConfig: {
      deviceTypes: [ThryveDeviceType.GARMIN_COMPANION],
      // Required: `getOrCreate` throws without a license.
      garminCompanionConfig: {
        license: 'YOUR_GARMIN_LICENSE',
        // Optional
        modes: [ThryveGarminCompanionMode.REAL_TIME],
        // Optional: restricts the data types streamed from the Garmin device.
        dataTypes: [
          ThryveGarminCompanionDataType.HEART_RATE,
          ThryveGarminCompanionDataType.STRESS,
          ThryveGarminCompanionDataType.STEPS,
        ],
      },
    },
  });

  // `ThryveResponse` in RN has no `successful` flag. Check `errors` instead.
  if (response.errors && response.errors.length > 0) {
    // ThryveSDK init failed. Process the `ThryveError`s for the specific reason.
    response.errors.forEach((error) => console.error('ThryveSDK init failed:', error.message));
    return;
  }

  // ThryveSDK ready, you can start calling API methods.
}

// Optional: receive callbacks for device found/connecting/connected/disconnected/readings.
// Register them with this hook inside a React component. Implement any of them.
function DeviceScreen() {
  useDeviceEventListener({
    onDeviceFound: (response) => {},
    onDeviceConnecting: (response) => {},
    onDeviceConnected: (response) => {},
    onDeviceDisconnected: (response) => {},
    onDeviceReadingReceived: (device, dataType, response) => {},
    onDataUpload: (response) => {},
  });

  // ...
}
```

{% endtab %}
{% endtabs %}

<table><thead><tr><th width="207.50262451171875">Parameter</th><th width="419.3529052734375">Description</th><th>Mandatory</th></tr></thead><tbody><tr><td><code>deviceTypes</code></td><td>Limits the BLE device types the host application supports (e.g. <code>garminCompanion</code> or <code>heartRateMonitor</code>). Only devices matching the <code>ThryveDeviceType</code>  will appear in discovery.</td><td>no</td></tr><tr><td><code>garminCompanionConfig</code></td><td><p>Configuration for <code>ThryveGarminCompanion</code>  module</p><p></p><p><code>license</code> Garmin Companion specific license issued by Garmin.</p><p><br><code>dataTypes</code> ThryveGarminCompanionDataType supported by your app. </p><p></p><p><code>modes</code> ThryveGarminCompanionMode supported by your app. Only <code>realTime</code> mode is currently supported</p></td><td>no</td></tr><tr><td><code>eventListener</code></td><td><p>An object of <code>ThryveDeviceEventListener</code> that will have to implement  the following functions to receive events from the Thryve BLE modules.</p><pre><code>onDeviceFound(response: ThryveResponse&#x3C;ThryveDevice>) 
onDeviceConnecting(response: ThryveResponse&#x3C;ThryveDevice>) 
onDeviceConnected(response: ThryveResponse&#x3C;ThryveDevice>)
onDeviceDisconnected(response: ThryveResponse&#x3C;ThryveDevice>) 
onDeviceReadingReceived(device: ThryveDevice, dataType: any ThryveDataType, response: ThryveResponse&#x3C;ThryveDeviceReading>)
</code></pre></td><td>no</td></tr></tbody></table>

## Allow users to connect to devices

To enable Bluetooth connectivity for users, implement the following features:

* Display a list of nearby devices available for connection.
* Allow users to connect to and disconnect from devices.
* Provide connection status and troubleshooting information.

Using `ThryveBLE`, these functionalities can be achieved quickly with just a few functions to expedite your initial integration.

### Start device discovery

To discover nearby BLE devices, initiate `startDeviceDiscovery`. Handle any found devices using `onDeviceFound` in the `ThryveDeviceEventListener`. The discovery will end when `stopDeviceDiscovery` is called or the discovery process timed out as per configuration.

The Thryve SDK only returns Bluetooth devices that match the configured Bluetooth Device Profiles, automatically filtering out unsupported devices.

{% hint style="info" %}
Be aware that devices may appear compatible if manufacturers advertise open Bluetooth Device Profiles in device discovery, yet choose proprietary integration methods for connection and data access.
{% endhint %}

{% tabs %}
{% tab title="iOS" %}

```swift
import ThryveCore
import ThryveCommons

class DeviceViewModel: ObservableObject, ThryveDeviceEventListener {

     init() {
        // Registers this object as the event listener. Safe to call before the SDK is ready.
        // Repeat `deviceTypes` - omitting it resets the filter to all types.
        ThryveSDK.get().updateModuleConfig(
            config: ThryveDeviceConfig(
                deviceTypes: [.heartRateMonitor, .glucoseMonitor],
                eventListener: self))
    }

    func startScan() {
        Task {
            // Returns only when the scan window closes (`scanTimeoutSeconds`, 120s by default).
            // Devices are reported incrementally via `onDeviceFound` while scanning.
            let response = await ThryveSDK.get().startDeviceDiscovery()
            guard response.successful else {
                // Process all `[ThryveError]` in `response.errors`
                return
            }

            // Returns a list of `ThryveDevice` found
            let listOfThryveDevices = response.data
        }
    }

    // `onDeviceFound` method is a `ThryveDeviceEventListener` event listener callback.
    // It is automatically triggered by ThryveSDK each time a `ThryveDevice` is found
    // during the scanning session in `ThryveSDK.get().startDeviceDiscovery()`
    func onDeviceFound(response: ThryveResponse<ThryveDevice>) {
        guard let device = response.data else {
            // Process all `[ThryveError]` in `response.errors`
            return
        }
        // Process the `ThryveDevice` found in the `startDeviceDiscovery` process
        // example update UI with the device object
    }

    // Call `stopDeviceDiscovery()` to stop the BLE discovery or scanning process
    func stopScan() {
        Task {
            _ = await ThryveSDK.get().stopDeviceDiscovery()
        }
    }
}
```

{% endtab %}

{% tab title="Android" %}

```kotlin
import com.thryve.sdk.ThryveSDK
import com.thryve.sdk.ble

...
fun startScan(activity: ComponentActivity) {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().startDeviceDiscovery(sources = sources, activity = activity) { response ->
            continuation.resume(response)
        }
    }
                
    if (!response.successful) {
      // Process all [ThryveErrors] in response.errors
      return 
    }   
             
   //returns a list of ThryveDevice found
   val listOfThryveDevices = response.data               
}

 //onDeviceFound method is a ThryveDeviceEventListener event listener callback.
 //It is automatically triggered by ThryveSDK each time a ThryveDevice is found 
 //during the scanning session in ThryveSDK.get().startDeviceDiscovery
override fun onDeviceFound(response: ThryveResponse<ThryveDevice>) {
    if (!response.successful) {
       // Process all [ThryveErrors] in response.errors
       return
    } 
    
    val thryveDevice = response.data
    //process the ThryveDevice found in the startDeviceDiscovery process
    //example update UI with the thryveDevice object 
}

//Call stopDeviceDiscovery to stop the device discovery or scanning process
fun stopScan() {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().stopDeviceDiscovery(sources) { response ->
            continuation.resume(response)
        }
    }
                
    if (response.successful) {
      // device discovery process has stopped.
    }else{
      // Process all [ThryveErrors] in response.errors
    } 
}
...
```

{% endtab %}

{% tab title="React Native" %}

```tsx
import { Source, useDeviceEventListener } from '@thryve/react-native-sdk';

// `thryve` is the initialised ThryveSDK instance (see initialisation above).

async function startScan() {
  // Start Garmin first: `await thryve.start(Source.GARMIN_COMPANION, [])`.
  // Optional `sources`: [Source.GARMIN_COMPANION]; omit for all.
  // Resolves only when the scan window closes.
  // Devices are reported incrementally via `onDeviceFound` while scanning.
  const response = await thryve.startDeviceDiscovery([Source.GARMIN_COMPANION]);
  if (response.errors && response.errors.length > 0) {
    // Process all `ThryveError`s in `response.errors`
    return;
  }

  // Returns a list of `ThryveDevice` found
  const listOfThryveDevices = response.data;
}

function DeviceScreen() {
  // `onDeviceFound` is a device event callback, registered with the
  // `useDeviceEventListener` hook inside a React component.
  // It is triggered automatically by the SDK each time a `ThryveDevice` is found
  // during the scanning session in `thryve.startDeviceDiscovery()`.
  useDeviceEventListener({
    onDeviceFound: (response) => {
      if (response.errors && response.errors.length > 0) {
        // Process all `ThryveError`s in `response.errors`
        return;
      }

      // Process the `ThryveDevice` found in the `startDeviceDiscovery` process
      // example: update UI with `response.data`
    },
  });

  // ...
}

// Call `stopDeviceDiscovery` to stop the discovery or scanning process
async function stopScan() {
  const response = await thryve.stopDeviceDiscovery([Source.GARMIN_COMPANION]);
  if (response.errors && response.errors.length > 0) {
    // Process all `ThryveError`s in `response.errors`
    return;
  }

  // The device discovery process has stopped.
}
```

{% endtab %}
{% endtabs %}

**Recommendation:**

Manage the scanning process carefully. Avoid continuous scanning or high scanning timeouts when no devices are available, or users opt not to connect a device. Continuous scanning can significantly drain the smartphone's battery, leading to user frustration and potential app uninstallation.

### Connect to BLE device

To enable users to connect to a BLE device upon a button click, utilize the `connectDevice` method from the `ThryveBLE`. Handle the connection status through the `ThryveDeviceEventListener` interface, using the `onDeviceConnecting` and `onDeviceConnected` callback methods for processing connection events effectively.

<figure><img src="https://597945514-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FXHZczPNWZCe1I09YetDl%2Fuploads%2Fgafe3W7kw7fpsqgtoDHP%2FThryve%20Connect%20Blood%20Glucose%20Meter%20Bluetooth.gif?alt=media&amp;token=bc8bf6bb-0b99-4ecb-a338-cc2954da7045" alt="Showing Bluetooth Device Pairing Process" width="188"><figcaption><p>Pairing process with Roche ACCU-CHEK Guide</p></figcaption></figure>

Once connected, all available data is automatically retrieved and uploaded to the Thryve backend. For broadcasting devices like heart rate monitors, [heart rate data is accessible locally in real-time](/integrate-your-mobile-app/real-time-bluetooth-device-data.md) and is automatically uploaded every 300 readings or every 5 minutes, or on stop/disconnect.

{% tabs %}
{% tab title="iOS" %}

```swift
import ThryveCore
import ThryveCommons

// The SDK holds `eventListener` weakly - make sure the app retains this object,
// or events stop arriving silently.
class DeviceViewModel: ObservableObject, ThryveDeviceEventListener {

    func connect(_ device: ThryveDevice) {
        Task {
            let response: ThryveResponse<Bool> = await ThryveSDK.get().connectDevice(device)
            guard response.successful else {
                // Process all `[ThryveError]` in `response.errors`
                return 
            }
            
            // Process the connection status in the response object 
          }
    }
    
    // `onDeviceConnecting` method is a `ThryveDeviceEventListener` event listener callback.
    // It is automatically triggered by ThryveSDK each time a `ThryveDevice` is connecting 
    // during the execution of `ThryveSDK.get().connectDevice()`
    func onDeviceConnecting(response: ThryveResponse<ThryveDevice>) {
        guard response.successful else {
            // Process all `[ThryveError]` in `response.errors`
            return 
        }
        
        // Process `response.data`. example update UI
    }
    
    // `onDeviceConnected` method is a `ThryveDeviceEventListener` event listener callback.
    // It is automatically triggered by ThryveSDK each time a `ThryveDevice` is connected 
    // during the execution of `ThryveSDK.get().connectDevice()`.
    // Data is automatically uploaded when a BLE device is connected.
    func onDeviceConnected(response: ThryveResponse<ThryveDevice>) {
        guard response.successful else {
            // Process all `[ThryveError]` in `response.errors`
            return 
        }
        
        // Process `response.data`. example update UI
    }
}
```

{% endtab %}

{% tab title="Android" %}

```kotlin
import com.thryve.sdk.ThryveSDK
import com.thryve.sdk.ble

...
fun connectDevice(activity: ComponentActivity, thryveDevice: ThryveDDevice) {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().connectDevice(thryveDevice, activity) { response ->
            continuation.resume(response)
        }
    }
                
    if (!response.successful) {
      // Process all [ThryveErrors] in response.errors
      return 
    }   
             
   //thryveDevice has connected. update application UI          
}

 //onDeviceConnecting method is a ThryveDeviceEventListener event listener callback.
 //It is automatically triggered by ThryveSDK each time a ThryveDevice is connecting 
 //during the execution of ThryveSDK.get().connectDevice
 override fun onDeviceConnecting(response: ThryveResponse<ThryveDevice>) { response ->
    if (!response.successful) {
       // Process all [ThryveErrors] in response.errors
       return
    } 
    
    //ThryveDevice is connecting.
}

 //onDeviceConnected method is a ThryveDeviceEventListener event listener callback.
 //It is automatically triggered by ThryveSDK each time a ThryveDevice is connected 
 //during the execution of ThryveSDK.get().connectDevice
 //Data is automatically uploaded when a device is connected.
 override fun onDeviceConnected(response: ThryveResponse<ThryveDevice>) {
    if (!response.successful) {
       // Process all [ThryveErrors] in response.errors
       return
    } 
    //ThryveDevice is connected.process response.data. example update UI
}

...
```

{% endtab %}

{% tab title="React Native" %}

```tsx
import { ThryveDevice, useDeviceEventListener } from '@thryve/react-native-sdk';

// `thryve` is the initialised ThryveSDK instance (see initialisation above).

async function connect(device: ThryveDevice) {
  const response = await thryve.connectDevice(device);
  if (response.errors && response.errors.length > 0) {
    // Process all `ThryveError`s in `response.errors`
    return;
  }

  // Process the connection status in `response.data`
}

function DeviceScreen() {
  // Callbacks fired while `thryve.connectDevice()` runs.
  useDeviceEventListener({
    onDeviceConnecting: (response) => {
      if (response.errors && response.errors.length > 0) {
        // Process all `ThryveError`s in `response.errors`
        return;
      }

      // Process `response.data`, for example update the UI
    },
    onDeviceConnected: (response) => {
      if (response.errors && response.errors.length > 0) {
        // Process all `ThryveError`s in `response.errors`
        return;
      }

      // Process `response.data`, for example update the UI
    },
  });

  // ...
}
```

{% endtab %}
{% endtabs %}

#### Disconnect BLE device

To enable users to manually disconnect their BLE device, use `disconnectDevice`. Handle the disconnection status with the `ThryveDeviceEventListener`'s `onDeviceDisconnected` callback.

{% tabs %}
{% tab title="iOS" %}

```swift
import ThryveCore
import ThryveCommons

class DeviceViewModel: ObservableObject, ThryveDeviceEventListener {

    func disconnect(_ device: ThryveDevice) {
        Task {
            let response = await ThryveSDK.get().disconnectDevice(device)
            guard response.successful else {
                // Process all `[ThryveError]` in `response.errors`
                return
            }

            // Process the disconnection boolean result in `response.data`
        }
    }
    
    // `onDeviceDisconnected` method is a `ThryveDeviceEventListener` event listener callback.
    // It is automatically triggered by ThryveSDK when a `ThryveDevice` is disconnected 
    // via `disconnectDevice`/`forgetDevice` call or on an unexpected drop, in which case
    // `response.errors` carries the reason.
    func onDeviceDisconnected(response: ThryveResponse<ThryveDevice>) {
        guard response.successful else {
        // Process all `[ThryveError]` in `response.errors`
            return 
        }
            
        // Process the `ThryveDevice` that was disconnected. `response.data`
    }
}
```

{% endtab %}

{% tab title="Android" %}

```kotlin
import com.thryve.sdk.ThryveSDK
import com.thryve.sdk.ble

...
fun onDeviceDisconnected(activity: ComponentActivity, thryveDevice: ThryveDDevice) {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().disconnectDevice(activity, thryveDevice) { response ->
            continuation.resume(response)
        }
    }
                
    if (!response.successful) {
      // Process all [ThryveErrors] in response.errors
      return 
    }   
             
   //thryveDevice has disconnected. update application UI          
}

//onDeviceDisconnected method is a ThryveDeviceEventListener event listener callback.
//It is automatically triggered by ThryveSDK when a ThryveDevice is disconnected
override fun onDeviceDisconnected(response: ThryveResponse<ThryveDevice>) {
    if (!response.successful) {
       // Process all [ThryveErrors] in response.errors
       return
    } 
    //ThryveDevice is disconnected.process response.data. example update UI
}
...
```

{% endtab %}

{% tab title="React Native" %}

```tsx
import { ThryveDevice, useDeviceEventListener } from '@thryve/react-native-sdk';

// `thryve` is the initialised ThryveSDK instance (see initialisation above).

async function disconnect(device: ThryveDevice) {
  const response = await thryve.disconnectDevice(device);
  if (response.errors && response.errors.length > 0) {
    // Process all `ThryveError`s in `response.errors`
    return;
  }

  // Process the disconnection boolean result in `response.data`
}

function DeviceScreen() {
  // Called when a `ThryveDevice` is disconnected.
  useDeviceEventListener({
    onDeviceDisconnected: (response) => {
      if (response.errors && response.errors.length > 0) {
        // Process all `ThryveError`s in `response.errors`
        return;
      }
      // Process the `ThryveDevice` that was disconnected: `response.data`
    },
  });

  // ...
}
```

{% endtab %}
{% endtabs %}

## Show device connection status

We recommend to transparently display users actively connected devices as well as known, previously connected devices and allow them to re-connect those.

{% tabs %}
{% tab title="iOS" %}

<pre class="language-swift"><code class="lang-swift"><strong>import ThryveCore
</strong>import ThryveCommons

class DeviceViewModel: ObservableObject, ThryveDeviceEventListener {

    func getActiveDevices() {
        Task {
            // Optional `sources:` `[.ble]` or `[.garminCompanion]`; omit to query all.
            let response = await ThryveSDK.get().getActiveDevices()
            guard response.successful else {
                // Process all `[ThryveError]` in `response.errors`
                return
            }
    
            // Process the active (connected or connecting) `[ThryveDevice]` in `response.data`
        }
    }
    
    func getPreviousDevices() {
        Task {
            // Optional `sources:` `[.ble]` or `[.garminCompanion]`; omit to query all.
            let response = await ThryveSDK.get().getPreviousDevices()
            guard response.successful else {
                // Process all `[ThryveError]` in `response.errors`
                return
            }

            // Process the previously connected `[ThryveDevice]` in `response.data`
        }
    }
}
</code></pre>

{% endtab %}

{% tab title="Android" %}

```kotlin
import com.thryve.sdk.ThryveSDK
import com.thryve.sdk.ble
...

fun getActiveBLEDevices() {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().getActiveDevices { response ->
            continuation.resume(response)
        }
    }
                
    if (!response.successful) {
      // Process all [ThryveErrors] in response.errors
      return 
    }   
    //Process all active or connected [ThryveDevice] in response.data         
}

fun getPreviousDevices() {
    val response = suspendCancellableCoroutine { continuation ->
        ThryveSDK.get().getPreviousDevices { response ->
            continuation.resume(response)
        }
    }
                
    if (!response.successful) {
      // Process all [ThryveErrors] in response.errors
      return 
    }   
    //Process all previous [ThryveDevice] in response.data         
}

...

```

{% endtab %}

{% tab title="React Native" %}
**Get active devices**

```tsx
import { Source } from '@thryve/react-native-sdk';

// `thryve` is the initialised ThryveSDK instance (see initialisation above).
// Start Garmin first: `await thryve.start(Source.GARMIN_COMPANION, [])`.

async function getActiveDevices() {
  // Optional `sources`: [Source.GARMIN_COMPANION]; omit to query all.
  const response = await thryve.getActiveDevices([Source.GARMIN_COMPANION]);
  if (response.errors && response.errors.length > 0) {
    // Process all `ThryveError`s in `response.errors`
    return;
  }

  // Process the active (connected or connecting) `ThryveDevice`s in `response.data`
}
```

**Get previous devices**

```tsx
import { Source } from '@thryve/react-native-sdk';

// `thryve` is the initialised ThryveSDK instance (see initialisation above).
// Start Garmin first: `await thryve.start(Source.GARMIN_COMPANION, [])`.

async function getPreviousDevices() {
  // Optional `sources`: [Source.GARMIN_COMPANION]; omit to query all.
  const response = await thryve.getPreviousDevices([Source.GARMIN_COMPANION]);
  if (response.errors && response.errors.length > 0) {
    // Process all `ThryveError`s in `response.errors`
    return;
  }

  // Process the previously connected `ThryveDevice`s in `response.data`.
  // These survive app restarts and can be passed straight to `connectDevice`.
}
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

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

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.thryve.health/integrate-your-mobile-app/direct-bluetooth-device-connection.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
