Skip to content
This repository has been archived by the owner on Mar 11, 2024. It is now read-only.

thrivehealth/capacitor-healthkit

 
 

Repository files navigation

Capacitor HealthKit Plugin

❤️ Capacitor plugin to retrieve data from HealthKit ❤️

Disclaimer : for now only some of the HK data base, in the future the retrieve base will be bigger !

Getting Started

Prerequisites

  • Add HealthKit to your Xcode project (section signing & capabilities)

alt text

  • ADD Privacy - Health Share Usage Description to your Xcode project
  • ADD Privacy - Health Update Usage Description to your Xcode project

alt text

Installing

Do

npm i --save capacitor-healthkit

Then

npx cap update

And if you use Ionic or Angular, here a example setup:

in your .ts file add this:

import { Plugins, PluginResultError } from '@capacitor/core';
const { CapacitorHealthkit } = Plugins;

and then you can create async functions for example like this:

async queryHKitSampleType(sampleName: string) {
  // sample name, start date (string), end Date (date), limit (0 to infinite)
  // let start = "2019/07/01" // YY/MM/DD
  this.dataName = sampleName;
  const endDate = new Date();
  this.data = await CapacitorHealthkit.queryHKitSampleType({
    sampleName: sampleName,
    startDate: "2019/07/01",
    endDate: endDate,
    limit: 0
  });
}

so you can use the plugin for example with the call CapacitorHealthkit.queryHKitSampleType(...

And you're all set ! 👍


Methods

isAvailable()

Tells if HealthKit is available on the device.

isAvailable(successCallback, errorCallback)
  • successCallback: {type: function(available)}, if available a true is passed as argument, false otherwise
  • errorCallback: {type: function(err), called if something went wrong, err contains a textual description of the problem

isEditionAuthorized()

Tells if HealthKit is authorized for edit data.

isEditionAuthorized(sampleName, successCallback, errorCallback)
  • sampleName: {type: String}, a string of sampleName Example:
{
    sampleName : "stepCount"
}
  • successCallback: {type: function(available)}, if available a true is passed as argument, false otherwise
  • errorCallback: {type: function(err), called if something went wrong, err contains a textual description of the problem

Sample name available for this query:

Sample name for query Request Auth Needed HealthKit equivalent
stepCount steps HKQuantityTypeIdentifierStepCount
flightsClimbed stairs HKQuantityTypeIdentifierFlightsClimbed
distanceWalkingRunning distance HKQuantityTypeIdentifierDistanceWalkingRunning
distanceCycling distance HKQuantityTypeIdentifierDistanceCycling
appleExerciseTime appleExerciseTime HKQuantityTypeIdentifierAppleExerciseTime
activeEnergyBurned calories HKQuantityTypeIdentifierActiveEnergyBurned
basalEnergyBurned calories HKQuantityTypeIdentifierBasalEnergyBurned
sleepAnalysis activity HKCategoryTypeIdentifierSleepAnalysis
workoutType activity HKWorkoutTypeIdentifier

multipleIsEditionAuthorized()

Tells if HealthKit is authorized for edit data (you can ask in this method for multiple sampleName)

isEditionAuthorized(sampleNames, successCallback, errorCallback)
  • sampleNames: {type: [String]}, a list of strings of sampleNames Example:
{
    sampleNames : ["stepCount", "flightsClimbed"]
}
  • successCallback: {type: function(available)}, if available a true is passed as argument, false otherwise
  • errorCallback: {type: function(err), called if something went wrong, err contains a textual description of the problem

Same sample names available as isEditionAuthorized()


requestAuthorization()

Requests read and 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.

Important: this method must be called before using the query and store methods, even if the authorization has already been given at some point in the past. Failure to do so may cause your app to crash... 😬

requestAuthorization(datatypes, successCallback, errorCallback)
  • datatypes: {type: Mixed array}, a list of data types you want to be granted access to. You can also specify read or write only permissions.
{
    all : [""] // Read & Write permission
    read : ["steps"], // Read only permission
    write : ["height", "weight"] // Write only permission
}

Example:

let result = await CapacitorHealthkit.requestAuthorization({
      all: ["calories", "stairs", "activity"], // ask for Read & Write permission
      read: ["steps", "distance", "duration"], // ask for Read Only permission
      write: [""] // ask for Write Only permission
    });
  • successCallback: {type: function}, called if all OK
  • errorCallback: {type: function(err)}, called if something went wrong, err contains a textual description of the problem

The datatype activity also includes sleep. If you want to get authorization only for workouts, you can specify workouts as datatype, but be aware that this is only availabe in iOS.

Data type for requestAuthorization:

Data type Unit What you actually ask from HealthKit with this data type
steps count HKQuantityTypeIdentifierStepCount
stairs count HKQuantityTypeIdentifierFlightsClimbed
distance m HKQuantityTypeIdentifierDistanceWalkingRunning + HKQuantityTypeIdentifierDistanceCycling
duration min HKQuantityTypeIdentifierAppleExerciseTime
calories kcal HKQuantityTypeIdentifierActiveEnergyBurned + HKQuantityTypeIdentifierBasalEnergyBurned
activity activityType HKWorkoutTypeIdentifier + HKCategoryTypeIdentifierSleepAnalysis

"duration" should be ask for read only property


queryHKitSampleType()

Please make sure that you called correct requestAuthorization for HealthKit sample you want before use this method :shipit:

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! 😰

queryHKitSampleType(queryOptions, successCallback, errorCallback)

queryOptions example:

const endDate = new Date();
queryOptions = {
      sampleName: "stepCount", // String
      startDate: "2019/07/01", // String
      endDate: endDate, // Date
      limit: 0 // Int
};

Sample name available for queries:

Sample name for query Request Auth Needed HealthKit equivalent
stepCount steps HKQuantityTypeIdentifierStepCount
flightsClimbed stairs HKQuantityTypeIdentifierFlightsClimbed
distanceWalkingRunning distance HKQuantityTypeIdentifierDistanceWalkingRunning
distanceCycling distance HKQuantityTypeIdentifierDistanceCycling
appleExerciseTime appleExerciseTime HKQuantityTypeIdentifierAppleExerciseTime
activeEnergyBurned calories HKQuantityTypeIdentifierActiveEnergyBurned
basalEnergyBurned calories HKQuantityTypeIdentifierBasalEnergyBurned
sleepAnalysis activity HKCategoryTypeIdentifierSleepAnalysis
workoutType activity HKWorkoutTypeIdentifier

Example function in Angular:

async queryHKitSampleType(sampleName: string) {
    // sample name, start date (string), end Date (date), limit (0 to infinite)
    // let start = "2019/07/01" // YY/MM/DD
    this.dataName = sampleName;
    const endDate = new Date();
    this.data = await CapacitorHealthkit.queryHKitSampleType({
      sampleName: sampleName,
      startDate: "2019/07/01",
      endDate: endDate,
      limit: 0
    });
  }
  • startDate: {type: Date}, start date from which to get data

  • endDate: {type: Date}, end data to which to get the data

  • sampleName: {type: String}, the data type to be queried (see above)

  • limit: {type: integer}, optional, sets a maximum number of returned values

  • successCallback: {type: function(data) }, called if all OK, data contains the result of the query in the form of an array (detail below)

  • errorCallback: {type: function(err)}, called if something went wrong, err contains a textual description of the problem

iOS quirks:

  • Limit is set to unlimited by default (if you insert 0)
  • Datapoints are ordered in an descending fashion (from newer to older). You can revert this behaviour by adding ascending: true to your query object.
  • HealthKit does not calculate active and basal calories - these must be inputted from an app
  • HealthKit does not detect specific activities - these must be inputted from an app
  • When querying for activities, only events whose startDate and endDate are both in the query range will be returned.

Result of query (array):

{
    "countReturn": result.count, // number of results
    "resultDate": output // output data in result of query
}

countReturn returns the number of results

output contain a set of fixed fields:

  • startDate: {type: Date} a date indicating when the data point starts
  • endDate: {type: Date} a date indicating when the data point ends
  • sourceBundleId: {type: String} the identifier of the app that produced the data
  • source: {type: String} the name of the app that produced the data (as it appears to the user)
  • unit: {type: String} the unit of measurement
  • value: the actual value
  • uuid: (string) the unique identifier of that measurement

output content:

  • If quantity type output contains:
- uuid (string)
- value (double)
- unitName (string)
- startDate (ISO8601 String)
- endDate (ISO8601 String)
- duration (double)
- source (string)
- sourceBundleId (string)
  • If Workout type output contains :
- uuid (string)
- startDate (ISO8601 String)
- endDate (ISO8601 String)
- duration (double)
- source (string)
- sourceBundleId (string)
- workoutActivityId (Int)
- totalEnergyBurned (kilocalorie)
- totalDistance (meter)
- totalFlightsClimbed (count)
- totalSwimmingStrokeCount (count)

If data = -1 => no data collected

  • If Sleep type output contains :
- uuid (string)
- startDate (ISO8601 String)
- endDate (ISO8601 String)
- duration (double)
- source (string)
- sourceBundleId (string)

multipleQueryHKitSampleType()

Please make sure that you called correct requestAuthorization for HealthKit sample you want before use this method :shipit:

Gets all the data points of multiple data types within a certain time window.

Exact same query as queryHKitSampleType() but return a large object with all queries in one.

Warning: if the time span is big, it can generate long arrays! 😰

multipleQueryHKitSampleType(queryOptions, successCallback, errorCallback)

queryOptions example:

const endDate = new Date();
queryOptions = {
      sampleNames: ["stepCount", "workoutType"], // String
      startDate: startDate, // Date
      endDate: endDate, // Date
      limit: 0 // Int
};

result will look like : [String: [String: Any]] First string is sample name, then [String: Any] is exactly like return of queryHKitSampleType() method

Questions ?

Please contact me (Theo) 💬

Whats coming soon ?

  • Add more data compatibility (nutrition etc)
  • Get aggregated data from HK
  • Store data in HK
  • Delete data in HK

Built With

  • Capacitor
  • VSCode
  • XCode

Contributing

Theo Creach for Ad Scientiam

Acknowledgements : Timothée Bilodeau - 😎 iOS Developer - Linkedin

Versioning

Version 0.1.3

Authors

  • Theo Creach - ⭐ Developer - Twitter

License

This project is licensed under the MIT License

Packages

No packages published

Languages

  • Swift 86.1%
  • TypeScript 6.2%
  • Objective-C 3.7%
  • Ruby 3.1%
  • JavaScript 0.9%