For the complete documentation index, see llms.txt. This page is also available as Markdown.

iOS (Swift)

The iOS SDK allows you to seamlessly send data from iOS to your backend or to your app directly!

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

spinner
  1. Add https://github.com/tryterra/TerraiOS as a package dependency to your Xcode project.

  2. Add Capabilities:

    1. Healthkit > Healthkit Background Delivery

    2. Background Modes > Background processing

    3. Background modes > Background fetch

  3. In your info.plist, add the following:

Key
Value

Privacy - Health Share Usage Description

Description of how Health data is used

(Min 3 words)

Privacy - Health Records Usage Description

Description of how Health data is used

(Min 3 words)

Privacy - Health Update Usage Description

Description of how Health data is used

(Min 3 words)

Permitted background task scheduler

co.tryterra.data.post.request


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

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

  • completion : Callback with a TerraManager or an instance TerraError

Find more details in the SDK Reference: Terra manager initialization function

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() method provided by the TerraManager instance that you created above.

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

From TerraManager, call 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. See Permissions mapping for every value and the Apple Health type it requests.

  • schedulerOn: This parameter has no effect. Background delivery is controlled by calling Terra.setUpBackgroundDelivery() in your AppDelegate — see Step 5 below.

To learn more about these parameters and their different options, check the iOS SDK Reference.

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

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.

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.

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 POSThttps://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.

Controlling when the HealthKit popup appears

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

  1. When your app calls 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.

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.

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

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.

Webhook payload example

This means data from the same real person can arrive at your webhook under different user_ids sharing the same reference_id:

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() 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.

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 nil, 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.


5. Enable Background Delivery

  1. Go to your AppDelegate's didFinishLaunchingWithOptions function.

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

iOS Background Delivery Behaviour:


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.

Common bundle identifiers:

App
Bundle identifier

WHOOP

com.whoop.app

Garmin Connect

com.garmin.connect.mobile

Fitbit

com.fitbit.FitbitMobile

Oura

com.ouraring.oura

To find a specific app's bundle identifier, have the user check Settings → Health → Data Access & Devices on their iPhone.


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

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


Disconnecting a user

In order to disconnect an Apple Health user, you may use the same endpoint as for Web API-based integrations, called from your backend.


You can request for historical data using one of the data retrieval functions. You may set toWebhook to false if you wish for the callback function to return the data payload on the client side.

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


Writing Data

  • postActivity: allows you to write a completed activity to a user's Apple Health

  • postNutrition: allows you to write a nutrition log to a user's Apple Health

  • postBody: allows you to write a body measurement to a user's Apple Health

In order to write a completed activity to a user's Apple Health, use the postActivity function as below.


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


Data Sources requiring the Terra Mobile-SDK

Last updated

Was this helpful?