Guides
Apple Health Integration
Sync Apple Health (HealthKit) data to AnyBio with two lines of code. BioSDK handles authorization, querying, mapping, and batch upload automatically.
Quick Start
// Enable HealthKit (requests authorization for all supported types)
try await sdk.enableHealthKit()
// Sync recent data to AnyBio
let result = try await sdk.syncHealthKit()
print("Synced \(result.created) observations")
// Optional: enable automatic background sync
sdk.enableHealthKitBackgroundSync(frequency: .hourly)Architecture
iOS App
│
│ 1. sdk.enableHealthKit()
│ → Requests HealthKit read authorization
│
│ 2. sdk.syncHealthKit()
│ → Queries HealthKit for new samples since last sync
│ → Maps HK types → AnyBio biosignal slugs + LOINC codes
│ HKQuantityType.heartRate → biosignal_slug: "Heart Rate"
│
│ 4. Batch POST to AnyBio
│ POST /api/v1/observations/batch
│ Authorization: Bearer org_*
│ source_slug: "apple_health"
│
│ 5. Backend upserts, links to episodes, fires policiesData stays on-device until explicitly synced. No server-to-server OAuth — Apple Health is read locally via HealthKit.
HealthKit → AnyBio Mapping
| HealthKit Type | HK Identifier | AnyBio Biosignal | LOINC | Unit |
|---|---|---|---|---|
| Heart Rate | HKQuantityTypeIdentifier.heartRate | Heart Rate | 8867-4 | bpm |
| Heart Rate Variability | HKQuantityTypeIdentifier.heartRateVariabilitySDNN | Heart Rate Variability | 80404-7 | ms |
| Resting Heart Rate | HKQuantityTypeIdentifier.restingHeartRate | Heart Rate | 8867-4 | bpm |
| Walking Heart Rate Avg | HKQuantityTypeIdentifier.walkingHeartRateAverage | Heart Rate | 8867-4 | bpm |
| Oxygen Saturation | HKQuantityTypeIdentifier.oxygenSaturation | Oxygen Saturation | 59408-5 | % |
| Respiratory Rate | HKQuantityTypeIdentifier.respiratoryRate | Respiratory Rate | 9279-1 | breaths/min |
| Body Temperature | HKQuantityTypeIdentifier.bodyTemperature | Body Temperature | 8310-5 | degC |
| Blood Pressure (systolic) | HKQuantityTypeIdentifier.bloodPressureSystolic | Blood Pressure | 8480-6 | mmHg |
| Blood Pressure (diastolic) | HKQuantityTypeIdentifier.bloodPressureDiastolic | Blood Pressure | 8462-4 | mmHg |
| Blood Glucose | HKQuantityTypeIdentifier.bloodGlucose | Glucose (CGM) | 99504-3 | mg/dL |
| Step Count | HKQuantityTypeIdentifier.stepCount | Step Count | 41950-7 | steps |
| Active Energy Burned | HKQuantityTypeIdentifier.activeEnergyBurned | Calories Burned | 41979-6 | kcal |
| Body Mass | HKQuantityTypeIdentifier.bodyMass | Body Weight | 29463-7 | kg |
| Height | HKQuantityTypeIdentifier.height | Body Height | 8302-2 | cm |
| BMI | HKQuantityTypeIdentifier.bodyMassIndex | Body Mass Index | 39156-5 | kg/m2 |
| VO2 Max | HKQuantityTypeIdentifier.vo2Max | VO2max | 94122-9 | mL/kg/min |
| Sleep Analysis | HKCategoryTypeIdentifier.sleepAnalysis | Sleep Metrics | 93832-4 | — |
| Skin Temperature | HKQuantityTypeIdentifier.appleSleepingWristTemperature | Skin Temperature | 8310-5 | degC |
| Walking Steadiness | HKQuantityTypeIdentifier.appleWalkingSteadiness | Gait Analysis | 95809-3 | % |
| Falls | HKCategoryTypeIdentifier.fallEvent | Falls | 41952-1 | — |
Batch Ingest Endpoint
POST /api/v1/observations/batch
Authorization: Bearer org_your_key
Content-Type: application/jsonRequest Body
{
"observations": [
{
"xuser_id": "550e8400-e29b-41d4-a716-446655440000",
"biosignal_slug": "Heart Rate",
"effective_datetime": "2026-04-04T14:30:00Z",
"value_quantity": 72,
"value_unit": "bpm",
"source_slug": "apple_health",
"metadata": {
"hk_source": "com.apple.health",
"hk_device": "Apple Watch Series 9"
}
},
{
"xuser_id": "550e8400-e29b-41d4-a716-446655440000",
"biosignal_slug": "Oxygen Saturation",
"effective_datetime": "2026-04-04T14:30:00Z",
"value_quantity": 98,
"value_unit": "%",
"source_slug": "apple_health"
}
]
}Response
{
"created": 2,
"errors": []
}If some items fail validation:
{
"created": 1,
"errors": [
{ "index": 1, "error": "Unknown biosignal_slug: invalid_type" }
]
}Status codes:
201— All observations created successfully207— Partial success (some created, some failed — checkerrorsarray)413— Batch too large (max 1000 per request)403— Unauthorized xuser_id
Example: Swift HealthKit Sync
import HealthKit
let store = HKHealthStore()
// 1. Request authorization
let readTypes: Set<HKObjectType> = [
HKQuantityType(.heartRate),
HKQuantityType(.oxygenSaturation),
HKQuantityType(.stepCount),
]
try await store.requestAuthorization(toShare: [], read: readTypes)
// 2. Query recent heart rate samples
let heartRateType = HKQuantityType(.heartRate)
let since = Calendar.current.date(byAdding: .hour, value: -1, to: Date())!
let predicate = HKQuery.predicateForSamples(withStart: since, end: Date())
let samples = try await withCheckedThrowingContinuation { (cont: CheckedContinuation<[HKQuantitySample], Error>) in
let query = HKSampleQuery(sampleType: heartRateType, predicate: predicate, limit: 100,
sortDescriptors: [NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)])
{ _, results, error in
if let error { cont.resume(throwing: error) }
else { cont.resume(returning: results as? [HKQuantitySample] ?? []) }
}
store.execute(query)
}
// 3. Map to AnyBio observations
let observations = samples.map { sample in
[
"xuser_id": xuserId,
"biosignal_slug": "Heart Rate",
"effective_datetime": ISO8601DateFormatter().string(from: sample.startDate),
"value_quantity": sample.quantity.doubleValue(for: .count().unitDivided(by: .minute())),
"value_unit": "bpm",
"source_slug": "apple_health",
"metadata": ["hk_device": sample.device?.name ?? "unknown"]
] as [String: Any]
}
// 4. POST batch to AnyBio
var request = URLRequest(url: URL(string: "https://api.anybio.io/api/v1/observations/batch")!)
request.httpMethod = "POST"
request.setValue("Bearer \(orgKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try JSONSerialization.data(withJSONObject: ["observations": observations])
let (data, response) = try await URLSession.shared.data(for: request)Background Sync
For continuous sync, use HealthKit's background delivery:
// Enable background delivery for heart rate
store.enableBackgroundDelivery(for: heartRateType, frequency: .hourly) { success, error in
if success {
print("Background delivery enabled for heart rate")
}
}
// Register an observer query
let observerQuery = HKObserverQuery(sampleType: heartRateType, predicate: nil) { _, completionHandler, error in
// New heart rate data available — trigger sync
Task {
await syncRecentSamples()
completionHandler()
}
}
store.execute(observerQuery)Deduplication
The batch endpoint handles duplicates automatically. The unique key is:
(xuser_id, biosignal_id, source_id, effective_datetime, resolution, loinc_code)If you sync the same HealthKit sample twice, the second insert is a no-op (or updates if values changed). Safe to re-sync overlapping time windows.
Security Notes
- HealthKit data never leaves the device unless the user explicitly authorizes your app
- Use
org_*key for the batch endpoint (server-side or secure SDK storage) - Each observation is scoped to an xuser — cross-user access is impossible
- Include
hk_sourceandhk_devicein metadata for provenance tracking
Next Steps
- Streaming — Real-time BLE streaming (complementary to HealthKit)
- Session Management — Backend session lifecycle
- Events — Subscribe to BioSDK events