First, initialize the TerraManager class within your ContentView file.
Terra initialization
Terra should be initialized every time your app is brought into the foreground
This is a necessary prerequisite for other SDK functions to run as expected
To do this, run the Terra manager initialization function as below:
import TerraiOS
var terra: TerraManager
Terra.instance(devId: "YOUR DEV ID", referenceId: "REFERENCE ID"){manager, error in
terra = manager
}
Terra is thereafter ready to be used with other functions
Connect to Apple Health
Once Terra is initialized, you can create an Apple Health connection.
initConnection only needs to be run a single time.
Apple Health prohibits the permission popup to appear more than once for any given permission, so calling initConnection more than once will result in no action at all
The only case where it would re-appear is if:
you call initConnection with an expanded set of customPermissions
the app is deleted & reinstalled.
// Initialize connection with example data
let token = "example_token"
let customPermissions: Set<CustomPermissions> = []
let schedulerOn = true // Enables background delivery to occur
terra.initConnection(
type: .APPLE_HEALTH,
token: token,
customReadTypes: customPermissions,
schedulerOn: schedulerOn
) { success, error in
if success {
print("Connection successful!")
} else if let error = error {
print("Connection failed: \(error.message)")
}
}
customPermissions
🚧 Webviews & HealthKit permissions screen
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
Enable Background Delivery
This will ensure you get updates for the user's Apple Health data automatically sent to your destination.
Data will
still be sent even if your app is exited/killed completely
if the connection exists, all is good! 🎉 keep vibing along
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
Historical Data retrieval
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.
// e.g.
terra.getDaily(type: Connections.APPLE_HEALTH, startDate: startDate, endDate: endDate, toWebhook: false) {
succ, payload, err in
if succ {
if let d = payload {
print(d)
} else {
print("failed to unpack payload")
}
} else {
print("get failed")
}
}
Check out the iOS SDK reference for details about all the functions in the SDK
Writing data
You may write other types of data into Apple Health through one of the helper functions in the SDK
device_data must be passed in for postActivity to succeed.
Internal implementation
import TerraiOS
// Function to post an activity to Terra
func postActivityData() {
// Create an instance of TerraManager
let terraManager = TerraManager()
// Define the connection type (e.g., APPLE_HEALTH or FREESTYLE_LIBRE)
let connectionType: Connections = .APPLE_HEALTH
// Create a sample TerraActivityData payload
let activityData = TerraActivityData(
metadata: TerraActivityMetaData(
name: "Morning Run",
start_time: Date().addingTimeInterval(-3600).terraString, // 1 hour ago
end_time: Date().terraString, // Now
type: TerraActivityType.RUNNING.rawValue
),
calories_data: TerraCaloriesData(total_burned_calories: 600.0),
heart_rate_data: TerraHeartRateData(
summary: TerraHeartRateSummaryData(
avg_hr_bpm: 140.0,
max_hr_bpm: 180.0
)
),
device_data: TerraDeviceData(
name: "Terra"
),
distance_data: TerraActivityDistanceData(
summary: TerraDistanceSummaryData(distance_meters: 5000.0) // 5 km
)
)
// Post the activity data using the `postActivity` function
terraManager.postActivity(type: connectionType, payload: activityData) { success, error in
if success {
print("Activity posted successfully!")
} else if let error = error {
print("Failed to post activity: \(error.localizedDescription)")
} else {
print("Failed to post activity for an unknown reason.")
}
}
}
// Call the function to post the activity
postActivityData()
import TerraiOS
// Create body data (e.g., body measurements and biometrics)
let bodyMetaData = TerraBodyMetaData(
start_time: Date().terraString, // Start time of the body measurement
end_time: Date().terraString // End time of the body measurement
)
let heartRateData = TerraBodyHeartData(
pulse_wave_velocity_samples: [], // Pulse wave velocity samples (optional)
heart_rate_data: TerraHeartRateData(summary: TerraHeartRateSummaryData(
user_max_hr_bpm: 190, // Max HR during the day
avg_hr_bpm: 70, // Average HR
resting_hr_bpm: 60 // Resting HR
)),
rr_interval_samples: [RRIntervalSample(timestamp: Date().terraString, rr_interval_ms: 850.0, hr_bpm: 70)] // RR Interval data
)
let bodyData = TerraBodyData(
metadata: bodyMetaData,
heart_data: heartRateData,
oxygen_data: TerraOxygenData(avg_saturation_percentage: 98.0), // Average Oxygen saturation
glucose_data: TerraGlucoseData(day_avg_blood_glucose_mg_per_dL: 90.0), // Average blood glucose data
hydration_data: TerraHydrationData(day_total_water_consumption_ml: 2500), // Water consumption in ml
weight_kg: 70.0 // Body weight in kg
)
// Post the body data to Apple Health
terra.postBody(type: .APPLE, payload: bodyData) { success in
if success {
print("Body data successfully written to Apple Health.")
} else {
print("Failed to write body data.")
}
}
import TerraiOS
// Create nutrition data (e.g., meals, water consumption, and nutrient intake)
let nutritionSummary = TerraNutritonSummary(
macros: TerraMacrosModel(
protein_g: 100.0, // Protein intake in grams
carbohydrates_g: 300.0, // Carbohydrates intake in grams
fat_g: 50.0, // Fat intake in grams
calories: 2500.0 // Total calorie intake
),
water_ml: 2000.0 // Water intake in milliliters
)
let meal1 = TerraMealData(
name: "Breakfast Cereal",
quantity: TerraQuantityModel(unit: 0, amount: 1.0), // 1 serving
macros: TerraMacrosModel(protein_g: 30.0, carbohydrates_g: 50.0, fat_g: 15.0, calories: 450.0), // Macro breakdown for breakfast
micros: TerraMicrosModel(vitamin_C_mg: 60.0, iron_mg: 8.0), // Micro-nutrients
timestamp: Date().terraString // Timestamp for the meal
)
let meal2 = TerraMealData(
name: "Steak and fries",
quantity: TerraQuantityModel(unit: 0, amount: 1.0), // 1 serving
macros: TerraMacrosModel(protein_g: 40.0, carbohydrates_g: 80.0, fat_g: 20.0, calories: 600.0), // Macro breakdown for lunch
micros: TerraMicrosModel(vitamin_D_mg: 10.0, calcium_mg: 300.0), // Micro-nutrients
timestamp: Date().terraString // Timestamp for the meal
)
let nutritionData = TerraNutritionData(
meals: [meal1, meal2], // Add multiple meals
summary: nutritionSummary // Summary of nutrition data
)
// Post the nutrition data to Apple Health
terra.postNutrition(type: .APPLE, payload: nutritionData) { success in
if success {
print("Nutrition data successfully written to Apple Health.")
} else {
print("Failed to write nutrition data.")
}
}
Writing Planned Workouts
Planned Workouts are only available for iOS 17 and above
Overview
This will typically look like the following example:
Planned workouts consist of a series of steps. Each step has:
Completion criteria (called duration for all intents and purposes). For example:
distance (keep going until you cover 500m),
energy expenditure (keep going until you burn 200 kcal)
heart rate threshold (keep going until your heart rate reaches above 150 bpm)
etc..
A target to be maintained
heart rate (maintain heart rate between certain bounds)
speed (maintain speed between certain bounds)
grade (inclination above a certain threshold)
etc...
An associated intensity label
Warmup
Active
Cooldown
etc..
An exercise type
Running
Cycling
Deadlifting
etc...
A series of sub-steps
These modalities allow any workout to be described in a series of defined steps for each of its segments.
Once written, a planned workout will be available in the user's Workout app on their Apple Watch. They will then be able to follow the workout and receive guidance throughout each step.
Usage
import TerraiOS
// Create workout steps with durations and targets
let durationTime = PlannedWorkoutStepDuration.Time(TimePlannedWorkoutStepDuration(seconds: 600)) // 10 minutes
let durationDistance = PlannedWorkoutStepDuration.Distance(DistancePlannedWorkoutStepDuration(distance_meters: 2000)) // 2km
let targetSpeed = PlannedWorkoutStepTarget.Speed(SpeedPlannedWorkoutStepTarget(speed_meters_per_second: 2.5)) // 2.5 m/s
let targetHeartRate = PlannedWorkoutStepTarget.HR(HRPlannedWorkoutStepTarget(hr_bpm_low: 120, hr_bpm_high: 150)) // 120-150 bpm
// Define the first step of the workout (e.g., 10-minute warm-up)
let warmUpStep = PlannedWorkoutStep(
order: 1,
type: .STEP,
name: "Warm-up",
description: "Light jogging to warm up",
intensity: 1, // low intensity
durations: [durationTime], // 10-minute duration
targets: [targetHeartRate] // Heart rate target: 120-150 bpm
)
// Define the second step of the workout (e.g., 2km running at target speed)
let runningStep = PlannedWorkoutStep(
order: 2,
type: .STEP,
name: "2km Run",
description: "Steady run with a speed target",
intensity: 3, // medium intensity
durations: [durationDistance], // 2km distance
targets: [targetSpeed] // Speed target: 2.5 m/s
)
// Metadata for the planned workout
let workoutMetadata = TerraPlannedWorkoutMetaData(
id: UUID(), // Unique identifier for the workout
name: "Morning Run",
type: .RUNNING, // Activity type: Running
description: "A planned morning run with warm-up and steady pace",
estimated_duration_seconds: 1800, // Estimated total duration: 30 minutes
estimated_distance_meters: 5000, // Estimated total distance: 5km
estimated_calories: 300, // Estimated calories burned
planned_date: Date().terraString // Planned date of the workout
)
// Create the planned workout with steps and metadata
let plannedWorkout = TerraPlannedWorkout(
steps: [
.PlannedWorkoutSteps(warmUpStep),
.PlannedWorkoutSteps(runningStep)
],
metadata: workoutMetadata
)
// Post the planned workout for Apple Health
if #available(iOS 17, *) {
terra.postPlannedWorkout(type: .APPLE, payload: plannedWorkout) { success, error in
if success {
print("Planned workout successfully written to Apple Health.")
} else {
if let error = error {
print("Failed to write planned workout. Error: \(error)")
} else {
print("Failed to write planned workout for an unknown reason.")
}
}
}
} else {
print("iOS 17 is required to write planned workouts.")
}
Run the on the terra instance you created above to have the Apple Health permission screen pop up.
customPermissions is used to customize the permissions list shown in the Apple Health popup when calling . When empty, it defaults to all available permissions
Add to your AppDelegate's didFinishLaunchingWithOptions function.
To ensure a connection is still valid on the client side, use the method. This function is synchronous and returns the user_id right away or nil if none exists.
Always use this method right after to see if the connection still exists,
if you expect the connection to exist, but returns nil, to re-establish it. No permission screen will be shown again and the user flow will be unaffected
if it is expected for the connection to no longer exist, you may allow the user to re-connect Apple Health if they so choose (you'd then call , but no permission screen would be shown)
postActivity uses the class internally, which has been deprecated as of iOS 17.
Writing planned workouts allows you to create a workout plan for the user (such as those )
In order to write a workout for the user to follow on their Apple Watch, use the function as below, with the
token is a single-use token created to ensure the authentication endpoint for creating a (and connecting the SDK to Terra's servers) does not get abused.
In order to generate it, place the call below on your server, and provide it to your client side using your own logic.
Testing & developing
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
If you do not need to access the , there is no reason to use Terra mobile SDKs!
If you need to access Terra's Health & Fitness API, it is only secure to do so from your server backend and send the desired data to your mobile frontend.
Never expose your API key on the client side unless you are simply testing
Generates an authentication token for the Terra mobile SDKs
post
Creates a token to be used with initConnection() functions in the Terra mobile SDKs in order to create a user record for Apple Health or Samsung Health (or equivalent)
Header parameters
dev-idstringRequired
your developer ID
Example: testingTerra
x-api-keystringRequired
your API key
Example: OtHJok60oQmT8zhnUWc4SWBJI7ztPTs88C0gOsJJ
Responses
application/json
objectOptional
application/json
post
POST /v2/auth/generateAuthToken HTTP/1.1
Host: api.tryterra.co
dev-id: text
x-api-key: text
Accept: */*