Getting the Most Out of HealthKit

Session 209 WWDC 2016

HealthKit offers more than ever to developers joining in Apple's vibrant health and fitness ecosystem. Discover new ways to integrate the popular activity-tracking features of Apple Watch with your own app. Learn how to contribute to an even more complete picture of users' health data by reading and writing Health Documents from your organization. Review core concepts for working with HealthKit and explore best practices for managing data to ensure your users enjoy a seamless experience.

[ Music ]

[ Applause ]

Good morning, and welcome to Getting the Most Out of HealthKit.

I'm Matt, I'm a software engineer on the HealthKit team, and I'll be joined on stage shortly by my colleague Jeff.

In this session, we'll be going over some great new and recent additions to the HealthKit APIs in iOS X and watchOS 3.

Just as importantly, we'll be covering some key features, core concepts, and important workflows to make sure that you're really able to get the most out of HealthKit.

We expect you have some familiarity with HealthKit if you're here watching this session, but if you don't, we'll list some past sessions at the end of this talk that you can refer to later to get up to speed.

But for now, let's get started.

As all of you here know, Apple's health and fitness ecosystem has become a huge hit with our users.

People are getting more fit and more healthy because of the integration of HealthKit and now ResearchKit and CareKit with your apps and devices for iPhone and Apple Watch.

We want to make sure that you're able to keep creating these great health and fitness user experiences that our users have come to expect and enjoy.

So today we're going to be covering all the things you need to do to get these great experiences right.

First, we'll cover authorization, which underpins everything else you do with HealthKit.

Next, we'll cover the Activity Rings API introduced in iOS 9.3 and Health Records introduced this year in iOS X, both of which happen to have important implications related to authorization.

Finally, we'll spend the rest of the session going over a wide range of best practices for handling data when you're interacting with HealthKit.

So let's dive in.

First up is authorization.

If you've been using HealthKit for a while, some or even most of this section might feel like review, but we really recommend you pay important attention to the details because they'll be important for the stuff we cover later in this session.

We'll sprinkle in some best practices as well, so keep your ears peeled.

iOS gives users full control over their health data and which apps can access which parts of that data.

Before interacting with HealthKit, your app should request access to the appropriate types via HK Health Store, and then the Health Store in turn will present the appropriate authorization UI to the user if necessary.

Be aware that the user can change permissions for your app at any time, so you should keep that in mind while you're developing your app.

And just as importantly, read authorization and write authorization are completely independent.

That last bit can actually be a little tricky, so let's look at it in a little more detail.

Here's how read and write authorizations work.

If the user grants you both read and write permissions to a given HealthKit type, then your app can query and save data for that HealthKit type just like you'd expect.

If the user grants you only read permissions for a given type, then your app can read but not write HealthKit data for that type.

So far, so good.

Now, if the user grants you write permissions for a given type, then your app can write data to HealthKit for that type but not read it back from HealthKit for that type.

Write permissions do not imply read permissions.

However, there's an important exception to that point.

If your app has write permissions for a HealthKit type, then you can read back data that your app has written, just not data from other sources.

Finally, if the user denies you both read and write permissions for a given type, then you can neither query nor save data for that type.

And there's an important implication of that last point as well.

If the user denies you a previously granted write permission for that app, then that means your app can no longer read any data from HealthKit for that type, even data that your app previously wrote, so keep that in mind.

So that was all technically review, but there is one important change to authorizations in general in iOS X, and that has to do with usage descriptions.

Apps linked on or after iOS X must include a description for the user of why they're trying to access health data.

This reinforces our principle of user control over data.

These usage descriptions should be declared on your app's info.plist file as values for the NSHealthShareUsageDescription key if you're reading data or the NSHealthUpdateUsageDescription key if you're writing data.

As a refresher, this is how you request authorization.

The first thing we want to do is make sure that HealthKit is even available on the current device.

For instance, maybe this instance of our app is running on an iPad.

After we've established that, we list the types that we're interested in reading and writing.

And finally, we call request authorization on HK Health Store, pass in the types we're interested in, and then handle the response and the callback.

What if you have a watchOS app?

Authorizations are shared between your iOS app and its companion watchOS app, and you can request authorization at any time from your iOS code or your watchOS code.

However, the system authorization UI can only be presented to the user on the phone, so this has some important usability implications.

For instance, if the user is about to start a workout, they might already have their phone wrapped up in an armband, and if that's the case, they can't really easily approve an authorization request from your app, so this may not be the best time to request initial authorization.

But there's an even more important case.

If the user's using a Watch app, their phone may not be nearby at all, and if that's the case, the authorization sheet can't even be shown.

So we really recommend you consider these important cases when you're developing your Watch app.

Also remember that the response time for the request authorization call is not guaranteed, so definitely don't block any UI while you're waiting for a response.

So clearly it's important to get the authorization user experience right, and on top of that, your app may be requesting access to some or even many HealthKit data types, so that all raises a really important question.

When should I be requesting access to some or all of these types?

Here are our recommendations on that front.

First, we recommend that you request access to sensible groupings of types that correspond to logical activities in your application.

So for instance, say your app allows users to track both food intake as well as body measurements like BMI.

If that's the case, you might consider requesting access to nutrition types the first time the user tries to log food, but then requesting access to body measurement types whenever the user tries to log one of those.

One exception to that rule is if your app has an on-boarding flow.

If this is the case, it might actually make sense to request access to all the types your app wants to use upfront because you're already in a context where you can clearly explain to the user what your app is going to be doing with those types.

Regardless of which you choose, we definitely recommend that you frequently test authorization during development.

You can easily reset the initial authorization flow by deleting your app off of the device or out of the simulator before building and running again so that HealthKit presents the user with the initial authorization flow all over again.

When you're doing this, be sure to test cases where authorization is either delayed or completely denied by the user.

How does your app function in these cases?

What capabilities are left over?

Finally, if we could summarize authorization in one sentence, it's this.

Consider the user experience.

Don't present obstacles at inopportune times, and ensure that your flows make sense.

So we've spent a lot of time talking about authorization.

Now let's move onto some new features since last year in HealthKit, starting with Activity Rings.

Apple's developed a great health and fitness tracking experience for the Apple Watch.

Users love how easy it is to track key activity metrics and improve their day, and now in iOS X and watchOS 3, users can even share their Activity Rings with each other and compete.

Now we're giving you a great way to incorporate this Activity Ring experience right into your app with the Activity Rings API in iOS 9.3.

To do this, we start with an HKActivitySummary object.

HKActivitySummary represents the sum of a user's activity over the course of a given day.

That includes their move calories, exercise minutes, and stand hours, and their goals for each.

HKActivitySummary is a distinct type for authorization.

It's not an HKObject, but rather a special read-only type that you request authorization for distinctly from its component types.

That last part's really important.

HKActivitySummary covers some of the same HealthKit information as the HealthKit types; active energy, exercise minutes, and stand hours, but only on a daily aggregated basis.

So for instance, if you want to do something more specific like contribute to a user's Move ring by writing to the active energy type or shown finer grain statistics for activity over the course of a given day, in that case, you'd want to request access to the constituent types separately.

Now, because an ActivitySummary object represents activity over the course of a given calendar day, which may or may not correspond to a particular 24-hour period, we specify the day corresponding to an activity summary using a DateComponents object.

Let's see how that works in the following example.

So suppose we want to fetch the activity summary for today.

To do that, we use an HKActivitySummaryQuery.

First, we use our calendar to create a DateComponents object corresponding to today using the required components; era, year, month, and day.

Next, we use those components to create a predicate object that restricts our query to activity summaries whose day corresponds to today.

And then finally, we create our query, pass in the predicate, and then handle the, in this case, single activity summary that should come back in the response.

So that's how you retrieve activity summary data, but the really fun part is showing the rings themselves.

To do that, we use HKActivityRingView on iOS or the analogous WKInterfaceActivityRing on watchOS.

They look like this, and just like you'd expect, they perform this great animation when you call setActivitySummary, animated.

Some tips for using HKActivityRingView and WKInterfaceActivityRing.

Firstly, just like in the health and activity apps on iOS and watchOS, the rings look best on a black background, so we recommend display them similarly in your apps.

Secondly, if your app has sharing and communication features, you can use the writable properties of HKActivitySummary to construct your own object, supply it to HKActivityRingView or WKInterfaceActivityRing, and thereby display another user's rings alongside the current user's rings in your own app.

Finally, when you're using HKActivitySummaryQuery, remember to use the required DateComponents; era, year, month, and day in your HKActivitySummaryQuery.

Date map can be notoriously tricky, so if you have any questions about using Calendar or DateComponents, be sure to check out this great talk from a prior conference.

So we've been talking a lot about authorization and Activity Rings.

Let's put it into action with a quick demo.

Here on the right, we have an up and coming app for a medical group called LoopHealth.

This app has some other features, but its main page is a dashboard with some helpful information, so for instance, your doctor's name, upcoming appointments, and some healthy tips.

LoopHealth wants their patients to live healthier daily lives as well, so they saw this as a great opportunity to incorporate Apple's Activity Rings right onto their dashboard.

As you can see here, we've already dropped an HKActivityRingView into the storyboard for our application, but we haven't actually written the code that hooks it up with data yet.

Let's see how easy it is to do that.

So over here in Xcode, we have DashboardViewController.

This is the view controller we were just looking at in the LoopHealth app.

It's pretty empty so far, but we do have some helpful things filled in.

You can see right here we have an IBOutlet set up connected to the activityRingView that's already in our app.

Up here we import HealthKitUI.

This is the new framework that you can find HKActivityRingView in on iOS.

And finally down here, LoopHealth sets up its app-wide HKHealthStore in its app delegate, so we've just set up a simple computed property to retrieve it for convenience when we need it.

Okay, so if we want to show an activity summary in our app, the first thing we need to do is request read access to HKActivitySummary.

And since we're reading health data, that means we need to include a usage description, so let's go to our info.plist file and add a new key.

The key we're interested in is called NSHealthShare UsageDescription, which is written here in plain English as Privacy Health Share.

Perfect. And I'll drop in a quick usage description, and that's it.

We're all set.

Now I can go back to DashboardViewController and then write the code that actually requests authorization.

Since we want to show the initial prompt to the user and also update our rings whenever the user navigates to the dashboard tab, the perfect place to do that is in the viewDidAppear method.

So I'll drop that in here.

And after our requisite call to super, notice that we call RequestAuthorization on HKHealthStore, pass in the activity summary type, and then in the response, we call updateActivitySummary, which we'll write to actually fetch and update the data.

Let's go implement that now.

So here's our skeleton for updateActivitySummary, and what we want to do here is create an HKActivitySummaryQuery, request today's activity summary, and then set that activity summary on our HKActivityRingView once we get it back.

First, let's create a DateComponents object corresponding to today.

Since DateComponents only makes sense in the context of a particular calendar, we set the calendar object that we use back on the components object.

Oops, perfect.

Next, we can create our predicate using that components object, and once we have the predicate, we can create our query, pass in the predicate, and then in the response, we grab the single summary that should come back for today.

Now, once we have that summary, all we have to do is dispatch back to the main queue to update our UI and then call setActivitySummary, animated on our Activity Ring view.

Now that we have our query, all we have left to do is execute it, and that's it.

So let's build and run and see how this all looks.

Excellent.

So first thing you see is now that we're requesting access to activity summary, Health is asking the user to approve authorization.

Let's approve read access for our activity type, and while we're down there, notice that at the bottom of the screen, that usage description that we added for reading health data is included and shown to the user.

It's important to note that in a real app, we'd want to make sure this description is localized, so we'd include that in our info.plist.strings file instead.

I'll approve authorization here, and just like that, we see the Activity Rings animate beautifully into place.

[ Applause ]

So that's how easy it is to incorporate Activity Rings right into your own app.

Be sure to check out the API.

Next, I'll turn it over to my colleague Jeff who's going to tell you about an awesome new feature in iOS X.

[ Applause ]

Thanks, Matt.

Good morning, everyone.

My name is Joefrey Kibuule.

I work alongside Matt as an iOS software engineer on the Health team.

Today I have the proud privilege to introduce to you a new feature of iOS X, Health Records.

Health Records provides an easy and portable way to carry the information most personal to you right on your smartphone.

Today the current experience when users visit a medical professional and ask for their health records afterwards is this.

A stack of documents which may be cumbersome to find a particular piece of information.

More recently, health organizations have been providing their patients this.

CDs of digitized information which may be unintuitive to use.

But now with Health Records in iOS, we can help solve this problem.

Through the work that we've done in this release, your apps can start to unlock new possibilities in the exchange and interaction of Health Records.

In fact, in the U.S., adoption of these APIs can help health organizations comply with new regulations that require them to give their patients more control of their own health data.

So first, an overview.

Health Records in iOS is an umbrella term we use to represent a variety of different patient visits generated by health institutions.

Today we're adding support specifically for health documents.

Standard machine-readable XML that represents specific patient visits.

These include patient visit summaries, continuity of care visits, and operative notes, just to name a few.

We support the international HL-7 CDA standard for interoperability with a variety of different providers.

These documents are available through patient healthcare portals online and can be imported via Safari, Mail, and now all of your apps.

These documents are stored just like all other HealthKit data safely and securely encrypted on your iOS device.

Next, let's talk about permissions.

Since so much information is contained within each health document, we give the user additional controls compared to other data types in HealthKit.

Access is granted on a per-document basis in addition to the health document data type.

As shown on the right, we present the UI in order to allow the user to both view and select the document before granting your app access.

This UI will present it whenever you query for a document and a new one is available.

If you query for documents and none are, nothing has changed, we will not show this UI to the user and your query will return immediately with results.

If you query for documents while your application is in the background, we will never prompt the user UI to grant access to new documents.

HealthKit ensures that the user is always aware when they're granting access to documents to your apps the first time it occurs.

Next, let's talk about how to create a document in HealthKit.

When saving a document into HealthKit, you can save the raw XML into the new HKCDADocumentSample type.

We validate on creation to ensure compliance with the standard and will throw errors if this fails.

We automatically extract the title, patient, custodian, and author names whenever the document is saved into HealthKit in order to make querying for these fields faster without needing to read the entire document.

Let's take a look at this example in code.

We're going to take the documentData and transform it into a data object.

The origin of this XML will typically come from a health organization server.

We're then going to create a new HKCDADocumentSample passing in that data object, setting the appropriate dates, and any additional metadata, just like any other HKSample.

And then we'll save the document to the healthStore.

That's it.

Now your health document is saved into HealthKit ready to be used in other apps or viewed directly by the user in the Health App.

Now, let's talk about querying for documents in HealthKit.

Since HKCDADocumentSample is a subclass of HKSample, existing query objects you may be already familiar with continue to work just as you'd expect.

However, you need to use the new HKDocumentQuery in order to fetch the raw XML.

Fetching the raw XML's expensive, and we only do so when explicitly specified.

We provide predicate support in order to query for the automatically extracted fields, and then lastly, to remember that since HKDocumentSamples are immutable, updated information to previously samples are considered new samples.

Now, let's take a look at an example of how to query for documents in HealthKit.

In this example, we're going to query for all the documents a user has stored.

So first, we need to get the document type.

We're going to pass the CDA identifier into the document type forIdentifier method on HKObjectType.

We're then going to create an HKDocumentQuery.

You have additional fields in order to filter and order the documents received back to you the order that you'd like.

And then we execute the query in order to get the HKCDADocumentSamples back from HealthKit.

One thing I want to note in this particular example we set includeDocumentData to false.

Only ever set it to true if you need the full raw XML document data.

Now, let's talk about some best practices when dealing with the health documents in HealthKit.

First, check for validation errors whenever creating and HKCDADocumentSample.

The errors will tell you why we weren't able to transform your raw XML into a usable sample.

Next, you should verify by the Health App that you imported were correctly saved and automatically extracted fields are present.

This way, you can tell that queries based on those automatically extracted fields return the correct sample that you'd expect.

And lastly, request the raw XML data only when you need to.

Queries that don't request, including document data, will return the automatically extracted fields, and this may be all you need for you and your users to uniquely identify a document in HealthKit.

For more information on the HL-7 CDA Standard, visit the link on the screen.

Now, I'd like to switch gears and offer some general guidance on handling data.

As you know, HealthKit serves as a central repository where your app and other apps can help contribute to a user's record of health data.

Your app in cloud service may also have a direct connection with another app in cloud service, and this may require some special considerations.

So there are three main topics that I'd like to discuss when talking about handling data.

First, syncing data.

Second, tracking change data.

And third, migrating data.

So first, syncing data.

You should be using HKAnchoredObjectQuery in order to handle processing both new and deleted samples in order to keep up to date with HealthKit.

Anchors act as a bookmark to keep track of the last query operation you used to fetch data.

You could save this anchor for the next time you need to create a new HKAnchoredObjectQuery.

You'll open one query for each sample type that you're interested in and then pass an optional update handler in order to continuously process new and deleted samples without needing to unnecessarily requery HealthKit.

But say for a better user experience, in order to have fresh UI when your application is first launched, or to keep your cloud data in sync, your application needs to handle processing new and deleted samples even when it's currently not running.

That's where HKObserverQuery working with HKAnchoredObjectQuery comes into play.

Let's look at an example with a diagram.

So there are four main steps in order to handle background updates split into two phases; setup and execution.

In the first step, we're going to register for background updates.

You need to do this for every sample type that you're interested in.

In the second step, you're going to open an ObserverQuery.

Once set up, the ObserverQuery will monitor for both new and deleted samples in HealthKit.

When new samples are generated, that's when you have the third step.

You'll get a callback from the observer query and then execute an HKAnchoredObjectQuery in order to fetch new and deleted samples.

And then in the fourth step, you'll call the observer queries completion handler in order to let HealthKit know that you've processed and delivered the background update.

You'll then continue to cycle between steps three and four in order to keep up to date with HealthKit.

Now, let's take a look at this example one by one in code.

So in the first step, we need to register for background updates.

Your application needs to do this every time it's launched, so we recommend you do it in application didFinishLaunching WithOptions.

You'll then grab the step's quantity type from HKObjectType and then pass that to HK Health Store's enableBackgroundDelivery for, passing in the steps type and the frequency you'd like updates.

Do note that background delivery times are not guaranteed.

Your application needs to pick the longest possible frequency it can handle in order to preserve a user's device's battery life.

Also note that this API is iOS specific.

Background updates are not available on watchOS.

In the second step, we're going to pass the step's quantity type to create the HKAnchored, the HKObserverQuery.

Here we'll have a custom updateSteps method that we can use in order to know, to fetch new and deleted samples when HealthKit detects that.

And then we're going to execute the query.

That's it.

That completes the setup process in order for HealthKit to monitor new and deleted samples in HealthKit.

So as I'm walking across stage generating health samples, step samples, our, we're going to dive into the update steps method in order to know what we need to do in order to grab new and deleted samples.

So first, we're going to create an HKAnchoredObjectQuery, passing in the steps type.

You'll also have predicate, additional fields in order to filter the particular samples you'd like.

Then we'll call the handleSteps method in order to process new and deleted samples and then update our anchor for the next time we need to create an HKAnchoredObjectQuery.

We then call the completionHandler in order to know that we've done processing fetching new data, and then we execute the query.

Then lastly, in step four, we're going to call the completionHandler given to us from the observer query in order to let HealthKit know we've both received and processed the background update.

And that's it.

Now your application will have a fresh UI on launch and keep your cloud data in sync following all of these steps.

Next, let's talk about tracking change data.

You should be using UUIDs in order to keep tracking of unique HKObjects.

A unique identifier is set each time an object is created and persists for the lifetime of the sample.

Record UUIDs in your own data store or both locally on the device and remotely in the cloud so that way you could tell a particular sample is the same.

Whenever these samples are deleted, say, a workout from the Health App, you should be monitoring for these changes to make sure that these, those same samples are also deleted again locally on the device and remotely in the cloud.

And ensure that future sync operations don't re-add already deleted samples.

Now, there are two potential problems that I'd like to discuss when referring talking about how to avoid duplication.

The first is pre-populating data.

Pre-populating data is, I'm sorry, onboarding.

Pre-populating data is a great way during on-boarding to save the user time by pulling information that may already be stored in HealthKit.

Users have the ability to verify data that's already in HealthKit and change it if necessary.

However, the problem is saving unchanged values.

Be sure only to save data again if this is the user's intent.

Another additional potential problem may be ingesting data both from another app and HealthKit.

Remember to only pick one source of information that's most appropriate to your application.

HealthKit has a great privacy story that our users have already bought into.

However, you know what's, what source is best for your app.

Make sure not to save another application's data on their behalf.

Writing only your data once avoids duplicating data by simplifying which app's responsibility it is to write it.

There is one particular exception for this rule.

Sometimes duplication is intentional.

For example, if data's coming from multiple sources.

If a data, step data is generated both on a user's phone and his or her Apple Watch, you can use HKStatisticsQuery and HKStatisticsCollectionQuery in order to automatically de-duplicate data by the order of the preferred data sources that exist in the Health App.

This way, our users get a consistent experience of the view of their health data throughout our ecosystem.

Now, I want to talk about migrating data.

Let's say that you've launched a new Bluetooth thermometer and app that writes data into HealthKit.

Your application has been in the App Store for a few days, but your users discover a problem.

In certain locales, instead of saving 98 degrees Fahrenheit, you actually save 98 degrees Celsius.

That's a bit warm.

But in this case, we know exactly how we can migrate this data in order to fix it.

We first need to find old samples, write new samples, making sure to update UUID stored elsewhere, and then delete old samples.

Now, a few new things regarding the flow of data between iPhone and Apple Watch.

Starting in iOS 9.3, data originating on a user's phone will now sync back to all of their paired Apple Watches.

Apple Watch is now a reflection of the most recent health data stored within the HealthKit ecosystem.

In order to accomplish this, samples are now periodically pruned based off their end date on Apple Watch.

Make sure to save samples after HKHealthStore's earliest permitted sample date in order to make sure your samples are correctly saved and synced back to a user's device.

Lastly, sync times are not guaranteed.

You should be saving data on either iPhone or Apple Watch, not both.

Now, I'm going to hand it back off to Matt, who's going to wrap up the rest of our session.

Thank you, everyone, and have a great WWDC.

[ Applause ]

Thank you, Jeff.

Before we wrap up, I'd like to highlight one awesome additional feature that's brand new in iOS X and watchOS 3.

Wheelchair support.

Accessibility is extremely important to use here at Apple.

All of our and your users deserve to enjoy our products and experiences as much as possible, and the great activity tracking experience we brought to Apple Watch is no exception.

iOS X and watchOS 3 include great new motion-tracking features that automatically record data important to wheelchair users.

And now you can work with and contribute to those types when you're interacting with HealthKit.

First, there's a new characteristic data type, HKWheelchairUse that identifies whether the user uses a wheelchair.

The value can be yes, no, or indeterminate.

Next, we have some new quantity types specific to wheelchair users.

Those include wheelchair distance and push count, which you can think of like step count.

Finally, we have some new workout types important to wheelchair users as well.

Those include wheelchair walk pace and wheelchair run pace.

When a wheelchair user is using Apple Watch, the watch automatically records wheelchair pushes to the new push count data type.

In addition, the stand ring corresponding to the stand hours data type instead becomes roll hours.

Be aware that wheelchair distance is only automatically recorded during a wheelchair workout and also be aware that a user's wheelchair status can change over time.

This is really important if you're querying for historical data.

In this case, you want to make sure that you query for both wheelchair types and non-wheelchair types so that you're not potentially leaving out a big chunk of the user's historical information.

To sum up, we work really hard to make sure that all of our products and experiences are accessible to everyone.

You should too.

We strongly encourage you to reach this important segment of our users by supporting and contributing to wheelchair data types in your apps.

So we've talked about a lot today.

Let's recap.

Authorization is extremely important for protecting users' privacy, but getting the user experience right is absolutely key.

Keep authorization in mind when you're developing your app, and be sure to test it often.

Next, fit in with Apple's health and fitness ecosystem by incorporating the Activity Rings right into your app with the Activity Rings API.

Whenever you're interacting with HealthKit, take care to handle all cases where data is synchronized, deleted, or duplicated properly to ensure that your users' data is always precise and always what you'd expect.

And finally, don't forget to take advantage of the great, new features we've introduced this year in iOS X and watchOS 3, especially wheelchair support, something we think is extremely important.

If you'd like more information about any of the things we talked about today or if you have any questions, please visit this site.

We have lots of additional resources available.

And don't forget to check out our related sessions as well.

We also have these great sessions from previous years if you want to get up to speed on HealthKit.

Thank you for creating your great apps that help users live healthier lives and enjoy the rest of your WWDC.

[ Applause ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US