KHealth
1.1.0indexedSimplifies access to Android Health Connect and Apple HealthKit APIs, enabling easy health data management, permission handling, and record reading/writing across platforms in a unified environment.
Simplifies access to Android Health Connect and Apple HealthKit APIs, enabling easy health data management, permission handling, and record reading/writing across platforms in a unified environment.
KHealth (Kotlin Health) is a modern Kotlin Multiplatform library providing a unified, type-safe API over Android's Health Connect and Apple's HealthKit (iOS and watchOS). It is designed from the ground up for seamless use in Kotlin Multiplatform (KMP) and Compose Multiplatform projects.
[!NOTE]
Sample apps showcasing integration can be found in thesampleAppsdirectory:
sampleApps/shared: Shared KMP logic, repository layer, and sample health interactions.sampleApps/android: Android Jetpack Compose app with Health Connect permission rationale.sampleApps/apple: SwiftUI iOS and watchOS applications.
Add the dependency to your shared module's build.gradle.kts:
kotlin {
sourceSets {
commonMain.dependencies {
implementation("io.github.shubhamsinghshubham777:khealth:2.1.0")
}
}
}
Or using Gradle version catalogs:
[versions]
khealth = "2.1.0"
[libraries]
khealth = { module = "io.github.shubhamsinghshubham777:khealth", version.ref = "khealth" }
kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.khealth)
}
}
}
Add HealthKit usage descriptions to your Info.plist:
<plist =>
NSHealthUpdateUsageDescription
We need permission to save health records to Apple Health.
NSHealthShareUsageDescription
We need permission to read your health records from Apple Health.
In Xcode, ensure the HealthKit capability is enabled in your application target's Signing & Capabilities tab (with Clinical Health Records if applicable).
Add the following to your AndroidManifest.xml:
[!TIP] KHealth automatically initializes its Android context via a built-in
ContentProvider. No manual context passing orinitialise()calls are needed!
Add the permissions matching the data types your app accesses:
Instantiate KHealth directly in your shared multiplatform code (e.g., in a ViewModel, Repository, or Dependency Injection module):
// In commonMain (KMP / Compose Multiplatform)
val kHealth = KHealth()
[!NOTE] On Android, if your application needs to prompt the user with permission dialogs via
requestPermissions(), bind your activeComponentActivity:// Option A: Bind activity to an existing instance kHealth.bindActivity(activity) // Option B: Construct with activity in Android source set val kHealth = KHealth(activity)
Check current permission states without prompting the user:
val permissions: Set<KHPermission> = kHealth.checkPermissions(
KHPermission.ActiveCaloriesBurned(read = true, write = true),
KHPermission.HeartRate(read = true, write = false),
)
You can also pass an Iterable<KHPermission>:
val permissionList = listOf(
KHPermission.StepCount(read = true, write = false),
KHPermission.Weight(read = true, write = true),
)
val permissions = kHealth.checkPermissions(permissionList)
Request access from the user:
val permissions: Set<KHPermission> = kHealth.requestPermissions(
KHPermission.ActiveCaloriesBurned(read = true, write = true),
KHPermission.HeartRate(read = true, write = false),
)
KHealth provides intuitive extension functions to verify granted permissions:
// Check if a specific permission is granted with the requested read/write access
val canWriteCalories: Boolean = permissions.isGranted(
KHPermission.ActiveCaloriesBurned(read = false, write = true)
)
// Check if all requested permissions in the response are granted
val allGranted: Boolean = permissions.allGranted()
// Query KHealth directly at any time
val canReadHeartRate: Boolean = kHealth.hasPermission(
KHPermission.HeartRate(read = true, write = false)
)
Write one or more health records using type-safe constructors:
import kotlinx.datetime.Clock
kotlin.time.Duration.Companion.minutes
response: KHWriteResponse = kHealth.writeRecords(
KHRecord.ActiveCaloriesBurned(
unit = KHUnit.Energy.KiloCalorie,
value = ,
startTime = Clock.System.now().minus(minutes),
endTime = Clock.System.now(),
),
KHRecord.HeartRate(
samples = listOf(
KHHeartRateSample(
beatsPerMinute = ,
time = Clock.System.now().minus(minutes),
),
KHHeartRateSample(
beatsPerMinute = ,
time = Clock.System.now().minus(minutes),
),
),
),
KHRecord.BasalMetabolicRate(
unit = KHUnit.Energy.KiloCalorie,
value = ,
time = Clock.System.now(),
),
)
(response) {
KHWriteResponse.Success -> println()
KHWriteResponse.SomeFailed -> println()
KHWriteResponse.Failed -> println()
}
Reading records is fully generic and type-safe. The returned list matches the exact record type requested—no manual casting needed!
import kotlinx.datetime.Clock
import kotlin.time.Duration.Companion.days
// Returns List<KHRecord.HeartRate>
val heartRateRecords = kHealth.readRecords(
KHReadRequest.HeartRate(
startTime = Clock.System.now().minus(7.days),
endTime = Clock.System.now(),
)
)
heartRateRecords.forEach { record ->
record.samples.forEach { sample ->
println("BPM: ${sample.beatsPerMinute} at ${sample.time}")
}
}
stepRecords = kHealth.readRecords(
KHReadRequest.StepCount(
startTime = Clock.System.now().minus(days),
endTime = Clock.System.now(),
)
)
totalSteps = stepRecords.sumOf { it.count }
println()
KHealth supports reading and writing extensive exercise and workout types:
[!NOTE] Types unsupported by a specific platform are safely ignored.
KHealth/
├── khealth/ # Core KMP library module
│ ├── commonMain/ # Interfaces (KHealth), data models, permissions, requests
│ ├── androidMain/ # Health Connect integration, auto-init provider, mappers
│ └── appleMain/ # HealthKit integration (iOS & watchOS), mappers, readers, writers
└── sampleApps/ # Sample demonstration apps
├── shared/ # Shared KMP module with health repository & operations
├── android/ # Native Android app (Jetpack Compose)
└── apple/ # Native Apple apps (SwiftUI iOS & watchOS)
Contributions are welcome! Please feel free to open an issue or submit a Pull Request.
This library is licensed under the Apache 2.0 License. See the LICENSE file for details.
val kHealth = KHealth() directly in commonMain across Android, iOS, and watchOS.ContentProvider. No manual initialise() call or Android Context required in shared code.readRecords(KHReadRequest.HeartRate(...)) directly returns List<KHRecord.HeartRate> without type casting or runtime checks.perms.isGranted(...), perms.allGranted(), and kHealth.hasPermission(...).<!-- Check if Health Connect is installed -->
<queries>
<package android:name="com.google.android.apps.healthdata" />
</queries>
<application ...>
<!-- Android 13 and below: Rationale activity for Health Connect -->
<activity
android:name=".PermissionsRationaleActivity"
android:exported="true">
<intent-filter>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
</activity>
<!-- Android 14+: Rationale activity alias for Health Connect -->
<activity-alias
android:name="ViewPermissionUsageActivity"
android:exported="true"
android:permission="android.permission.START_VIEW_PERMISSION_USAGE"
android:targetActivity=".PermissionsRationaleActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
</intent-filter>
</activity-alias>
</application>
| Type | Manifest Permissions |
|---|
| ACTIVE_CALORIES_BURNED | android.permission.health.READ_ACTIVE_CALORIES_BURNEDandroid.permission.health.WRITE_ACTIVE_CALORIES_BURNED |
| BASAL_METABOLIC_RATE | android.permission.health.READ_BASAL_METABOLIC_RATEandroid.permission.health.WRITE_BASAL_METABOLIC_RATE |
| BLOOD_GLUCOSE | android.permission.health.READ_BLOOD_GLUCOSEandroid.permission.health.WRITE_BLOOD_GLUCOSE |
| BLOOD_PRESSURE | android.permission.health.READ_BLOOD_PRESSUREandroid.permission.health.WRITE_BLOOD_PRESSURE |
| BODY_FAT | android.permission.health.READ_BODY_FATandroid.permission.health.WRITE_BODY_FAT |
| BODY_TEMPERATURE | android.permission.health.READ_BODY_TEMPERATUREandroid.permission.health.WRITE_BODY_TEMPERATURE |
| BODY_WATER_MASS | android.permission.health.READ_BODY_WATER_MASSandroid.permission.health.WRITE_BODY_WATER_MASS |
| BONE_MASS | android.permission.health.READ_BONE_MASSandroid.permission.health.WRITE_BONE_MASS |
| CERVICAL_MUCUS | android.permission.health.READ_CERVICAL_MUCUSandroid.permission.health.WRITE_CERVICAL_MUCUS |
| DISTANCE | android.permission.health.READ_DISTANCEandroid.permission.health.WRITE_DISTANCE |
| ELEVATION_GAINED | android.permission.health.READ_ELEVATION_GAINEDandroid.permission.health.WRITE_ELEVATION_GAINED |
| EXERCISE | android.permission.health.READ_EXERCISEandroid.permission.health.WRITE_EXERCISE |
| FLOORS_CLIMBED | android.permission.health.READ_FLOORS_CLIMBEDandroid.permission.health.WRITE_FLOORS_CLIMBED |
| HEART_RATE | android.permission.health.READ_HEART_RATEandroid.permission.health.WRITE_HEART_RATE |
| HEART_RATE_VARIABILITY | android.permission.health.READ_HEART_RATE_VARIABILITYandroid.permission.health.WRITE_HEART_RATE_VARIABILITY |
| HEIGHT | android.permission.health.READ_HEIGHTandroid.permission.health.WRITE_HEIGHT |
| HYDRATION | android.permission.health.READ_HYDRATIONandroid.permission.health.WRITE_HYDRATION |
| INTERMENSTRUAL_BLEEDING | android.permission.health.READ_INTERMENSTRUAL_BLEEDINGandroid.permission.health.WRITE_INTERMENSTRUAL_BLEEDING |
| LEAN_BODY_MASS | android.permission.health.READ_LEAN_BODY_MASSandroid.permission.health.WRITE_LEAN_BODY_MASS |
| MENSTRUATION | android.permission.health.READ_MENSTRUATIONandroid.permission.health.WRITE_MENSTRUATION |
| NUTRITION | android.permission.health.READ_NUTRITIONandroid.permission.health.WRITE_NUTRITION |
| OVULATION_TEST | android.permission.health.READ_OVULATION_TESTandroid.permission.health.WRITE_OVULATION_TEST |
| OXYGEN_SATURATION | android.permission.health.READ_OXYGEN_SATURATIONandroid.permission.health.WRITE_OXYGEN_SATURATION |
| POWER | android.permission.health.READ_POWERandroid.permission.health.WRITE_POWER |
| RESPIRATORY_RATE | android.permission.health.READ_RESPIRATORY_RATEandroid.permission.health.WRITE_RESPIRATORY_RATE |
| RESTING_HEART_RATE | android.permission.health.READ_RESTING_HEART_RATEandroid.permission.health.WRITE_RESTING_HEART_RATE |
| SEXUAL_ACTIVITY | android.permission.health.READ_SEXUAL_ACTIVITYandroid.permission.health.WRITE_SEXUAL_ACTIVITY |
| SLEEP | android.permission.health.READ_SLEEPandroid.permission.health.WRITE_SLEEP |
| SPEED | android.permission.health.READ_SPEEDandroid.permission.health.WRITE_SPEED |
| STEPS | android.permission.health.READ_STEPSandroid.permission.health.WRITE_STEPS |
| VO2_MAX | android.permission.health.READ_VO2_MAXandroid.permission.health.WRITE_VO2_MAX |
| WEIGHT | android.permission.health.READ_WEIGHTandroid.permission.health.WRITE_WEIGHT |
| WHEELCHAIR_PUSHES | android.permission.health.READ_WHEELCHAIR_PUSHESandroid.permission.health.WRITE_WHEELCHAIR_PUSHES |
| Type | Android | Apple (iOS & watchOS) | Notes / Units |
|---|
| ActiveCaloriesBurned | ✅ | ✅ | Energy (Calorie, KiloCalorie, Joule, KiloJoule) |
| BasalMetabolicRate | ✅ | ✅ | Energy per day (Calorie, KiloCalorie, Joule, KiloJoule) |
| BloodGlucose | ✅ | ✅ | MilliMolesPerLiter, MilliGramsPerDeciLiter |
| BloodPressure | ✅ | ✅ | MillimetersOfMercury (Systolic & Diastolic) |
| BodyFat | ✅ | ✅ | Percentage |
| BodyTemperature | ✅ | ✅ | Celsius, Fahrenheit |
| BodyWaterMass | ✅ | Kilogram, Gram, Milligram, Pound, Ounce | |
| BoneMass | ✅ | Kilogram, Gram, Milligram, Pound, Ounce | |
| CervicalMucus | ✅ | ✅ | Sensation & appearance states |
| CyclingPedalingCadence | ✅ | Revolutions per minute | |
| Distance | ✅ | ✅ | Meter, Kilometer, Mile, Foot, Inch |
| ElevationGained | ✅ | Meter, Kilometer, Mile, Foot, Inch | |
| Exercise | ✅ | ✅ | Supports 70+ exercise/workout categories |
| FloorsClimbed | ✅ | ✅ | Floor count |
| HeartRate | ✅ | ✅ | Samples with BPM and timestamps |
| HeartRateVariability | ✅ | ✅ | RMSSD in milliseconds |
| Height | ✅ | ✅ | Meter, Kilometer, Mile, Foot, Inch |
| Hydration | ✅ | ✅ | Liter, MilliLiter, FluidOunceUs |
| IntermenstrualBleeding | ✅ | ✅ | Spotting records |
| LeanBodyMass | ✅ | ✅ | Kilogram, Gram, Milligram, Pound, Ounce |
| MenstruationPeriod | ✅ | Menstruation period sessions | |
| MenstruationFlow | ✅ | ✅ | Light, Medium, Heavy, Unspecified |
| Nutrition | ✅ | ✅ | Macros (Protein, Carbs, Fat) & Micronutrients |
| OvulationTest | ✅ | ✅ | Inconclusive, Negative, Positive, High, etc. |
| OxygenSaturation | ✅ | ✅ | Blood oxygen percentage |
| Power | ✅ | ✅ | Watt, KiloWatt |
| RespiratoryRate | ✅ | ✅ | Breaths per minute |
| RestingHeartRate | ✅ | ✅ | Beats per minute |
| SexualActivity | ✅ | ✅ | Protection used / unspecified |
| SleepSession | ✅ | ✅ | Sleep stages (Awake, Light, Deep, REM, etc.) |
| Speed | ✅ | MetersPerSecond, KilometersPerHour, MilesPerHour | |
| RunningSpeed | ✅ | MetersPerSecond, KilometersPerHour, MilesPerHour | |
| CyclingSpeed | ✅ | MetersPerSecond, KilometersPerHour, MilesPerHour | |
| StepCount | ✅ | ✅ | Step counts over time ranges |
| Vo2Max | ✅ | ✅ | MillilitersPerMinutePerKilogram |
| Weight | ✅ | ✅ | Kilogram, Gram, Milligram, Pound, Ounce |
| WheelchairPushes | ✅ | ✅ | Wheelchair push counts |
| Type | Android | Apple (iOS & watchOS) | Type | Android | Apple (iOS & watchOS) |
|---|
| AmericanFootball | ✅ | ✅ | MartialArts | ✅ | ✅ |
| Archery | ✅ | MindAndBody | ✅ | ||
| AustralianFootball | ✅ | ✅ | MixedCardio | ✅ | |
| Badminton | ✅ | ✅ | MixedMetabolicCardioTraining | ✅ | |
| Barre | ✅ | Other | ✅ | ✅ | |
| Baseball | ✅ | ✅ | PaddleSports | ✅ | |
| Basketball | ✅ | ✅ | Paddling | ✅ | ✅ |
| Biking | ✅ | ✅ | Paragliding | ✅ | ✅ |
| BikingStationary | ✅ | ✅ | Pickleball | ✅ | |
| BootCamp | ✅ | ✅ | Pilates | ✅ | ✅ |
| Bowling | ✅ | Play | ✅ | ||
| Boxing | ✅ | ✅ | PreparationAndRecovery | ✅ | |
| Calisthenics | ✅ | ✅ | Racquetball | ✅ | ✅ |
| CardioDance | ✅ | RockClimbing | ✅ | ✅ | |
| Climbing | ✅ | RollerHockey | ✅ | ✅ | |
| Cooldown | ✅ | Rowing | ✅ | ✅ | |
| CoreTraining | ✅ | RowingMachine | ✅ | ✅ | |
| Cricket | ✅ | ✅ | Rugby | ✅ | ✅ |
| CrossCountrySkiing | ✅ | Running | ✅ | ✅ | |
| CrossTraining | ✅ | RunningTreadmill | ✅ | ✅ | |
| Curling | ✅ | Sailing | ✅ | ✅ | |
| Cycling | ✅ | ScubaDiving | ✅ | ✅ | |
| Dance | ✅ | Skating | ✅ | ✅ | |
| DanceInspiredTraining | ✅ | SkatingSports | ✅ | ||
| Dancing | ✅ | ✅ | Skiing | ✅ | ✅ |
| DiscSports | ✅ | SnowSports | ✅ | ||
| DownhillSkiing | ✅ | Snowboarding | ✅ | ✅ | |
| Elliptical | ✅ | ✅ | Snowshoeing | ✅ | ✅ |
| EquestrianSports | ✅ | Soccer | ✅ | ✅ | |
| ExerciseClass | ✅ | ✅ | SocialDance | ✅ | |
| Fencing | ✅ | ✅ | Softball | ✅ | ✅ |
| Fishing | ✅ | Squash | ✅ | ✅ | |
| FitnessGaming | ✅ | StairClimbing | ✅ | ✅ | |
| Flexibility | ✅ | StairClimbingMachine | ✅ | ✅ | |
| FrisbeeDisc | ✅ | Stairs | ✅ | ||
| FunctionalStrengthTraining | ✅ | StepTraining | ✅ | ||
| Golf | ✅ | ✅ | StrengthTraining | ✅ | ✅ |
| GuidedBreathing | ✅ | ✅ | Stretching | ✅ | ✅ |
| Gymnastics | ✅ | ✅ | Surfing | ✅ | ✅ |
| HandCycling | ✅ | SurfingSports | ✅ | ||
| Handball | ✅ | ✅ | SwimBikeRun | ✅ | |
| HighIntensityIntervalTraining | ✅ | ✅ | Swimming | ✅ | |
| Hiking | ✅ | ✅ | SwimmingOpenWater | ✅ | |
| Hockey | ✅ | SwimmingPool | ✅ | ||
| Hunting | ✅ | TableTennis | ✅ | ✅ | |
| IceHockey | ✅ | ✅ | TaiChi | ✅ | |
| IceSkating | ✅ | ✅ | Tennis | ✅ | ✅ |
| JumpRope | ✅ | TrackAndField | ✅ | ||
| Kickboxing | ✅ | TraditionalStrengthTraining | ✅ | ||
| Lacrosse | ✅ | Transition | ✅ | ||
| UnderwaterDiving | ✅ | Volleyball | ✅ | ✅ | |
| Walking | ✅ | ✅ | WaterFitness | ✅ | |
| WaterPolo | ✅ | ✅ | WaterSports | ✅ | |
| Weightlifting | ✅ | ✅ | Wheelchair | ✅ | ✅ |
| WheelchairRunPace | ✅ | WheelchairWalkPace | ✅ | ||
| Wrestling | ✅ | Yoga | ✅ | ✅ |
Surfaced from shared tags and platforms — no rankings paid for.