> For the complete documentation index, see [llms.txt](https://docs.tryterra.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tryterra.co/introduction/examples/terra-grip.md).

# Terra Grip

Terra Grip is a React Native app that shows how to stream real-time wearable data with Terra, built on the [`terra-rt`](https://www.npmjs.com/package/terra-rt) SDK. Scan a QR code from your Terra dashboard and the phone pairs to a heart-rate strap, watch, or its own sensors and streams live data to Terra for the next several hours.

It's the *producer* half of a [Streaming API](/streaming-api/getting-started.md) integration (the wearable → app → Terra path), and the code you copy to build it into your own app. There's no backend to run: the dashboard QR carries a reusable token, so no API keys are ever typed into or stored by the app.

Terra Grip is the `streaming-mobile-app` example in the [`terra-examples`](https://github.com/tryterra/terra-examples) repository. Scaffold your own copy with one command:

```bash
npm create tryterra-app -- --template streaming-mobile-app
cd my-app/app && npm install && npx expo run:ios
```

{% hint style="info" %}
**Just want to try the app?** Install Terra Grip from the [App Store](https://apps.apple.com/app/id6789555253) or [Google Play](https://play.google.com/store/apps/details?id=co.tryterra.streamingdemo), then scan a pairing QR from your dashboard. To build and modify it yourself, follow [Run it yourself](#run-it-yourself) below.
{% endhint %}

## What it demonstrates

* [Opening a producer connection](/streaming-api/your-app-greater-than-terra/react-native.md) to Terra's WebSocket broker with the `terra-rt` SDK
* [Pairing a wearable to the app](/streaming-api/connect-wearable-to-sdk/react-native.md) over BLE, plus the phone's own sensors and Apple Watch
* QR-code pairing with a reusable `rt.` token (the no-backend flow), held in the device keychain
* Background streaming with the screen locked (`bluetooth-central` on iOS, a foreground service on Android)
* Rendering readings live, straight from the local SDK feed, with honest empty and reconnecting states
* A demo mode that runs the whole producer flow on synthetic data, with no hardware, account, or network

<figure><img src="/files/Cz9da2Yi0uMTARDt1bgO" alt="The Terra Grip Live tab showing a live heart-rate reading of 118 bpm and an R-R interval of 574 ms, each with a sparkline"><figcaption><p>The Live tab renders readings straight from the local SDK feed as they stream</p></figcaption></figure>

## How streaming works here

Real-time streaming has four parts: the **wearable**, your app (the **producer**), Terra's **WebSocket broker**, and your backend (the **consumer**). Terra Grip is the producer: it streams the wearable's data to Terra's backend. A consumer (your backend, or the dashboard's streaming tester) reads the same data back out. For the consumer side, see [Terra → Your backend](/streaming-api/terra-greater-than-your-backend.md).

Most integrations mint a short-lived token from their backend for each connection (the flow in the [RN reference](/streaming-api/your-app-greater-than-terra/react-native.md)). Terra Grip takes the simpler demo path: the QR from the dashboard streaming page carries a reusable `rt.` token that already has the user's identity bound into it. The same token registers the SDK and opens every stream for its lifetime, with no minting and no network on reconnect.

## Tech stack

| Layer         | Technology                                                                                 |
| ------------- | ------------------------------------------------------------------------------------------ |
| Framework     | React Native 0.81 on Expo SDK 54 (dev build; Expo Go unsupported)                          |
| Streaming SDK | [`terra-rt`](https://www.npmjs.com/package/terra-rt) 0.2.9 (native module)                 |
| UI            | React 19, `react-native-svg` charts, `lucide-react-native`, Poppins                        |
| Pairing       | `expo-camera` (QR scan), `expo-secure-store` (keychain)                                    |
| State         | A hand-rolled `ProducerController` state machine, bound to React with a `useProducer` hook |

## Run it yourself

{% hint style="info" %}
**Prerequisites:** [Node.js](https://nodejs.org/) 18+, Xcode (with [CocoaPods](https://cocoapods.org/)) or Android Studio, and access to the streaming page of the [Terra dashboard](https://dashboard.tryterra.co/dashboard/streaming). The `terra-rt` SDK is a native module, so the app needs a development build (the first compile takes a few minutes). Expo Go won't work.
{% endhint %}

{% stepper %}
{% step %}

#### Build and launch

```bash
npm create tryterra-app -- --template streaming-mobile-app
cd my-app/app
npm install
npx expo run:ios      # or: npx expo run:android
```

This compiles a development build and launches it on a simulator or connected device. To pair a wearable or scan the dashboard QR you need a **physical phone**; on a simulator or emulator, use the **"Try the demo"** path below.
{% endstep %}

{% step %}

#### Add a test user

On the [streaming page](https://dashboard.tryterra.co/dashboard/streaming) of the Terra dashboard, click **+ Test User**. Optionally set a **Reference ID** to label the user, then choose **Use my own wearable device**.

<figure><img src="/files/nhLjDYi9SK165URBrMPQ" alt="The Add Test Data dialog with an optional Reference ID field and a choice between Use my own wearable device and Generate test data"><figcaption><p>Click + Test User, optionally set a Reference ID, and choose "Use my own wearable device"</p></figcaption></figure>
{% endstep %}

{% step %}

#### Scan the pairing QR

The dashboard shows a pairing **QR code**. Leave it on screen for the next step.

<figure><img src="/files/vTS0hmdA7v70zxBm8eoI" alt="The Connect Your Device dialog showing a pairing QR code"><figcaption><p>The dashboard shows a QR code to pair your device</p></figcaption></figure>
{% endstep %}

{% step %}

#### Scan the QR to pair

Scan the dashboard QR either way:

* **In the app:** open the **Pair** tab and scan it there.
* **With your phone's camera or any QR scanner:** if Terra Grip is installed, it opens and pairs directly; if not, you're sent to the [App Store](https://apps.apple.com/app/id6789555253) or [Google Play](https://play.google.com/store/apps/details?id=co.tryterra.streamingdemo) to install it first, then scan again.

Either way, the app stores the reusable `rt.` token in the device keychain and shows who you're streaming as, and you're ready to stream from **Connect**.

<figure><img src="/files/50u1f0VvdISvV9P1JKIR" alt="The Terra Grip Pair tab scanner pointed at the dashboard QR code" width="460"><figcaption><p>Scan the dashboard QR on the app's Pair tab to pair</p></figcaption></figure>

{% hint style="warning" %}
**Scanning the QR needs a physical phone** with a camera. The iOS Simulator and Android emulator can't scan it. To try the app without a device (or without dashboard access), tap **"Try the demo"** on first launch and the full producer flow runs on synthetic local data.
{% endhint %}
{% endstep %}

{% step %}

#### Connect a data source

On the **Connect** tab, pick **BLE device** (a Polar H10, Wahoo TICKR, Garmin HRM, and similar chest straps), **Phone sensors**, or **Apple Watch** on iOS. Tap a discovered device and **Continue**, and the app connects and starts streaming to Terra automatically.

<figure><img src="/files/lWDPBtXKqqPC9vViWidO" alt="The Connect tab listing nearby BLE devices (Polar H10, Wahoo TICKR, and Garmin HRM-Pro) under a Choose your data source heading"><figcaption><p>Pick a BLE sensor, the phone's own sensors, or an Apple Watch on the Connect tab</p></figcaption></figure>
{% endstep %}

{% step %}

#### Watch it stream

The **Connect** tab shows "Streaming to Terra" once the link is live. Open **Live** to see the readings the SDK is emitting (heart rate, R-R interval, and whatever else the device supports), then open the dashboard streaming tester to see the same data arrive on Terra's side.

<figure><img src="/files/yiwFZ4zqIqbNn7BgM92T" alt="The Connect tab showing a connected Wahoo TICKR streaming to Terra, with View live data and Stop streaming buttons"><figcaption><p>Once a device connects, the app streams to Terra until you stop it or the session expires</p></figcaption></figure>
{% endstep %}
{% endstepper %}

## How the Terra integration works

Two files hold the integration, both kept free of UI so they read as reference code.

`app/src/producer/terraSdk.ts` is a thin adapter over `terra-rt`; the demo adapter swaps in behind the same interface. The SDK is initialised with your dev ID and the user's `reference_id`, then the device is registered with a token:

```typescript
import * as rt from 'terra-rt';

async initialize(devId, referenceId) {
  throwIfFailed(await rt.initTerra(devId, referenceId), 'initTerra');
},

async registerDevice(token) {
  throwIfFailed(await rt.initConnection(token), 'initConnection');
},
```

`app/src/producer/ProducerController.ts` drives the sequence (initialise, fetch the token, register, read back the Terra `user_id`), then opens the stream. Because the token is reusable, `fetchSdkToken` and `fetchStreamingToken` return the same string the QR carried:

```typescript
// setup(): initTerra → rt. token → initConnection → map reference_id
await sdk.initialize(devId, referenceId);
const token = await api.fetchSdkToken();
await sdk.registerDevice(token);
const userId = await sdk.getUserId();
this.set({ phase: 'ready', userId });

// startStreaming(): pass the token to open the producer connection
const streamingToken = await api.fetchStreamingToken(this.snapshot.userId);
await sdk.startStreaming(this.snapshot.connectionType, STREAM_DATA_TYPES, streamingToken);
```

`startStreaming` maps onto `rt.startRealtime(connection, dataTypes, token)`. The token is the only difference from local-only streaming: with it, the SDK opens the WebSocket to Terra and relays every reading in real time.

## Explore the code

The [full source is on GitHub](https://github.com/tryterra/terra-examples/tree/main/packages/cli/templates/streaming-mobile-app), including a Jest suite over the producer state machine and demo adapter.

| Path                                     | What it holds                                                           |
| ---------------------------------------- | ----------------------------------------------------------------------- |
| `app/src/producer/terraSdk.ts`           | The `terra-rt` integration surface; start here                          |
| `app/src/producer/ProducerController.ts` | The producer state machine: setup, scanning, streaming lifecycle        |
| `app/src/auth/`                          | The pairing session, keychain storage, and reusable-token provider      |
| `app/src/datatypes/`                     | One entry per streaming data type; unknown types get a generic renderer |
| `wearos/`                                | The standalone Wear OS companion app                                    |

## Watch companions

Terra Grip can also stream from a watch. On **Apple Watch**, a small watchOS companion (generated into the build at prebuild) streams heart rate and steps to the phone over WatchConnectivity. Apple Watch doesn't broadcast standard BLE heart rate, so this can't be reached over BLE. On **Wear OS**, a standalone companion app in `wearos/` streams over Terra's `terra-wearos` SDK. Both are documented in the template's [`docs/`](https://github.com/tryterra/terra-examples/tree/main/packages/cli/templates/streaming-mobile-app/docs) folder.
