HealthKit helps you build world-class health and fitness apps by centralizing health data from third-party apps, iPhone, Apple Watch, and external health devices. Discover how you can manage authorization and privacy around Health data, read and write data to the shared Health Store, and use HealthKit's built-in queries to fetch data and calculate statistics for that data.
Hello and welcome to WWDC. Hi everyone. My name is Dina, and I'm an iOS engineer here at Apple.
Today I'm going to introduce you to HealthKit and get you started building your first health application. Our own health data makes up some of the most personal parts of who we are, whether it's tracking menstrual cycles and fertility to get pregnant or my lab test result from my doctor's appointment...
and even the workout I completed this morning on my Apple Watch. Ready access to our own health data is one of the most empowering developments of modern technology.
Today there are over 18,000 applications on the App Store that all focus on improving our health and fitness. That's mind-blowing. Like seriously, it's mind-blowing. There are applications today that help us reach our fitness goals at home by providing at-home workouts, help us track our menstrual cycles and fertility and even log the food that we eat to help us reach or goal weight. There's a whole diversity of different applications available today that help us take control over our health. All of these applications are built on the foundation of HealthKit. HealthKit is a framework that creates a central repository of all the user's health data, allowing applications to read data from it and also contribute data to it, to help create the most seamless user experience. We can interact with our health data on multiple devices including our iPhone, Apple Watch, even iCloud. HealthKit securely stores and synchronizes all of our health data across these devices so you as a developer don't have to worry about handling that.
HealthKit also guards users' privacy by giving us explicit control over what data applications have access to. HealthKit is a framework that helps create a more seamless health application while also protecting our health data.
So today we're going to join this healthy community of ours by building an application of our own with HealthKit called SmoothWalker, and just like its name, the goal of this application is to allow us to record the walks that we've been on and visually see the daily steps we've taken over the past week.
To build SmoothWalker, we'll need to accomplish three different tasks. The first one being setting up HealthKit in our xCode project. Once we do, we'll be able to start saving data - specifically, the distances we've walked.
And finally, we can read data to display our daily step count over the past week in our application. To set up HealthKit, we'll need to accomplish several different tasks: the first one being, we'll need to enable the HealthKit capability in our Xcode project. Once we do, we'll need to check whether or not the platform we're running on supports HealthKit. And finally, we'll create an HKHealthStore, the entry point to the HealthKit API. To enable the HealthKit capability in your application you'll select the target that you want to integrate users' health data into. You'll tap "Signing & Capabilities" and hit that "+ Capability" button. You'll type in "HealthKit" and right then and there you'll add the HealthKit framework into your application, which will allow you to retrieve and store users' health information. We'll now need to check whether or not the platform we're running on supports HealthKit.
It's really common for developers to have one application and have it run on multiple platforms or operating systems, so we'll need to check whether the platform we're running on right now supports HealthKit. You'll need to import HealthKit, and then using HKHealthStore, you'll check whether or not health data is available. If it is, you'll continue being able to integrate users' health data into your application. If not, then make sure that your application is still able to operate without having users' health data.
Finally we'll create an HKHealthStore. An HKHealthStore is what I like to think of as the gateway or portal to the world of health data.
It's what you'll use when you want to request access to users' health data.
It's how you'll save data and even read data using queries. You only need to create one instance and reuse it across the lifecycle of your application. There's no need to create multiple instances. Here, we have our HealthStore object, the one that we'll be using across the entirety of our application, and if health data is available, we'll instantiate our HealthStore object.
And setting up HealthKit is that easy. Enabling the HealthKit capability, checking whether or not our platform supports HealthKit and creating an HKHealthStore. That's all you need to do. Now that we finished setting up HealthKit, let's move on to saving data - specifically, the distances we've logged.
But before we do, we'll need to take a deeper look at how HealthKit organizes and structures our data. I have this little ritual of mine where right before any presentation or talk, I like to do a little bit of a workout It helps boost up my energy but also calm my nerves. There's nothing a couple push ups or a couple sprints can't fix. So that's what I did right before this presentation. I did a quick workout. So let's see all of the health data that was recorded during my workout. For my workout, I went for a walk from 2:35 to 3:00 p.m. today. I walked outside because the weather was gorgeous... partially cloudy, low humidity... great for a good hair day. I walked 628 meters and I was blasting Lizzo and Rico Nasty a little bit too loudly on Apple Music and should probably turn down the volume moving forward to preserve my hearing health. Five minutes into my walk, I also started noticing some mild abdominal cramping due to the fact that my period is going to start any day now. All of these different types of health data are stored as Health Samples. Health Samples are all structured in a very similar pattern. They all have the associated type of data that they represent, the value of that type of data, the time these health events occurred and metadata which represents any additional information we want to store about these samples - like whether or not my workout was outdoors or indoors, the weather outside, the device that recorded it and even the application that wrote it. As all of these different types of data are stored as samples, there are some nuance differences between all of them. Right now I'm going to focus on one type of sample called Quantity Samples. Quantity Samples store a numerical value and unit associated with our health data - perfect for storing the distance we walk and our headphone audio level exposure.
Regardless of type, we always want to remain in control of our data and to do so, HealthKit has its authorization system, which will allow you to request authorization for users' health data by specific data type. And to give users the most control over what data applications have access to, read permissions and write permissions are handled separately. So your user may or may not grant you permission to the data that you requested for, depending on the sensitivity of it and what they feel comfortable sharing.
Any time you request authorization, the screen on the right might appear to the user. This is HealthKit's authorization sheet. It allows the user to grant or deny permission to the data that you're requesting access for.
So it's important to request authorization in context so the user has an understanding of what the screen is and why this application needs access to their health data. You'll be required to define two keys in your application's Info.plist: Health Update Usage Description and Health Share Usage Description.
This will give you an opportunity to briefly explain what your application does and how it interacts with the user's health data. Another important part of context is requesting authorization when there's good timing. Request authorization any time you intend to interact with the user's health data, whether that's saving or reading their health data. Onboarding is also another great place to request authorization because that's usually where the user has an understanding of what your application does and how it'll use their health data in the experience. As requesting authorization in context is really important, make sure to request authorization for only the data that you need to create the best experience for the user. If, for instance, you requested authorization for all HealthKit data types (that's well over 100 by the way), then the user might be a little bit confused as to why an application that's helping them perform a swimming workout is also asking them for all the times they brush their teeth. I mean you could be hitting that really niche group of athletes who swim while also brushing their teeth but I'd be a little bit skeptical. Finally, request authorization every time you intend to interact with the user's health data. HealthKit is the source of truth when it comes to user permissions, and user permissions can change at any time outside of your application so make sure to request authorization so your application stays up-to-date. To save the distance I walked during my workout today, the first thing we'll need to do is request write access to our walking distance data type using HealthKit's authorization system. Once we do, we'll need to create our walking distance sample and save that sample using our HealthStore. To request write access, what we'll need to do is to find the type of data that we want write access to. In this case, it's just our distanceWalkingRunning quantity type. Then we'll call the request authorization method on our HealthStore, passing in the data that we want to write to and read from. In SmoothWalker, we're only writing the distances we've walked. We're not reading any of that data. So we're only going to request write access. Finally, we'll know if we successfully requested authorization in our completion handler. Success in this case does not mean that the user has granted permission to these data types.
It just means that you successfully requested authorization and if it failed, you can always try again. User privacy is so important when it comes to our sensitive health data so make sure when requesting authorization to only ask for what you need, when you need it, every time. We finished requesting write access so let's move on to our final two tasks of creating our sample that represents the distance I walk during my workout and saving that sample using our HealthStore. We'll need to define the type of data that we want to save, which is our quantity type distanceWalkingRunning - the same quantity type we requested write access for. Then we'll need to instantiate the start and end time of when I walked that distance. In this case it was 2:35 and 3 p.m. today. All quantity samples in HealthKit are stored with an associated HKQuantity. HKQuantities represent the value and unit associated with the sample - so from my walk today I walked a distance of 628 meters. A really powerful part of HealthKit is that it can convert between different units for you. So, for instance, if I wanted to see all the distances I walked in meters in one application but in yards in another application, then HealthKit can do all that conversion for you on the same data set - so you don't have to do any of that manual math. We have all the pieces needed to construct our sample. We'll pass in the type of data we want to create, which is our distanceWalkingRunning quantity type, our quantity of 628 meters and our start and end date 2:35 and 3 p.m. today. Finally, we'll call our save method on our HealthStore and pass in our sample, and we'll know if we successfully saved the sample into the database through our completion handler. And just with that we saved the distance I walked during my workout. We just took a deeper look into how Quantity Samples are structured and stored in HealthKit.
But not all of our data is necessarily best represented in quantity samples like my workout or my abdominal cramps today. So let's take a deeper look into how HealthKit organizes our data in a more general way. To reiterate, we have Quantity Samples that store a numerical value and unit associated with our health data - perfect for storing the distance I walked and my headphone audio level exposure. However, not all of my health data is necessarily quantitative.
Some of it is more qualitative and those are stored as Category Samples in HealthKit where the value that you can record comes from a predefined list and it doesn't carry a unit. So for instance when I log my mild abdominal cramping today, I had the option to log whether it was mild, moderate or severe and, thankfully, it was just mild. Both of these types of samples represent singular values but not all of our health data is necessarily represented in that way in HealthKit like my workout, which summarizes multiple values and can carry multiple units depending on the values that it's summarizing. Like my workout today summarized the 628 meters I walked, the three flights I climbed and even the 105 calories that I burned. All of these different types of data are samples in HealthKit. Samples all have an associated start and end time of when these health events happened. As we looked at quantity samples, category samples and workouts, there are many more different types of samples available that I encourage you to look into. Not all of our health data though changes over time like samples do. Some of our health data remains more static over our life - like my birthday or my blood type. These are stored as characteristics in HealthKit. All of these different types of objects are unified under HKObject, where it has the associated unique identifier for data so you can easily reference it, the device that recorded it and even the application that wrote it. This hierarchy represents the objects that are stored in HealthKit. There is also a parallel hierarchy of all the types in HealthKit.
For instance, the distance I walked today was stored as a quantity sample of the quantity type distanceWalkingRunning. My mild abdominal cramps was stored in a category sample of the category type abdominalCramps.
We just took a look into how HealthKit organizes and structures our data.
So let's move on to our final and third task of reading data. When it comes to reading health data, you'll want to use queries. HealthKit has a wide range of queries to help you accomplish the task that you have at hand.
Here are some of the many. Today, we're gonna look into three of them: HKStatisticsQuery, HKStatisticsCollectionQuery and HKSampleQuery.
When it comes to using queries, the first thing you'll need to do is construct a query object. You'll need to define the type of data that you want to read as well as specify a filter that will restrict the results returned from your query. This is done by defining a predicate. A predicate can vastly improve the efficiency and performance of your query by filtering out the results that it returns. Finally, you'll execute your query on our HealthStore and get our results from our handler. The first query we're gonna look into is HKStatisticsQuery, and it is exactly what it sounds like.
It's a query that performs statistics on samples, specifically quantity samples.
Let's take a look at what type of statistics can be computed with an HKStatisticsQuery. For instance, I might want to know the total amount of steps I took during my workout or the total amount of calories I burned.
And if I'm feeling particularly brave, I might want to know the total amount of caffeine I consumed today, which might unveil a matcha latte addiction.
I think I'll hold off on that one.
We can compute the sum of these different data types but it doesn't make sense to sum up all of our health data. For instance, I might want to know my average UV exposure for today so I know that my SPF 50 and my shades had me fully protected... or to know that my hit workouts were really impacting my minimum resting heart rate... and even know my maximum body temperature from when I was sick a couple weeks ago. We can also compute the average minimum and maximum on our health data. It doesn't make sense to sum up all of our body temperatures or compute the average calories I burned during my workout, so the type of statistics we can compute depends on what makes the most sense and what reveals the most insightful information about our health.
This is known as the aggregation style of samples in HealthKit where the total steps I took and my calories burned are all cumulative while my UV exposure and my heart rate are discrete. Today I want to know the total amount of steps I took this past week from June 15th to today. Here are all my steps for the past week. What I'll do is construct an HKStatisticsQuery specifying the type of statistics I want to compute, which is cumulative sum. I'll execute it on our HealthStore and we'll get back an HKStatistics object, which has the start date and end date of all the samples that make up our statistic as well as our sum of 6,902 steps. It's not that much. I probably should really up my activity moving forward. All of those samples were recorded on my Apple Watch but sometimes when I go for a walk, I like to bring my iPhone with me in case I need to make a couple of phone calls. Both my Apple Watch and my iPhone are both recording steps.
If we were to sum up all of my steps over all of my devices then we would easily be doublecounting a lot of those steps. HKStatisticsQuery can help with the de-duplication of data. For instance on June 15th, HKStatisticsQuery will ignore those 900 steps recorded on my iPhone since that would be duplicated data but it would also add in those additional 1100 steps that were recorded on my iPhone when I forgot my Apple Watch at home.
So we'll construct our HKStatisticsQuery, execute it on our HealthStore and get our updated HKStatistics object with our new sum of 8002 steps. So I did better but not by that much. I'm more interested in the patterns of my daily step count. I want to know the steps that I take every day for the past week. What we could do is construct HKStatisticsQueries for every day of the week. That would be seven of them. That might be fine for now but if I wanted to know my daily step count over the past year then that would be well over 300 HKStatisticsQueries to manage and that just becomes too much. HealthKit has a solution. It's called HKStatisticsCollectionQuery, and it's a query that performs statistics on fixed time intervals that you specify. In this case, we'll specify a daily cadence of when I want the sum of my steps to be computed. I'll also need to specify an anchor date which represents when our statistics start being computed and how our samples get bucketed. When I construct my HKStatisticsCollectionQuery, I'll specify my anchor of June 15th at midnight, my time interval of a daily cadence and the statistics I want to compute, which is the sum. I'll execute this on my HealthStore, and we'll get back an HKStatisticsCollection object. An HKStatisticsCollection object is exactly what it sounds like. It's a collection of HKStatistics. So for every day of the week, we'll get an HKStatistics object with the sum of our steps.
So for Monday, June 15th, I'll get a sum of 2,222 steps. Tuesday, I'm not going to lie. I was super lazy... didn't even get out of bed so I didn't take any steps. Wednesday, I have a sum of steps of 1,100 so I'll get an HKStatistics object with a sum of 1,100, and the rest will follow suit. Here are some of the many charts in Health that are all powered by HKStatisticsCollectionQueries.
It's incredible to see how one query can unveil so much useful and insightful information about our health. HKStatisticsCollectionQuery is really powerful.
I want to touch on one of its other super powers. It can listen for updates to the user's health data. To do so, we'll need to set its update handler before executing the query. That way our query will be running in the background listening to any new statistics or new data coming into the health database.
Now our query will be running in the background indefinitely, so we'll want to make sure to call stop when we're done collecting all the data that we need. So let's go ahead and use HKStatisticsCollectionQuery and SmoothWalker to display our daily step count over the past week. The first thing we'll need to do is construct our HKStatisticsCollectionQuery. Once we do, we'll execute it on our HealthStore and update our UI with the HKStatisticsCollection object we get back. Let's move on to our demo. All of the code you're about to see is available online through a sample Xcode project for you to download and to play around with. Here we have SmoothWalker running in our simulator in the left-hand portion of my screen. The view is empty right now because we haven't implemented the pieces to display our daily step count. In the upper right-hand corner we also have an add data button which will allow us to add any new steps for today. So let's go ahead and move over into our Xcode project and implement all the pieces we need to display our daily steps. Here I am in DailyStepCountTableViewController. This is the view controller that's powering our view in the simulator right now. We have an array that's hooked up to our table view so all we need to do is read our daily steps and populate our array with that. The first thing we'll need to do though is request read and write access to our step count data. We'll need to request authorization where there's appropriate context. A great place to do that is in our method viewWillAppear. That way when our application loads or we hit that step count tab, our authorization sheet will appear.
So let's go ahead and do that.
Here we define the type of data that we want read and write access to, which is our stepCount data type. Then, using our HealthStore, we'll request authorization, passing in the data that we want to write to and read from. If we successfully request authorization, we'll call the method calculateDailyStepCountForPastWeek. This is the method where all the magic happens. It's where we'll read our daily steps and update our UI. So let's go down into this method.
Here I am in calculateDailyStepCountForPastWeek. We'll need to construct an HKStatisticsCollectionQuery, execute our query on our HealthStore and update our UI with the results. The first thing we'll need to do is define all the pieces needed to construct our query like the type of data we want to read, which in this case is our step count data.
We'll need our anchor, which is Monday at 3 a.m.
Our time interval, which is a daily cadence. There can be a lot of steps data in HealthKit but we're only interested in computing statistics over the past week's worth so we'll define a predicate. That way we're only computing statistics over the past week's worth of step samples.
Here I define my date from one week ago, and I'll construct my predicate using HKQuery's predicateForSamples. We have all the components needed to construct our query. So let's go ahead and do that. Here I have my HKStatisticsCollectionQuery passing in the type of data I want to read: which is our step count data, our predicate of one week ago, the statistics we want to compute (which is our sum), our anchor of Monday and our time interval of a daily cadence.
Now that we've constructed our query, we'll need to update our UI with the results we get back. Here we have our initialResultsHandler. When our query is done executing, we'll get back our statisticsCollection and an error if there was any issue reading our health data. Here is where we'll update our UI.
If there are any statistics to show on our table view, we'll call our method updateUIFromStatistics. This is where we'll pass in our statisticsCollection and update our array that's backing our table view. So let's go down into this method and implement it.
Here I am in updateUIFromStatistics. It takes a statisticsCollection object so we'll enumerate over all of our statistics and append it into our array that's backing our table view. Since we'll be updating our UI, we want to make sure to dispatch back to the main thread. Here we have our dataValues array, which is the array backing our table view and our UI. It is an array of HKStatistics. What we'll need to do is enumerate over statistics for the past week in our statisticsCollection object. We'll define our start date of one week ago and we'll enumerate until today.
Then we'll call our enumerateStatistics method on our statisticsCollection, and as we enumerate we'll append our statistics into our array backing our table view. Finally when we're done enumerating, we'll call reloadData.
This will refresh our table view and update our UI. The final thing we need to do though is execute our query so we'll go back up into our calculateDailyStepCountForPastWeek method and execute our query after setting our initial results handler.
And with that, I'll hit run and let's see what happens.
Ah. Our authorization sheet just popped up on our device. This will allow us to deny or grant permission to our step count data for SmoothWalker. Since I'm not particularly sensitive about steps data, I'll enable both read and write access for it. I'll hit "Turn All Categories On" and hit that "Allow" button.
Ah. And our table view just updated with all of our daily steps for the past week. Let's go ahead and try and add some data for today. I'll add 800 steps and hit that "Add" button. And as you can see, our table view didn't update.
And that's because we haven't set our update handler on our query. So let's go back into Xcode and set up our update handler. That way our query will be running in the background listening for any new health data or any new steps coming in. We'll set up our update handler in our method calculateDailyStepCountForPastWeek. We'll need to set up our update handler before we execute our query.
So that's where we'll do it. Here, I have my statisticsUpdateHandler.
This will get called when there's any new data that's coming into the database or if there's any new statistics. We'll get back any new statistics, a statisticsCollection object and an error if there's any issue reading our data. Here we'll update our UI, calling the same method we did in our initial results handler.
Now our query is going to be running in the background indefinitely, listening to any new steps that'll be added into the database so we'll want to make sure to call stop when we're done collecting all the data that we need.
A great place to do that is in our method viewWillDisappear. That way, our query will stop when our view is no longer visible on our device. So let's go ahead and implement that.
Here we're in viewWillDisappear. We'll stop our query in this method.
And with that, let's hit run again and see what happens when we add some new steps for today. So I'm going to add some data, I'll add 235 steps - and our table view just updated with that new step count.
And with that, we just finished displaying our daily steps in our application.
Let's just take a look at what we just did. We constructed our HKStatisticsCollectionQuery, executed it on our HealthStore and updated our UI with our statisticsCollection object. We just took a look at HKStatisticsQuery and HKStatisticsCollectionQuery. They are super powerful. However, you may not always want to compute statistics on the health data, and not all of our health data is quantitative. You can use HKSampleQuery to retrieve the raw health samples stored in the database. So let's go take a look at how we would do that. Here we'll define the type of data I want to read, which is my workouts. Then I'll define a sort, which will sort all of the objects returned back from my query. In this case, I want them sorted from the most recent date that they finished. Finally, we'll construct our sample query, passing in the type of data we want to read, our predicate, which is nil, our limit of 1 (since we're only interested in the most recent one workout) and our sort. Finally, we'll execute our query using our HealthStore.
There are many other types of queries available in HealthKit to help you accomplish the task that you have at hand. We have HKAnchoredObjectQuery, which will allow you to track changes in the user's database. There's also HKActivitySummaryQuery, which will allow you to display the rich activity ring data from our Apple Watch. There's also HKWorkoutRouteQuery which will display all of the locations we've been on during our outdoor workouts.
And with that we finished integrating HealthKit into SmoothWalker. We discussed a lot today. I want to go over some of the bigger takeaways.
HealthKit is a framework that helps create and manage a central repository of the user's health data, providing a rich ecosystem for your health application.
However our health data makes up some of the most sensitive data on our device so make sure to request authorization for only the data that you need. When you need it. Every time. HealthKit has a rich type system to help organize and structure health data. We looked at quantity types, category types and even workouts but there are many more that I encourage you to look into to see what works best for your health application. HealthKit has a wide range of queries to help you efficiently and performantly read data.
We looked at sample query and statistics queries today but there are many more to help you create the next great health application. We're so excited for you to join our healthy community here at Apple and to see the incredible health applications you'll build that will revolutionize the way users take control over their health.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.