HealthKit: Let’s Talk About Units

It’s coming on that time of year again, when people are starting to come up with their New Year’s Resolution, which usually involve some king of fitness goal. But 2015 will be extra special. It’s the first year of the Apple Watch! And Apple has decided that fitness is one of the biggest use-cases for their Watch. So much so, that they created a whole line of watches specifically for sports, which are tripped out with sensors that monitor heart-rate.

With WatchKit coming out this month (yay!), we as developers have the opportunity to to be the first ones to develop for this new device that is almost guaranteed to be a success. So while we wait for the WatchKit debut, I wanted to write a bit about HealthKit for those interested in making health-related apps for the upcoming Apple Watch.

So I’m sure all of you heard about HealthKit, but for those of you who are not clear about what exactly it is (it wasn’t clear to me until I looked into it), HealthKit is an interface for a global (aka Apple devices only of course) Health database. This means that any app can write and read to this database (given user permission of course). So your app can use nutrition data tracked by the Panera app for example.

panera

To use this database, we have to all have a common language to talk about things like units and quantities, which is this first blog post about HealthKit will be about…

HKUnit

HKUnit lets developers standardize how we talk about units. There are two ways to instantiate a unit:

let cmUnit = HKUnit(fromString: "cm")

Here is a chart of the appropriate combinations you can have for instantiating HKUnit fromString:

Note that if you instantiate the wrong unit from string, your app will crash! So if your units are unpredictable, consider using the second way:

let cmUnit = HKUnit.meterUnitWithMetricPrefix(.Centi)
let inchUnit = HKUnit.inchUnit()

Here is the documentation of all the other units you can instantiate officially:

So when should you use fromString vs the official unit? My answer is to always think of readability. For example, HKUnit(fromString: "cm") is a lot more readable at a glance than HKUnit.meterUnitWithMetricPrefix(.Centi). However, HKUnit.inchUnit() or HKUnit.ounceUnit() is really readable and easy to instantiate without having to remember exactly the right fromString combination.

HKQuantity

An HKQuantity is a standardized way to talk about an amount of a specific unit. So for example, we can talk about 120cm as follows:


let cmUnit = HKUnit(fromString: "cm")

let length = HKQuantity(unit: cmUnit, doubleValue: 120)

Unit Conversion

While HKQuantity is instantiated with a specific unit in mind, it is very easy to do unit conversion with it:

let cmUnit = HKUnit(fromString: "cm")
let length = HKQuantity(unit: cmUnit, doubleValue: 120)

let inchUnit = HKUnit.inchUnit()
let lengthInInches = length.doubleValueForUnit(inchUnit)
// 47.244094488189

However, keep in mind that not all units can be converted. Take for example a cm to gram conversion:

Screen Shot 2014-11-12 at 7.36.45 AM

As you can see, this will crash your app! So if you’re in a situation when you’re unsure whether a unit can be converted to the unit you need, it’s very easy to check for compatibility:

let cmUnit = HKUnit(fromString: "cm")
let length = HKQuantity(unit: cmUnit, doubleValue: 120)

let gramUnit = HKUnit.gramUnit()

if length.isCompatibleWithUnit(gramUnit) {
    let lengthInGrams = length.doubleValueForUnit(gramUnit)
    println("The length is \(lengthInGrams) grams")
} else {
    println("length cannot be converted to grams!")
}

// length cannot be converted to grams!

Localization

I think this is my favorite part of HealthKit as a developer. With only a few lines of code, your app can be easily understood by people from all over the world.

Apple has added three new formatters specifically for this purpose:

  • NSMassFormatter
  • NSEnergyFormatter
  • NSLengthFormatter

All of these formatters function the same way, so I’m just going to show off the NSMassFormatter as an example:

let massFormatter = NSMassFormatter()

// if the mass is in relation to a person,
// set this property to true.
// I'm not a unit conversion expert, but
// I think in some countries, when you talk about
// a person's weight vs an object, it's different
// (I heard Canada is an example - 
// see Reference #4: http://en.wikipedia.org/wiki/Weight#References)
massFormatter.forPersonMassUse = true

// You can set this to
// .Short, .Medium, or .Long
// I recommend playing with this in
// a Playground to see the difference
massFormatter.unitStyle = .Long

let localizedWeight = massFormatter.stringFromKilograms(100)
// 220.462 pounds (localized to the US)

Sometimes, you might want just the unit string without the number (for example, when the user is filling out a form to input the units).

let massFormatter = NSMassFormatter()
massFormatter.forPersonMassUse = true
massFormatter.unitStyle = .Long

var massFormatterUnit = NSMassFormatterUnit.Kilogram
massFormatter.unitStringFromKilograms(100, usedUnit: &massFormatterUnit)
// "pounds"

// you can also get the singular version:
massFormatter.unitStringFromKilograms(1, usedUnit: &massFormatterUnit)
// pound

// notice that the massFormatterUnit is now a Pound
massFormatterUnit == NSMassFormatterUnit.Pound // true

// once the user enters a number into the form, 
// you can get the correct string for the unit
massFormatter.stringFromValue(100, unit: massFormatterUnit)
// "100 pounds"

So there you have it, with only a few lines of code, you can now have a standardized way of thinking about units and quantities, with a full unit conversion and localization included!

Want to play and test out HealthKit units? Check out my HealthKit Playground on Github. Enjoy!

Enjoy the article? Join over 20,000+ Swift developers and enthusiasts who get my weekly updates.