A plugin that abstracts fitness and health repositories like Apple HealthKit or Google Health Connect.
This work is based on cordova healthkit plugin. This plugin is kept up to date and requires a recent version of cordova (12 and on) as well as recent iOS and Android SDKs. For bugs and improvements use the issues tracker. For general question or small issue, please use the gitter channel.
This is a complete rewrite of the Android version of the plugin to support the new HealthConnect API. Google Fit APIs are deprecated and will be made obsolete in 2024.
Google Fit is no longer supported by this plugin. If, for any masochistic reason, you want to use Google Fit, you need to use an older version of this pluign (2.1.1).
Please be remindful that THIS IS STILL A WORK-IN-PROGRESS. While all functionalities listed here are implemented and working, there are several data types that were supported in older versions and are not supported in this version YET. If you need support for a given data type, please check if it is already implemented in iOS, then either add support in Java and send a pull request, or add an issue and I will prioritize it.
In Cordova:
cordova plugin add cordova-plugin-health --variable HEALTH_READ_PERMISSION='App needs read access' --variable HEALTH_WRITE_PERMISSION='App needs write access'
HEALTH_READ_PERMISSION
and HEALTH_WRITE_PERMISSION
are shown when the app tries to grant access to data in HealthKit.
NSHealthShareUsageDescription
and NSHealthUpdateUsageDescription
. These are assigned with a default string by the plugin, but you may want to contextualise them for your app.<platform name="android">
...
<preference name="GradleVersion" value="8.4" />
<preference name="AndroidGradlePluginVersion" value="8.1.1" />
<preference name="android-minSdkVersion" value="26" />
<preference name="android-targetSdkVersion" value="34" />
...
<platform>
Additionally, there are issues with some kotlin depenendencies which are fixed automatically by the plugin in src/android/build-extras.gradle
. All these hacks will hopefully be removed with future versions of the cordova-android platform.
Health Connect requires that each data type accessed is listed as permission in the AndroidManifest.xml file. This plugin will NOT ADD PERMISSIONS for the data types that you need, the list is too long to add them all and having all permissions listed may be problematic when submitting to the Play Store. You need to modify the AndroidManifest.xml file and add the needed permissions manually. See this to understand which permissions you need, depending on the data types that you actually want to access. The best way to add permissions is to include them in the config.xml file of your cordova project so that the build process is reproducible and the permissions are not lost when removing and re-adding the Android platform. Example:
<platform name="android">
...
<config-file target="AndroidManifest.xml" parent="/*" xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.health.READ_STEPS" />
<uses-permission android:name="android.permission.health.WRITE_STEPS" />
<uses-permission android:name="android.permission.health.READ_TOTAL_CALORIES_BURNED" />
...
</config-file>
...
</platform>
A Privacy Policy must be present on Android in order for the app to be approved for distribution. The plugin includes a simple webview, with no JS activated, to show the Privacy Policy when requested. The Privacy Policy must be formatted as an HTML page (no JS) and placed as a file with name: privacypolicy.html
under the www
folder of the project (in other words, the webview loads the following URL: file:///android_asset/www/privacypolicy.html
). It is possible to change that URL by changing the value inside the file platforms/android/app/src/main/res/values/CordovaPluginHealthStrings.xml
. WARNING: I suspect that this file may get reset sometimes to its original value. There should be a way to modify it from config.xml using edit-config but I was not able to make it work. Help is welcome.
Capacitor does not automatically include all changes to AndroidManifest.xml or gradle files from plugin.xml. This is a short guide to do it manually. Based on plugin v3.1.0 and @capacitor/android v5.5.1, future versions may be different.
install the plugin from npm (npm install cordova-plugin-health
), DO NOT USE awesome-codova-plugin, it hasn't been updated yet. Build the app as you would normally do (npm run build
), sync it with the android code (npx cap sync
) and get the Android project in Android Studio (npx cap open android
) or another editor.
add the Privacy Policy activity to AndroidManifest.xml, inside <application></application>
:
<!-- For supported versions through Android 13, create an activity to show the rationale
of Health Connect permissions once users click the privacy policy link. -->
<activity
android:name="org.apache.cordova.health.PermissionsRationaleActivity"
android:exported="true">
<intent-filter>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
</activity>
<!-- For versions starting Android 14, create an activity alias to show the rationale
of Health Connect permissions once users click the privacy policy link. -->
<activity-alias
android:name="ViewPermissionUsageActivity"
android:exported="true"
android:targetActivity="org.apache.cordova.health.PermissionsRationaleActivity"
android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
<intent-filter>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
</intent-filter>
</activity-alias>
add the possibility to query for the presence of Health Connect to AndroidManifest.xml, inside the root tag:
<!-- Check if Health Connect is installed -->
<queries>
<package android:name="com.google.android.apps.healthdata" />
</queries>
add permissions to AndroidManifest.xml , inside the root tag. This depends on the actual data types you want to access. See this for a list.
<uses-permission android:name="android.permission.health.READ_STEPS" />
<uses-permission android:name="android.permission.health.WRITE_STEPS" />
modify the main build.gradle file and update:
classpath 'com.android.tools.build:gradle:8.1.1'
modify the variables.gradle file, particularly:
minSdkVersion = 26
targetSdkVersion = 34
compileSdkVersion = 34
These are currently supported in both Android and iOS. Please notice that older versions of this plugin included more data types, but with Google Fit, not Health Connect. Support for previously supported data types has not been removed on iOS, it's simply not listed here. The plan is to complete the porting of all previously supported data types from Google Fit to Health Connect, just be patient, or give us a hand.
Data type | Unit | HealthKit equivalent | Health Connect equivalent |
---|---|---|---|
gender | HKCharacteristicTypeIdentifierBiologicalSex | NA | |
date_of_birth | HKCharacteristicTypeIdentifierDateOfBirth | NA | |
weight | kg | HKQuantityTypeIdentifierBodyMass | Weight |
height | m | HKQuantityTypeIdentifierHeight | HeightRecord |
bmi | count | HKQuantityTypeIdentifierBodyMassIndex | NA |
fat_percentage | % | HKQuantityTypeIdentifierBodyFatPercentage | BodyFatRecord |
steps | count | HKQuantityTypeIdentifierStepCount | StepsRecord |
distance | m | HKQuantityTypeIdentifierDistanceWalkingRunning + HKQuantityTypeIdentifierDistanceCycling | DistanceRecord |
activity | activityType | HKWorkoutTypeIdentifier | ExerciseSessionRecord |
appleExerciseTime | min | HKQuantityTypeIdentifierAppleExerciseTime | NA |
sleep | sleep | HKCategoryTypeIdentifierSleepAnalysis | SleepSessionRecord |
calories.active | kcal | HKQuantityTypeIdentifierActiveEnergyBurned | ActiveCaloriesBurnedRecord |
calories.basal | kcal | HKQuantityTypeIdentifierBasalEnergyBurned | BasalMetabolicRateRecord * time window |
calories | kcal | HKQuantityTypeIdentifierActiveEnergyBurned + HKQuantityTypeIdentifierBasalEnergyBurned | TotalCaloriesBurnedRecord |
heart_rate | bpm | HKQuantityTypeIdentifierHeartRate | HeartRateRecord |
blood_glucose | mmol/L | HKQuantityTypeIdentifierBloodGlucose | BloodGlucoseRecord |
mindfulness | sec | HKCategoryTypeIdentifierMindfulSession | NA |
UVexposure | count | HKQuantityTypeIdentifierUVExposure | NA |
Note: units of measurement are fixed!
Returned objects contain a set of fixed fields:
Example values:
Data type | Value |
---|---|
gender | "male" Notes: only available on iOS |
date_of_birth | { day: 3, month: 12, year: 1978 } Notes: currently only available on iOS |
weight | 83.3 |
height | 1.72 |
bmi | 25 Notes: only available on iOS |
fat_percentage | 0.312 |
steps | 34 |
distance | 101.2 |
activity | "walking" Notes: recognized activities and their mappings in Health Connect / HealthKit can be found here. Additional calories (in kcal) and distance (in m) can be added if the query has the includeCalories and/or includeDistance flags set. Warning If you want to fetch calories and/or distance, permission to access those quantities should be requested. |
appleExerciseTime | 24 Notes: only available on iOS |
sleep | 'sleep.light' Notes: recognized sleep stages and their mappings in HealthConnect / HealthKit can be found here in Android it is also possible to retrieve an entire session, in which case the value is an array of sleep stages [ { startDate: Date, endDate: Date, stage: 'sleep.light' }, ...] |
calories.X | 245.3 |
heart_rate | 66 |
blood_glucose | { glucose: 5.5, meal: 'breakfast', sleep: 'fully_awake', source: 'capillaryblood' } Notes: to convert to mg/dL, multiply by 18.01559 (The molar mass of glucose is 180.1559). meal can be: 'before' / 'after' / 'fasting' (Android only) + 'meal' (iOS only) / 'breakfast' / 'dinner' / 'lunch' / 'snack' / 'unknown'. sleep can be (iOS only): 'fully_awake', 'before_sleep', 'on_waking', 'during_sleep'. source can be: 'capillary_blood' ,'interstitial_fluid', 'plasma', 'serum', 'tears', whole_blood', 'unknown' |
mindfulness | 1800 Notes: only available on iOS |
UVexposure | 12 Notes: only available on iOS |
Tells if either HealthKit of Health Connect are available.
cordova.plugins.health.isAvailable(successCallback, errorCallback)
Android only. Launches the PlayStore to retrieve HealthConnect. From Android 14 and up this is not needed, becuase HealthConnect becomes part of the operating system.
cordova.plugins.health.getHealthConnectFromStore(successCallback, errorCallback)
Launches the Privacy Policy screen needed by Health Connect. Use it for testing how it appears.
cordova.plugins.health.launchPrivacyPolicy(successCallback, errorCallback)
Opens the health app (HealthConnect on Android, Health app on iOS) so that the user can review permissions and settings.
cordova.plugins.health.openHealthSettings(successCallback, errorCallback)
Requests read and/or write access to a set of data types. It is recommendable to always explain why the app needs access to the data before asking the user to authorize it.
If the user has already granted permissions to the app, this function will not prompt the user again, but will call the callback immediately.
cordova.plugins.requestAuthorization(datatypes, successCallback, errorCallback)
{
read : ['steps'], // Read permission
write : ['steps', 'weight'] // Write permission
}
Check if the app has authorization to read/write a set of datatypes.
cordova.plugins.health.isAuthorized(datatypes, successCallback, errorCallback)
{
read : ['steps'], // Read permission
write : ['steps', 'weight'] // Write permission
}
Gets all the data points of a certain data type within a certain time window.
Warning: if the time span is big, it can generate long arrays!
cordova.plugins.health.query({
startDate: new Date(new Date().getTime() - 3 * 24 * 60 * 60 * 1000), // three days ago
endDate: new Date(), // now
dataType: 'steps',
limit: 1000,
ascending: true,
}, successCallback, errorCallback)
sleepSession: true
. The returned value will be an array of objects like: [ { startDate: Date, endDate: Date, stage: 'sleep.light' }, ... ]
Gets aggregated data in a certain time window. Usually the sum is returned for the given quantity.
cordova.plugins.health.queryAggregated({
startDate: new Date(new Date().getTime() - 3 * 24 * 60 * 60 * 1000), // three days ago
endDate: new Date(), // now
dataType: 'steps',
bucket: 'day'
}, successCallback, errorCallback)
Not all data types are supported for aggregated queries. The following table shows what types are supported and examples of the returned object:
Data type | Example of returned object |
---|---|
height | { startDate: Date, endDate: Date, value: { average: 1.8, min:1.7, max: 1.8 }, unit: 'count' } Note: Android only |
weight | { startDate: Date, endDate: Date, value: { average: 73, min:72.5, max: 74 }, unit: 'kg' } Note: Android only |
steps | { startDate: Date, endDate: Date, value: 5780, unit: 'count' } |
distance | { startDate: Date, endDate: Date, value: 12500.0, unit: 'm' } |
calories | { startDate: Date, endDate: Date, value: 2892.4, unit: 'kcal' } |
calories.active | { startDate: Date, endDate: Date, value: 25698.4, unit: 'kcal' } |
calories.basal | { startDate: Date, endDate: Date, value: 3547.3, unit: 'kcal' } |
activity | Android: { startDate: Date, endDate: Date, value: 567000, unit: 'ms' } iOS: { startDate: Date, endDate: Date, value: { still: { duration: 520000 }, walking: { duration: 223000 }}, unit: 'activitySummary' } Note: durations are expressed in milliseconds |
sleep | { startDate: Date, endDate: Date, value: 493, unit: 's' } Notes: Android iOS |
appleExerciseTime | { startDate: Date, endDate: Date, value: 500, unit: 'min' } Notes: iOS only |
heart_rate | { startDate: Date, endDate: Date, value: { average: 72, min: 68, max: 82 }, unit: 'bpm' } |
Stores a data point.
cordova.plugins.health.store({
startDate: new Date(new Date().getTime() - 3 * 60 * 1000), // three minutes ago
endDate: new Date(),
dataType: 'steps',
value: 180,
}, successCallback, errorCallback)
cycling: true
.dataType: 'activity', value: 'walking', calories: 20, distance: 520
. Distance is set as DistanceWalkingRunning unless an additional cycling: true
is added to the object. Be aware that you need permission to write calories and distance first, or the call will fail.cycling: true
.sleepSession: true
and use an array of objects like [ { startDate: Date, endDate: Date, stage: 'sleep.light' }, ... ]
as value.Deletes data points. You can either delete a single data point (using its id, Android only), or a set of datapoints within a time range.
cordova.plugins.health.delete({
startDate: new Date(new Date().getTime() - 3 * 60 * 1000), // three minutes ago
endDate: new Date(),
dataType: 'steps'
}, successCallback, errorCallback)
or (Android only):
cordova.plugins.health.delete({
id: '812n12123nd23edj3234'
dataType: 'steps'
}, successCallback, errorCallback)
cycling: true
.unit
attribute, you can find the comprehensive list of possible units from the Apple Developers documentation.Any help is more than welcome! Raise issues, send Pull requests. I usually reply quickly. I know that this plugin is used in production in some commercial apps (includine mine), so I take it seriously.
I don't know Objective C and I am not interested in learning it now, so I would particularly appreciate someone who could give me a hand with the iOS part. Also, I would love to know from you if the plugin is currently used in any app actually available online. Paid consultancy is also possible if you need to speed up some part of the plugin, or other stuff. Just send me an email to my_username at gmail.com.
Thanks!