Lynx health SDK starter with Apple HealthKit + Health Connect + Huawei Health + Xiaomi provider support.
HealthDataToLynx is an open-source starter project that bridges health data providers (currently Apple HealthKit, Health Connect, Huawei Health, and Xiaomi Health) into a Lynx UI with a minimal and practical architecture.
It is designed for teams who want to:
- ship Lynx UI quickly,
- read real iOS health data with one tap,
- keep API consistency with
react-native-health, - keep a stable TypeScript data contract,
- and keep extending to Health Connect and other providers.
- One-click HealthKit authorization in Lynx UI
- One-click health snapshot reading via Swift native module
- Built-in Xiaomi Health provider adapter:
- supports native
XiaomiHealthManagerbridge when available - supports custom hook-based connector for your own Xiaomi backend
- supports native
- Built-in Health Connect provider adapter:
- supports native
HealthConnectManagerbridge when available - supports hook-based raw record normalization from Android Health Connect payloads
- maps official record types such as steps, exercise sessions, heart rate, blood pressure, blood glucose, sleep, weight, height, respiratory rate, and temperatures
- supports native
- Built-in Huawei Health provider adapter:
- supports native
HuaweiHealthManagerbridge when available - supports hook-based raw payload normalization aligned to Huawei Health Kit field names
- maps latest Huawei sleep record and sleep-breathing record fields into the shared snapshot contract
- supports native
- Complete typed payload for key metrics:
- Activity, sleep, heart, SpO2, body metrics, workouts
- Blood glucose included by default
- Mock data fallback for Lynx Explorer and early UI debugging
- Unified client API:
createHealthClient/quickReadHealthSnapshot - Clean adapter interface for future health providers
react-native-healthcompatibility layer (src/services/react-native-health.ts)
@lynx-js/react@lynx-js/rspeedy- Swift
LynxModulebridge (HealthKitManager) - TypeScript strict mode
HealthDatatoLynx/
docs/
healthkit-logo.svg
ios/HealthKitBridge/
HealthKitManager.swift
README.md
src/
App.tsx
App.css
lib/client.ts
services/health.ts
services/health-connect.ts
services/huawei-health.ts
services/xiaomi-health.ts
services/react-native-health.ts
types/health.ts
adapters/provider.ts
adapters/apple-healthkit.ts
adapters/health-connect.ts
adapters/huawei-health.ts
adapters/xiaomi-health.ts
npm install
npm run devThen copy the Lynx bundle URL (for example http://<your-ip>:3000/main.lynx.bundle?fullscreen=true) into Lynx Explorer.
Install:
npm install health-data-to-lynxThis package is designed for Lynx hosts and backend-connected health products. It ships:
- unified client APIs:
createHealthClient,quickReadHealthSnapshot,readHealthSnapshot - provider adapters: Apple HealthKit, Huawei Health, Xiaomi Health
- provider adapters: Apple HealthKit, Health Connect, Huawei Health, Xiaomi Health
- typed data contracts:
HealthSnapshot,HealthAlert,HealthWorkoutRecord, and related types - Huawei field constants for raw payload normalization
react-native-healthcompatibility helpers
The package is ESM-first and includes TypeScript declarations out of the box.
One-line read is the fastest way to integrate:
import { quickReadHealthSnapshot, readHealthSnapshot } from 'health-data-to-lynx';
const snapshot = await quickReadHealthSnapshot({
provider: 'auto', // auto resolves Apple first, then Health Connect, then Huawei, then Xiaomi
});
// Alias with the same behavior:
const snapshot2 = await readHealthSnapshot({ provider: 'apple-healthkit' });Useful options:
provider: 'auto' | 'apple-healthkit' | 'health-connect' | 'huawei-health' | 'xiaomi-health'authorize: true | falseuseMock: true | falsefallbackToMock: true | false
Default behavior:
provider: 'auto'resolves Apple first, then Health Connect, then Huawei, then Xiaomi.fallbackToMockdefaults totrue.- If native bridge or connector is unavailable, the SDK returns mock data instead of throwing.
- For production flows where silent fallback is not acceptable, set
fallbackToMock: false.
Use createHealthClient when you need explicit control over availability checks, authorization, and read timing:
import { createHealthClient } from 'health-data-to-lynx';
const client = createHealthClient({
provider: 'auto',
fallbackToMock: false,
});
const available = await client.isAvailable();
if (!available) {
throw new Error(`${client.providerName} is not available in the current host`);
}
const authorized = await client.authorize();
if (!authorized) {
throw new Error(`Authorization failed for ${client.providerName}`);
}
const snapshot = await client.readSnapshot();HealthClient exposes:
providerIdproviderNameisAvailable()authorize()readSnapshot({ useMock? })readWithAuthorization({ useMock? })
Use this when your Lynx iOS host already embeds HealthKitManager.swift:
import { createHealthClient } from 'health-data-to-lynx';
const client = createHealthClient({
provider: 'apple-healthkit',
fallbackToMock: false,
});
const snapshot = await client.readWithAuthorization();Expected native module name:
HealthKitManager
Expected native methods:
isHealthDataAvailablerequestAuthorizationgetHealthSnapshot
Use this when your Android Lynx host exposes a native HealthConnectManager or your backend already reads Health Connect records:
import {
createHealthClient,
HEALTH_CONNECT_RECORD_TYPES,
} from 'health-data-to-lynx';
const client = createHealthClient({
provider: 'health-connect',
fallbackToMock: false,
healthConnect: {
isAvailable: async () => true,
requestAuthorization: async () => true,
readRawData: async () => ({
authorized: true,
[HEALTH_CONNECT_RECORD_TYPES.steps]: [
{ startTime: '2026-03-08T00:00:00.000Z', endTime: '2026-03-08T01:00:00.000Z', count: 320 },
{ startTime: '2026-03-08T01:00:00.000Z', endTime: '2026-03-08T02:00:00.000Z', count: 410 },
],
[HEALTH_CONNECT_RECORD_TYPES.bloodPressure]: [
{ time: '2026-03-08T02:00:00.000Z', systolic: 128, diastolic: 82 },
],
[HEALTH_CONNECT_RECORD_TYPES.exercise]: [
{
exerciseTypeName: 'running',
startTime: '2026-03-08T06:00:00.000Z',
endTime: '2026-03-08T06:36:00.000Z',
totalDistance: 5200,
totalEnergyBurned: 382,
},
],
}),
},
});
const snapshot = await client.readWithAuthorization();Available exports for Health Connect:
HEALTH_CONNECT_LATEST_REQUIREMENTSHEALTH_CONNECT_SDK_STATUSHEALTH_CONNECT_FEATURESHEALTH_CONNECT_RECORD_TYPES
Expected native module name:
HealthConnectManager
Expected native methods:
isHealthDataAvailablegetSdkStatusgetFeatureStatusrequestAuthorizationgetHealthSnapshot
If your backend already converts Huawei data into the shared HealthSnapshot shape, inject it directly:
import { createHealthClient } from 'health-data-to-lynx';
const client = createHealthClient({
provider: 'huawei-health',
fallbackToMock: false,
huawei: {
isAvailable: async () => true,
requestAuthorization: async () => true,
readSnapshot: async () => ({
source: 'huawei-health',
authorized: true,
generatedAt: new Date().toISOString(),
activity: {
stepsToday: 8421,
},
heart: {
latestHeartRateBpm: 68,
},
}),
},
});
const snapshot = await client.readWithAuthorization();If you prefer to send raw Huawei fields and let this SDK normalize them, use readRawData:
import {
createHealthClient,
HUAWEI_HEALTH_RECORD_TYPES,
} from 'health-data-to-lynx';
const client = createHealthClient({
provider: 'huawei-health',
huawei: {
isAvailable: async () => true,
requestAuthorization: async () => true,
readRawData: async () => ({
authorized: true,
DT_CONTINUOUS_STEPS_DELTA: [
{ timestamp: '2026-03-08T01:00:00.000Z', delta: 320 },
{ timestamp: '2026-03-08T02:00:00.000Z', delta: 410 },
],
DT_INSTANTANEOUS_BLOOD_PRESSURE: [
{ timestamp: '2026-03-08T02:00:00.000Z', systolic: 128, diastolic: 82 },
],
[HUAWEI_HEALTH_RECORD_TYPES.sleep]: [
{
fall_asleep_time: '2026-03-07T15:10:00.000Z',
wakeup_time: '2026-03-07T22:40:00.000Z',
all_sleep_time: 450,
light_sleep_time: 255,
deep_sleep_time: 108,
dream_time: 87,
sleep_score: 86,
},
],
[HUAWEI_HEALTH_RECORD_TYPES.sleepBreathing]: [
{
sysMode: 2,
sysSessionDate: '2026-03-07T22:30:00.000Z',
eventAhi: 6.2,
sysDuration: 420,
allEventTimes: 8,
},
],
}),
},
});
const snapshot = await client.readWithAuthorization();readRawData is useful when your Android host or backend already reads Huawei Health Kit, but you want one stable JS contract for Lynx.
import { createHealthClient } from 'health-data-to-lynx';
const client = createHealthClient({
provider: 'xiaomi-health',
xiaomi: {
isAvailable: async () => true,
requestAuthorization: async () => true,
readSnapshot: async () => await getXiaomiSnapshotFromBackend(),
},
});
const snapshot = await client.readWithAuthorization();Use this in Lynx Explorer, story-like previews, or early frontend work:
import { quickReadHealthSnapshot } from 'health-data-to-lynx';
const snapshot = await quickReadHealthSnapshot({
provider: 'huawei-health',
authorize: false,
useMock: true,
});import type {
HealthSnapshot,
HealthAlert,
HealthWorkoutRecord,
} from 'health-data-to-lynx';
function renderSnapshot(snapshot: HealthSnapshot): string {
return `${snapshot.source} @ ${snapshot.generatedAt}`;
}import { HealthKit } from 'health-data-to-lynx';
HealthKit.initHealthKit(
{
permissions: {
read: [HealthKit.Constants.Permissions.StepCount],
write: [],
},
},
(err, result) => {
// react-native-health style callback
},
);Publish-ready exports are defined in package.json:
- ESM entry:
pkg/lib/index.js - Types:
pkg/lib/index.d.ts - iOS bridge source:
ios/HealthKitBridge/HealthKitManager.swift
Backward-compatible APIs are still available:
authorizeHealthKitloadHealthSnapshotbuildMockHealthSnapshot
- Apple native bridge name:
HealthKitManager - Huawei native bridge name:
HuaweiHealthManager - Xiaomi native bridge name:
XiaomiHealthManager - If you keep
fallbackToMock: true, unavailable providers do not break the Lynx page - For production, prefer
fallbackToMock: falseso missing permissions or bridge failures are surfaced immediately - For Huawei and Xiaomi backend connectors, keep OAuth, token exchange, and vendor SDK secrets on the server side
See /ios/HealthKitBridge/README.md.
Minimum setup:
- Add
HealthKitManager.swiftto your iOS Lynx host target. - Register
HealthKitManagerin Lynx module provider. - Enable
HealthKitcapability in Xcode. - Add Info.plist usage descriptions:
NSHealthShareUsageDescriptionNSHealthUpdateUsageDescription(if you write data later)
Huawei is supported in two ways:
- Native Lynx bridge:
- register a native module named
HuaweiHealthManager - expose methods aligned with HealthKit bridge style:
isHealthDataAvailablerequestAuthorizationgetHealthSnapshot
- register a native module named
- Hook-based connector:
- pass
huawei.isAvailable / requestAuthorization / readSnapshot / readRawDataintocreateHealthClient readSnapshotcan return a normalizedHealthSnapshotreadRawDatacan return raw Huawei Health Kit payloads keyed by official types such as:DT_CONTINUOUS_STEPS_DELTADT_INSTANTANEOUS_BLOOD_PRESSUREDT_HEALTH_RECORD_SLEEPDT_HEALTH_RECORD_VENTILATOR
- pass
Latest Huawei Health Kit coverage in this repo is aligned to the official docs for:
- atomic sampling data:
- steps, distance, calories, exercise intensity v2, altitude, height, weight
- heart rate, resting heart rate, SpO2, blood glucose, blood pressure
- stress, body temperature, skin temperature, VO2Max
- health records:
- sleep record fields such as
fall_asleep_time,wakeup_time,all_sleep_time,light_sleep_time,deep_sleep_time,dream_time,sleep_score,sleep_type - sleep-breathing record fields such as
sysMode,sysSessionDate,eventAhi,sysDuration,lumisTidvolMedian,clinicalRespRateMedian,maskOff,hypoventilationIndex,obstructiveApneaIndex,allEventTimes
- sleep record fields such as
Huawei Health Kit environment requirements used for alignment:
- Android
7.0to16(API24to36) - HMS Core
5.0.4.300+ - Huawei Health app
11.0.0.512+
You can integrate Xiaomi in two ways:
- Native Lynx bridge:
- register a native module named
XiaomiHealthManager - expose methods aligned with HealthKit bridge style:
isHealthDataAvailablerequestAuthorizationgetHealthSnapshot
- register a native module named
- Hook-based backend connector (no native module required):
- pass
xiaomi.isAvailable / requestAuthorization / readSnapshotintocreateHealthClient - keep Xiaomi OAuth/token exchange in backend for better security
- pass
activity.stepsTodayheart.latestHeartRateBpmheart.heartRateSeriesLast24h[]oxygen.bloodOxygenPercentoxygen.bloodOxygenSeriesLast24h[]metabolic.bloodGlucoseMgDlmetabolic.bloodGlucoseSeriesLast7d[](mg/dL in the shared contract)sleep.asleepMinutesLast36hsleep.apnea.eventCountLast30dsleep.apnea.ahiLastSessionsleep.lightSleepMinutesbody.stressScorebody.skinTemperatureCelsiusworkouts[]
This project focuses on a Lynx-first bridge path, which is currently less common in open-source health SDK examples.
- Huawei Health adapter (native bridge + normalized raw payload support)
- Xiaomi Health adapter (native bridge + custom connector hooks)
- Android Health Connect adapter (native bridge + raw record normalization)
- Scheduled sync + backend uploader
- Example backend schema and alert pipeline
- Patch (
0.1.x): bug fixes, no API contract changes. - Minor (
0.x+1.0): additive fields, new adapter capabilities, backward compatible. - Major (
x+1.0.0): breaking API/type/schema changes.
Commands:
npm run release:patch
npm run release:minor
npm run release:majorPublish:
npm publish --access publicMIT
