How many steps did I walk yesterday: using Cordova, HealthKit, and JavaScript, and a handful of Promises
As people who know me might know I have been playing around with various wearables for some years now, starting with a Nike Fuel Band, and also trying out products from Misfit and UP along the way. I have mostly just watched with interest as my exercise levels, and indeed sleep now we have a little one in the house, has gone up and down over time.
More recently with the arrival of HealthKit there comes a standard method of accessing this information from multiple products on the iPhone, I needed to brush up on my JavaScript for work so I decided to have a play in Cordova to build something in my spare time. One of the cool things about that Cordova community when compared to other app development frameworks is the large and healthy plugin community – and quick google and I found what I needed to support HealthKit HealthKit. This took only a small amount of patching from me to get at the information I wanted. In this blog I will just look at number of steps per day as a proxy for activity.
I am going to assume the rest of the Cordova App is in place and start with a simple wearable object which exposes a promise that allows you to check whether HealthKit is available on this platform, you might have iOS 8 but HealthKit is iPhone only. Remember that most of Cordova is interacting with a bunch of asynchronous native systems so you have to make sure you consider this as you work with it. Hence the liberal use of promises. (If you are wanting to support < iOS 8 then you will need to use a different promise library / shim) In this case I am also using the
underscorejs library to create a promise who’s result won’t be calculated until it is invoked for the first time.
var wearable = { avaliable: _.once(function() { return new Promise(function(resolve, reject) { if (window.plugins.healthkit) { window.plugins.healthkit.available( function() { console.log('Healthkit is avaliable'); resolve(true); }, function() { console.log('Healthkit is not avaliable'); reject(Error(false)); } ); } else { reject(Error('HealthKit Not Available')); } }); }), ... }
The next stage of the code, once you are sure that the system is available, is to get verify that you can access the data you want to access. Now Apple is clear in there developer guide that you should only request the data you might want to access once. So no asking for step data one time then heath rate data in a different part of the UI. The Cordova HealthKit Plugin will just return with a success if those values are already authorised, so we will use another promise as a gateway to actually working with the data to make one consistent entry point for authorisation.
var wearable = { ... getHealthKit : _.once(function() { return wearable.avaliable().then(function() { return new Promise(function(resolve, reject) { window.plugins.healthkit.requestAuthorization( { 'readTypes': ['HKQuantityTypeIdentifierStepCount'], 'writeTypes': [] }, function() { console.log('HealthKit authorisation accepted'); resolve(window.plugins.healthkit); }, function() { reject(Error('HealthKit authorisation rejected')); }); }); }); }), ... }
Finally now we have all the ground work in place, lets put in place a simple method to work out how many steps were recorded yesterday. I am then using special function called sumQuantityType that performs some rather clever statistical leg work for you. Consider if you have a iPhone 6 along with step monitoring wrist band of some kind like a Apple Watch, in HealthKit you will have two lots of data that inconsistently overlaps. HealthKit provides methods to deal with this, more information can be found in the WWDC presentation from 2014, but basically it works out for each time segment which data source(s) to use to get the value for the time range requested.
var wearable = { ... querySteps : function() { return wearable.getHealthKit().then(function (healthkit) { return new Promise(function(resolve, reject) { var m = moment().startOf('day'); var endDate = m.toDate(); var startDate = moment(m).subtract('d', 1).toDate(); healthkit.sumQuantityType({ 'startDate': startDate, 'endDate': endDate, 'sampleType': 'HKQuantityTypeIdentifierStepCount' }, function(value) { resolve(value) }, function() { reject(Error('Problem queuing steps')); }); }); } }
Now we have this object, we can access the data and show it to the user. Note that the promise objects chain so the catch function will tell you if something went wrong at any stage of the process, if you had tried to wire this up with event handlers you would have had an ungodly mess of code and callbacks.
wearable.querySteps() .then(function(value){ console.log('Number of steps ' + value); ... update UI }) .catch(function (error) { ... update UI };
This is of course just the start, there is a lot more interesting stuff you can get from this API; but that is for another day. I like HealthKit because unlike GoogleFit all your health data is stored locally by default. There is a lot of potential here for doing interesting stuff.
Finally a quick shout out of Eddy Verbruggen who created HealthKit plugin along with a whole host of other interesting libraries.
I am going to walk around for a bit now, gotta meet those targets. :-)
Reference: | How many steps did I walk yesterday: using Cordova, HealthKit, and JavaScript, and a handful of Promises from our WCG partner Gerard Davison at the Gerard Davison’s blog blog. |