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

Data stays on-device until explicitly synced. No server-to-server OAuth — Apple Health is read locally via HealthKit.

HealthKit → AnyBio Mapping

HealthKit TypeHK IdentifierAnyBio BiosignalLOINCUnit
Heart RateHKQuantityTypeIdentifier.heartRateHeart Rate8867-4bpm
Heart Rate VariabilityHKQuantityTypeIdentifier.heartRateVariabilitySDNNHeart Rate Variability80404-7ms
Resting Heart RateHKQuantityTypeIdentifier.restingHeartRateHeart Rate8867-4bpm
Walking Heart Rate AvgHKQuantityTypeIdentifier.walkingHeartRateAverageHeart Rate8867-4bpm
Oxygen SaturationHKQuantityTypeIdentifier.oxygenSaturationOxygen Saturation59408-5%
Respiratory RateHKQuantityTypeIdentifier.respiratoryRateRespiratory Rate9279-1breaths/min
Body TemperatureHKQuantityTypeIdentifier.bodyTemperatureBody Temperature8310-5degC
Blood Pressure (systolic)HKQuantityTypeIdentifier.bloodPressureSystolicBlood Pressure8480-6mmHg
Blood Pressure (diastolic)HKQuantityTypeIdentifier.bloodPressureDiastolicBlood Pressure8462-4mmHg
Blood GlucoseHKQuantityTypeIdentifier.bloodGlucoseGlucose (CGM)99504-3mg/dL
Step CountHKQuantityTypeIdentifier.stepCountStep Count41950-7steps
Active Energy BurnedHKQuantityTypeIdentifier.activeEnergyBurnedCalories Burned41979-6kcal
Body MassHKQuantityTypeIdentifier.bodyMassBody Weight29463-7kg
HeightHKQuantityTypeIdentifier.heightBody Height8302-2cm
BMIHKQuantityTypeIdentifier.bodyMassIndexBody Mass Index39156-5kg/m2
VO2 MaxHKQuantityTypeIdentifier.vo2MaxVO2max94122-9mL/kg/min
Sleep AnalysisHKCategoryTypeIdentifier.sleepAnalysisSleep Metrics93832-4
Skin TemperatureHKQuantityTypeIdentifier.appleSleepingWristTemperatureSkin Temperature8310-5degC
Walking SteadinessHKQuantityTypeIdentifier.appleWalkingSteadinessGait Analysis95809-3%
FallsHKCategoryTypeIdentifier.fallEventFalls41952-1

Batch Ingest Endpoint

POST /api/v1/observations/batch
Authorization: Bearer org_your_key
Content-Type: application/json

Request 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 successfully
  • 207 — Partial success (some created, some failed — check errors array)
  • 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_source and hk_device in metadata for provenance tracking

Next Steps

On this page