App Frameworks • iOS, tvOS, watchOS • 50:47
HealthKit has become the standard for apps that help you manage and track your health on iOS and watchOS. Learn about new HealthKit data types in iOS 11, how to create and access workout route data, how to avoid duplication of health data, and enhancements made in HealthKit to support people managing diabetes.
Speakers: Alexa VanHattum, Michael Ozeryansky
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript has potential transcription errors. We are working on an improved version.
Good afternoon, everyone. Thank you for being here. Welcome to "What's New in Health". My name is Alexa. I'm a software engineer on the HealthKit Team and one of my favorite parts of being on this team is getting to see the ways in which technology can help people improve their lives. Like many of our users, I've absolutely loved getting to use all fitness features on my Apple Watch. Getting to close my rings every day and share my activity with friends. And really the integration with all of your great fitness apps have made it easier to get fit.
Many people also use technology to help them track other components of the well-being like sleep. Having a good understanding of your sleep can help you be more prepared for your day and get a better night's rest. However, many of our users also rely on technology in ways that we may not even think of. For example, in managing a chronic condition like diabetes and on iOS and watchOS, the home of all of this personal health data is HealthKit.
So, we've seen that users have really bought into our health and wellness ecosystem. And by innovating with HealthKit, you're app can immediately take advantage of the guarantees of privacy, transparency and control that we aim to give all of our users. So, in the next 60 minutes we'll be going over what's new in our SEK this year in iOS 11 and watchOS 4 to help you continue to create innovative health technologies on Apple platforms. Let's dive in. So, first we'll go over a quick tour of the new datatypes in HealthKit this year. After that, we'll cover some general updates to our workout API's to help build really great fitness experiences.
After that we'll introduce sync identifiers which are a new feature to help you de-duplicate data across multiple devices. And finally, we have some updates to share regarding our support for users managing diabetes. So, let's get started with our new HealthKit types. So, if you've used HealthKit before you'll know that we categorize all of that personal data into a bunch of different types that you then save to HealthKit. And we have some new ones to introduce this year.
I'm going to start with the sample types. So, samples that you save into HealthKit are bits of data that have particular timestamps and are of a certain type. The first new one that we have this year, is HKWorkoutRoute. So, workout routes let you save a map of your colocation during a workout. And we will cover this in much more detail later in the talk. We are also introducing waist circumference. So, this is a body metric similar to height or weight that users can track over time to understand changes to their body.
Another body metric that we've introduced this year is VO2 max. So, VO2 max measures the maximum rate of oxygen intake during peak exercise. And what's really cool about this is on watchOS 4, the Apple Watch can even estimate VO2 max during certain outdoor walking and running workouts. So, with permission to do this data, your app can see the VO2 max that was estimated by Apple Watch. And finally, one of our most requested types in HealthKit, this year we are introducing insulin delivery. My colleague Michael will cover this [applause].
My colleague Michael will cover this in much more detail later in the talk. So, along with these sample types, we've also introduced some new workout activity types. So, these categorize the different workouts your users might be doing. And we have a wide range of them and they configure your workout as well. So, the first one we've introduced this year is Tai Chi as well as mixed cardio, and finally hand cycling. So, you can use these to build even more encompassing fitness experiences for your users.
Along with our new workout activity types, we also have some general updates to our workout API. So, some of these are tied to our fall release where we released the Apple Watch Series 2. And some of this is new this year in iOS 11 and watchOS 4. So, we'll cover some updates regarding swimming. We'll talk about the new segment API. And we'll also talk about a new way that your apps can pause and resume workouts.
So, last fall we released the Apple Watch Series 2. And people have absolutely loved getting to take their watch swimming. You can take it in the pool as well as out in the ocean and lakes. And so, I'm going to start by covering some of the types of tracking that Apple Watch does for swimming workouts, and then dive into some of the new stuff this year.
So firstly, as I mentioned we have support for both pool and open water swims. And for pool swims you can configure the lap length to get the most accurate statistics about the workout. We also automatically track some key swimming metrics. The first of these are swimming distance and swimming stroke count.
So, if you're familiar with HealthKit, you'll be aware of how you would want to open a query against certain samples that you're interested in during the workout. For example, the calories the user has burned or their heart rate for that current time. So, similarly, these two metrics you'd want to open a query for and track throughout the workout, so you can display them live to the user.
Apple Watch can also detect individual laps as the user is swimming in real time. In addition, we even track the stroke style per lap, which is really cool and can help give really up-to-date statistics about the workout. So, the stroke style detection is stored as metadata on both the stroke count sample, as well as each lap event. And we'll go over what that looks like in a little bit.
New this year, in watchOS 4, we also have automatic set detection. So, this is HealthKit detecting the time in which the user was swimming continuously and highlighting that to the user. And your apps will have access to that information as well. Also, new this year, in watchOS 4, your apps can now enable Water lock.
So, this is a really great feature that allows your app to ignore any input from water on the screen. Which is really great to get a consistent user experience. When the user wants to exit Water lock, all they need to do is simply twist digital crown on their watch and it'll eject the water through the speakers by displaying a custom sound.
So, now let's dig in by going over some of the new metadata keys and values that we introduced along with swimming. As I said, we have location type. And this is both pool open water, as well as unknown. We also have the swimming stroke style, which is common styles like freestyle and backstroke as well as unknown or mixed.
So, now let's say you want to build a swimming app and you want to know how to start out with HealthKit. So, the first step is going to be to configure a swimming workout. Let's say in this case we're doing a pool swim workout. So, first we'd want to create our HK workoutConfiguration object. Next, we want to set some properties on it. Firstly, the activity type to swimming.
The swimming location type. We said we're going for a pool swim. And then the lap length. In this case I'm setting it to the HK quantity of 25 yards, which is a pretty common pool length. Next, we want to start our session by creating a session object and then passing it to the healthStore.
So, first we're going to create our workout session and pass in the configuration. And you'll note here that the initializer for HK Workout session could throw. It would throw in the case that you pass an invalid workout configuration. For example, if you try to set the lap length on an open water swim. So, be sure to handle that in your code.
Next, we're going to set the delegate of the workout session to be ourselves so that we can receive callbacks from HealthKit as swimming events happen. Finally, we're going to actually start the session by calling the start method on healthStore and passing in the session we just created. So now, great. Our swimming session is ongoing. Maybe we also want to enable water lock for this workout.
So, a good place to do that is when our workout session changes state to start running. This is because this API will only work if you have an active workout, or a location session ongoing, and if you're in the foreground. So, let's look at this method. Workout session did change to from state date.
We're going to switch on the from state to the to state. In this case, we're interested in not started, switching to running, which is how we know that our session has begun. So, here's where we're going to want to grab the shared WatchKit extension and then call enable water lock on it.
So, along with the changes to our swimming data types, we also have some changes to our workout events to better support swimming as well as more nuance workout experiences in general. So, let's start with just an overview of HKWorkoutEvent. So, WorkoutEvents highlight a specific time of interest in your workout. That can be used for pausing and resuming, as well as things like laps and markers. And markers can really be anything arbitrary set by your application, and you can store data on the metadata of the event to store what you need for your purpose.
Events are created by both HealthKit and your app. And this is key because HealthKit will create events and then pass them to you immediately. So, that's how you'll find information about laps, and strokes style and that for swimming as well as pausing and resuming through general workout apps. And WorkoutEvents are saved on a list on HKWorkout. And so, you'll get them back when you're querying for workouts. Of, if you're looking at workouts from other applications.
In addition, workout events affect the workout's duration. Specifically, pause and resume events. So, if I go for a 10-minute run and I pause for a minute, HealthKit will see that there's a pause event followed by a resume event a minute later and calculate the total duration to be 9 minutes, just as we would expect.
So, let's take a look at how we can observe lap events back in my swimming example. So, we have this method, workout session did generate event. And that's where HealthKit is going to pass through events as the workout is ongoing. So, we're going to switch on the type of event that's given to us. In this case, what we're interested in is laps.
So, now that we have our lap event, we do something specific to our app, like maybe increment a count, so we can display it live to the user. We can also grab the stroke style off of this lap event. So, the stroke style is stored as metadata. We want to get the metadata for the key swimming stroke style and then again we can do something specific like display the current stroke style back to the user. So, as you can see, this can help you build really detailed experiences that your user can access really quickly right from their wrist.
So, we also have some new WorkoutEvents this year in iOS 11 and watchOS 4. So, here's our existing WorkoutEvent type Enum. And new this year, we've added segments and pause and resume. So, both of these new types have important implications for swimming, but they can also be used for all workout apps in general.
We'll start with talking about segments. So, rather than representing just a moment in time. Segments can represent a moment and a duration. And so, this has caused us to update our existing WorkoutEvent class; whereas previously we had a date property, we've deprecated that in favor of a date interval property. So, for all previous events saved to HealthKit, the date will now be the start date of the state interval, and they would have a duration of 0.
For segments, however, you would have both a start data and a duration, or equivalently a start date and an end date on that date interval. So, I want to show you a timeline of what this would look like for a typical swimming workout and all of these events you can get in your application as they happen with your workout session.
So, first we start our workout. And our user begins swimming. Each of these grey dots represents a lap event as the user is swimming if we take a look at a particular lap event, you'll see that the type of event is lap. It has date interval, in this case just the start date of 2. And then it has on the metadata the stroke style. So, our user's doing a freestyle stroke.
The user continues swimming and decides to switch stroke style to butterfly. This will be reflected immediately in the next lap event your application receives. The user might then decide to pause their workout and you'll get an event for that pause as well. In this case, you have no metadata on the pause event. And you also have just the start date and no duration to your date interval.
Now, in watchOS 4, HealthKit will actually generate the auto set detection for this particular swimming workout for the time in which the user was swimming continuously. So, this segment event has both the start date of 0 and it ends at 3:30 and the user has paused. And it doesn't have any metadata on it in this case.
Now, the user resumes their work out again, and we receive an event for that. And they begin swimming back to freestyle. Now, at this point we're like six 30 minutes in and our user gets tired and they decide to stop at the edge of the pool and take a breath, but they forget to actually pause their workout. However, HealthKit will still generate an event for the time in which the user was swimming continuously. So, there will be a segment from when they resumed the workout to when they stopped actively swimming.
Our user begins swimming again, once they've caught their breath. And HealthKit generates a final segment from when they started swimming again to the end of the workout. So, as you can see, you can use those to build really rich swimming experiences, but your apps can also create segments that are specific to any other type of workout app that you build.
So, the other new WorkoutEvent type that we have this year is pause and resume request. And if you've used the Apple Watch workout app, you might be familiar that you can press the digital crown and side button simultaneously to pause your workout without ever interacting with the screen, and now this year, your apps can enable that behavior as well.
So, again it's a quick press of the digital crown and the side button. And it does work in water lock. So, this is really great for swimming applications, because your user doesn't need to interact with the screen at all, or disturb their workout, they can pause just by pressing the buttons.
Like other events, you'd want to handle this in your workout session delegate, however, it's important to note here that we will not pause and resume automatically when this event is received. It's up to your application to actually enable this behavior by calling pause or resume. So, let's take a look at what that would look like to enable this behavior.
So, first our user is going to press the digital crown and side button to indicate they'd like to pause. And HealthKit then generates a pause and resume request. The application receives this request, in your workout session delegate and then you choose to respond to it. So, in this case we're going to respond. So, based on our state, we either pause or resume. If we're currently running, we'd want to pause and if we're currently paused, we'd want to resume.
When HealthKit receives that, it then generates an event for the pause or the resume itself. And finally, that pause or resume event is also received in your workout session delegate. We think this is a really great feature to provide consistent experiences to users using different workout apps. And we hope that you adopt it to continue that behavior.
So, another new, exciting workout API this year we've introduced is workout routes. Users can use this to track their location throughout their workout. And people have really loved doing this with the Apple Watch series 2. People have used it for things as ambitious as marathons or maybe just a casual jog around Golden Gate Park. So, let's start with how your applications can read this data from HealthKit.
So, we have a new datatype which is HKWorkoutRouteType. And it's important that this type requires additional authorization. And this is really on two fronts. The first is that even if you have access to read and write workouts from HealthKit, you need additional authorization to read or write WorkoutRoutes.
In addition to actually get the user's location you would want permission or authorization from Core Location to view the user's location. So, HealthKit models WorkoutRoutes as an array of CLLocation, which is Core Location Location. Each location has latitude and longitude as well as some other data about the location at that moment including speed.
It's important to know that these datasets can be quite large. So, in marathon example, it can be many thousands of data points. Because of this, we've introduced a new query specifically for WorkoutRoutes, which is HKWorkoutRouteQuery. This returns location data in batches, rather than all at once. So, you don't have to hold it all in memory at one time.
So, let's go over what this looks like in code to read a WorkoutRoute from HealthKit. So, we're going to assume we already have the workout that we're interested in saved as a workout object. So, first we create the type we want by calling WorkoutRoute on HKSeriesType. And then we create a predicate for the objects associated with our workout.
Next, we want to use an HKSampleQuery. So, this is an existing query that just returns samples of a certain type. And we're going to pass in our WorkoutRoute type as well as our workout predicate. We don't have any limit on the samples we want to receive back in this case. And we don't have any sort descriptors.
So, this will give us back the query itself as well as the samples we're interested in and potentially an error. So, the first step here, is we're going to guard that the samples that we got back are indeed HKWorkoutRoutes. And now, the second step is to actually query for the raw location data from each route. So, here's where the new HKWorkoutRouteQuery comes in. It takes the WorkoutRoute and it then returns that query. The raw locations, a Boolean to indicate whether it's done batching the data to you, and potentially an error.
So, here we'd want to do something specific to our application like add the location data to a map. And note that this block will probably be called multiple times as the data is returned to you in batches. Finally, we execute this by calling execute on the queries, or calling the execute method on healthStore and passing these queries. So, now let's go over how your applications can actually build and save your own workout routes. So, we use a builder model called HKWorkoutRouteBuilder. And the lifetime of this model is that you want one builder object per route that you're keeping track of.
So, location data is added asynchronously and it's sorted by date, by HealthKit when the series is finalized. So, you don't need to worry about the order in which you add the data. Also, the workout needs to be saved before the route. So, let's go over what the timeline would look like if you have a workout session and you want to keep track of the route for it and save it to HealthKit. So, first we're going to create our WorkoutRouteBuilder. Next, we want to actually start our workout session as we've shown.
Then our workout is active. And we want to begin observing location data. And this is where you' would be observing location data from Core Location, the location framework on iOS and watchOS. You then want to add those locations to your builder. And this process is ongoing. You want to do it many times throughout your workout.
Finally, you'd want to end your session and save the workout. And then the last step is to finish your WorkoutRoute, which saves it into HealthKit. So, let's take a look at the same thing in code. You're going to start by creating your RouteBuilder and adding locations. So, we create our builder, passing in the healthStore and a device. In this case, nil will default to the local device.
You then add locations as the workout is ongoing. So, presumably this snippet of code is called multiple times. And here I'm calling some local method fetchRecentLocations that returns me a list of CLLocations, and then inserting those locations into the builder. Because I'm a responsible developer, I'm going to handle all my errors.
Step three. So, this is after our workout is saved. We'd want to finish the route. And this takes in the workout objects that we've already saved, as well as any metadata that we want to be on the route. So, next I'd like to demo adding WorkoutRoutes to an application to you guys. So, if you attended our talk or watched online last year, you'll remember that Speedy Sloth is a super cool workout application for sloth lovers. And today we're going to add location routes to it.
Great. So, I'm going to switch over to XCode and you'll see here that I have Speedy Sloth running. So, Speedy Sloth is a WatchKit app that lets you configure your workout and then run it and show you some live metrics. And it saves the workouts into HealthKit. So, I'll start here by just running Speedy Sloth so you have an idea of what it looks like currently. So, you'll see that we keep track of the duration of the workout. I can pause or resume. And we keep track of the current totals of your metrics as the workout is ongoing. So, we have calories coming in, as well as meters.
So, I'll stop that. And we saw a summary there of our workout. So, switching over to XCode, I have this class, healthStoreManager. So, this is the class that handles all of our interactions with the HKhealthStore, which saves and reads data from HealthKit. So, what I want to do first, is implement the CLLocation Manager Delegate Protocol. This will allow me to receive locations from Core Location.
In order to track these locations, I'll need some properties. So, the first of these is going to be the HKWorkoutRouteBuilder that we just went over. Next, I'll also need a location manager from Core Location to receive the updates from them. Now, I want to actually start accumulating location data.
So, we have this method, start accumulating data. And this is called when our workout session changes state from not started to running. You can see here that I was already using this place to start walking and running queries, which is what's updating that distance metric, as well as the ActiveEnergyBurnedQuery which is going to be showing the calorie metric. So, here's a good place to also start accumulating location data. So, here I have a new method, startAccumulatingLocationData.
And I'm going to add in the implementation here. First, I want to guard that he location services are enabled, otherwise return. And then I want to create my CLLocationManager object. I'm going to set the delegate of the object to be myself, so I can receive callbacks for the locations that are incoming. I'm going to set the desiredAccuracy to be the best possible accuracy. This is a good idea since we're going to be displaying this data on a map. So, we want the most accurate data possible to show visually to our user.
Next, I'm going to set allows background updates to be true. And so, this will allow us to continue to receive locations while we're in the background. So, in order for this to work, we'd also need to setup some keys in our info.keylist. I'm not going to demo that explicitly, but we do have Speedy Sloth as a sample app that you can check out at the website we'll show at the end of the session.
And finally, from the Core Location side, I want to start updating locations. And all I need to do on the HealthKit side is simply instantiate my HKWorkoutRouteBuilder, passing in the healthStore variable, which is a HK healthStore, as well as a nil device. Awesome. So, now we have all the variables we need to track location. We've set everything up. Now, we actually need to handle those locations as they come in. And to do that we want to implement the function from Core Location Manager Delegate, locationManager didUpdateLocations. So, this passes us the manager object itself, as well as the list of CLLocation.
So, first, I'm going to filter the locations that come in. This is a good idea depending on the exact purpose of your app. I'm going to do a really simple filter here, in some cases you might want to instead do something like smooth the route, but for this demo, I'll just do a simple filter.
So, I'm checking that the horizontal accuracy is less than or equal to the nearest 10 meters. It's also important to do this because even though we set the desired accuracy on the Core Location Manager, that is not guaranteed. Next, I want to check that filter locations isn't empty otherwise return.
And finally, I'm going to insert this data into the WorkoutRouteBuilder, passing in the filtered locations and receiving back successfully and an error. If it's not successful, I'm just going to print the error here. Awesome. So, now our workout session is ongoing, we're receiving locations from Core Location and inserting them into our builder. Now, we want to make sure we clean up after ourselves, once the user has ended their workout.
So, this method, stopAccumulatingData is similarly called when the workout session changes state. In this case, when it changes state from running, or from pause, to ended. And so, here I was previously going through my healthStore queries and stopping them as well as removing the active queries on. So, here's where I'll want to also stop receiving location updates. So, I just called stopUpdatingLocation on the CLLocationMangaer.
Awesome. Now, the last step is to actually save this data into HealthKit. So, I have this method, saveWorkout and you'll see it takes in the session as well as a start and an end date. So, first I want to configure some metadata to save with my workout. In this case, what I'm doing is I'm checking whether the location type is indoor, and setting the metadata key indoor to that value.
Now, I actually want to create my workout object with the activity type from the workout configuration. The start and the end date. The list of workout events that I've saved elsewhere in Speedy Sloth. The total energy burned, the total distance, as well as the metadata I just created.
Now, finally I want to save the workout. And if the workout save was successful, you'll see here that we are already adding samples to workout. So, that was actually associating those distance and calorie samples with the workout. So, here's a good place to also finish the WorkoutRoute. So, I'm going to add this snippet of code in here, which calls FinishRoute on the Builder, passing in the workout as well as no metadata for the route itself. And finally, just check whether or not there's an error. Awesome. So, let's run this.
So, you'll note that I also did not show in this demo actually receiving authorization for the HealthKit types. I've already done that in the iOS component of the app. So, be sure to check out how to do that in our sample code as well. So, here we can start another outdoor walking workout, and see again that we have our metrics tracked. The duration, and the calories and meters that are incoming.
Maybe we want to throw in a marker if we've seen a really cool sloth. All right, finally, we're going to stop our workout and see our summary. And then, I'm going to open HealthKit to show you that this data has been saved. So, I'll open the Health app.
And here we see a list of workouts. So, in the most recent one, we'll see here we have workout route saved. I can click in and look about this map view. And now other applications that have access to WorkoutRoute can also receive this data and analyze it, or do anything else that they see fit.
So, that's how easy it is to add WorkoutRoutes to your existing application. I'm going to switch back to the slides, and then I want to summarize what I've just done. So, I first implemented the CLLocationManager delegate protocol. I kept around some variables to keep track of the state, both from HealthKit and from Core Location.
I set up the Location Manager. I made sure to insert the location data I got and Location Manager did update location into the WorkoutRouteBuilder. I made sure to stop updating location when I finished. And then finally after the workout was saved I finished the WorkoutRoute. So, next I want to hand it over to my colleague Michael, who's going to talk to you about a new feature called Sync Identifiers. Thank you.
[ Applause ]
Thanks Alexa. Hi. I'm Michael and I'm an iOS software engineer on the HealthKit team. Today our users are carrying more than one device, such as a watch and a phone. We know that it's important that you want to make sure that your health apps data stays consistent across all their devices. Sometimes this can be rather difficult.
Sometimes, we want to be able to add a sample to one device, and then also add a sample to another device, and then have the sample synced over, it can get sometimes complicated. In iOS 11 and watchOS 4 we've introduced Sync Identifiers. Sync Identifiers allows you to uniquely identify a sample within HealthKit across all your user's devices.
To support this, we've added two new metadata keys. These are HKMetadatKeySyncIdentifier and HKMetadataKeySyncVersion. The Sync Identifier can be any string, such as a string representation of a UUID or the primary key in a remote database. The sync version can be any number. HealthKit will use this version to perform conflict resolution on your behalf.
So, when you save a sample into HealthKit, first it will look for an existing sample matching the Sync Identifier. If it finds a sample, it will then compare the version. If your new sample has a higher version, HealthKit will delete the original sample, and then save your new sample in its place.
If you save a sample with a lower version, HealthKit will ignore this safe. You must use both these keys together to enable this behavior. This metadata applies to any HK object. Such as an HKSample, or an HKWorkout. Further, it's important to note that this is restricted to your source. So, only your app can override samples that you inserted.
Sync Identifiers adds a whole new flexibility to how you can manage your data. By just using these two metadata keys, you could ensure sample uniqueness across all your devices. Further, by taking advantage of the version key, you can also enable local versioning. HealthKit will manage all conflict resolution for you, upon saving and syncing.
Additionally, all operations done using Sync Identifiers are transaction safe. That means that if there was any error, then you can know that your data will be in a consistent state. And finally, relationships to parent objects are maintained. If you replace a sample that's been associated to an HKWorkout, or a HKCorrelation, the replaced sample will still be associated to that HKWorkout or HKCorrelation.
Now, let's take a look at how using sync identifiers can enable our apps to become fully independent. Imagine we have a workout app on our phone and a WatchKit extension. We're going to us a remote server to provide some additional processing after the workout has completed. Our user is going to perform a workout using their watch, and our app is going to save this data into HealthKit. We're going to use a random Sync Identifier and because this is the initial version, we're going to call this version 1.
After the save our watch can upload the data directly to our cloud. As usual, HealthKit notices that there's new data and syncs our devices. Now, the sample exists on both of our devices. Sometime later our cloud finishes processing the data and at this point, we can call this version 2.
Our phone notices new data in cloud and decides to download it and save it into HealthKit. We're going to use the same Sync Identifier that we used previously, but this time we're going to set the version to 2. Because we're using Sync Identifiers, HealthKit will first look for an existing sample matching that identifier. In this case, it found that the existing sample is a version 1, and our new sample has a version 2. So, it deleted the existing sample and replaced it with our new sample. Again, HealthKit notices that there's new data, so it performs a sync.
Because we're using Sync Identifiers during sync HealthKit will also look for an existing sample matching the Sync Identifier. In our case, it found the original sample. Notice that our new sample has a version 2, so it deleted the original sample and replaced it with our new sample. As we expect, there's only one sample in all of our devices, and HealthKit managed all the conflict resolution for us.
Because your apps are fully independent, the Watch does not need to know whether the phone has downloaded data or not. So, our Watch app sees new data in the cloud, downloads it and saves into HealthKit. We're going to use the same Sync Identifier and we're also going to specify the version as 2. In this case, HealthKit saw that there already existed sample with the same identifier, and the same version. So, this sample is ignored.
As we expect, we have one sample on all of our devices and HealthKit managed all the conflict resolution for us. Now, our app can upload and download, and sync whenever it wants. And we don't have to worry about anything. HealthKit manages all the conflict resolution for us and handles all the complicated things necessary.
All right. Alexa just added WorkoutRoutes to Speedy Sloth. Now, I'm going to do a demo on how to update this route. A common example of this might be to do some additional processing such as smoothing out the route and then we want to update the route in place. So, first I'm going to go to Speedy Sloth. Earlier, I added this new ability to Speedy Sloth, to slothify our workouts. What this means, it's going to take our original route and change it so we visit every tree along the way. Just like a sloth would.
All right, so let's see the implementation of this method. First, we're going to go to configuration interface controller. In this class, we have a method called updateWorkoutRoute. This method takes a workout, a route from that workout and all the locations from that route. Below in this file, I've already performed all the necessary queries to collect all this data. When we press slothify workouts, it's going to collect all this data and then pass it to this method.
With our raw locations collected from the initial workout, we're going to pass this to our method, slothifyRouteLocations. This is going to take our locations, find all the trees, and then adjust our route accordingly. Now, we're going to have the new locations. Next, we're going to create a new WorkoutRoute and associate this with our workout. To do this, we're going to create an HKWorkoutRouteBuilder.
Again, we're going to pass our healthStore and a nil device to represent our local source. To insert the locations, we're going to call insertRouteData on our WorkoutRouteBuilder passing our new locations. We're going to ensure that this was successful, and once that is done, we're going to finish our route on the original workout. All right, let's see this in action. Here is Alexa's original route. Let's go ahead and slothify that route.
[Pause] All right, as you can see the original route was slothified, and then applied onto the workout. But the original route is still there. What we need to do now is delete the original route and then add our new route. This can get kind of complicated. We need to make sure the data has been synced over correctly, and then it's also not really transaction safe.
So, to do this, very easily, I'm going to use Sync Identifiers. First, I'm going to go to where we originally created the WorkoutRoute. In this save workout method, after we've originally saved the workout, and right before we finish the route, I'm going to add in some Sync Identifier metadata.
First, I'm going to create the metadata dictionary. Next, I'm going to assign the Sync Identifier metadata key to string representation of a UUID. Next, I'm going to set the sync version to version 1, because this is our initial route. After we've created our metadata, we're going to pass it to finish route, so it is associated to the workout route when we save it onto the workout. Now let's go back to configuration interface controller to where we updated this WorkoutRoute.
To enable Sync Identifiers, we're also going to add the same Sync Identifier metadata to our updated route. First, we're going to grab the Sync Identifier that we used on the original route. Just in case our routes don't have any metadata, we're going to define a default value. Next, let's create a metadata dictionary.
Here, we create our dictionary and then assigns the Sync Identifier to the original Sync Identifier we had. Next, we're going to use the sync version of 2, because this is our updated route. Again, let's pass the metadata to finish route, so we associate it to the WorkoutRoute when it gets saved to the workout. HealthKit will then replace our original route. All right, let's see this in action.
[Pause] All right. First let's perform a new workout. When we save this workout, it will have Sync Identifier metadata. After we've burn some calories and walked a few meters, we're going to go ahead and stop our workout and save into HealthKit. Here, we can see our original route. Now, let's go back and slothify this route.
[Pause] All right. As you can see, the original route was replaced with our new route. All we need to do is add a couple metadata keys, and HealthKit handled all the conflict resolution for us. Now, you can see how easy it is to handle conflict resolution and take advantage of HealthKit's advanced new features.
Okay, onto sample source information. It's important to know that when you display data from HealthKit, the origin of where the data came from. When you query for data, samples will contain an HKSourceRevision. An HKSourceRevision includes the app and the device which saved the sample into HealthKit. In iOS 11 and watchOS 4, we're adding additional properties and a few constants so you can gain a deeper insight into the data that you display form HealthKit.
In iOS 10 HKSourceRevision included the source, as HK source, and the version of the app as an NSString. First, we added product type. Product type is a string representation of the device which saved the sample into HealthKit. So, if you save a sample on a Watch Series 2, this will be Watch 2, 4. Next, we added operating system version. This is the operating system that was running on the device when the sample was saved into HealthKit. So, if we save the sample using watchOS 4.0, this will return 4, 0, 0.
Next, we added three new constants. These constants are HKSourceRevision, any version, HKSourceRevision any product type. And HKSourceRevision any operating system. When creating predicates to query for data, which involves an HKSourceRevision, you must specify all of these values. You can use these constants so you could gain a very specific predicate, such as for a certain product type, but any operating system. With this new information, you can now gain deeper insight into the data that you display in your app. All right.
Finally, supporting diabetes management in HealthKit. Our users love using Apple products to help manage their condition. Today in HealthKit we have support for tracking blood glucose samples, keeping track of carbohydrates and tracking all kinds of activity data, which is all useful in managing diabetes. We've heard there's some missing pieces to the story and I'm pleased to announce that we've added some new additional features to help out.
First, we added the ability to track the relative mealtime to a blood glucose sample. Next, we added the ability to track insulin delivery. With the addition to CoreBluetooth in watchOS 4, now your Bluetooth enabled diabetes devices can connect directly to your Watch. Please watch "What's New in CoreBluetooth" to learn more about that.
To support the relative blood glucose mealtime, we've added a new metadata key. This metadata key is HKMetadataKey BloodGlucoseMealTime. You could use this metadata key when saving blood glucose samples. This metadata key supports two possible values. These values can be found in the Enum, HKBloodGlucoseMealTime. The values are preprandial and postprandial. Preprandial represents any time before a meal. And postprandial represents any time after a meal.
With this new data, now you could gain a deeper insight into blood glucose samples relative to when the user last ate a meal. Now, we are onto supporting insulin deliver in HealthKit. Here is a graph of what it looks like in HealthKit for all data that's been inserted into Health.
To support this new type, we've added a new quantity type identifier. This is HKQuantityTypeIdentifier, insulinDelivery. When adding samples of this quantity type, you must include the delivery reason. To do this, you should use the HKMetadataKeyInsulin DeliveryReason. Metadata. This metadata supports two possible values. These values and be found on the Enum HKInsulinDeliverReason. These values are basal and bolus. Basal is the background metabolic need throughout the day. While bolus is the episodic need such as for a meal and correcting high blood sugar.
It's important to know that you should only add these kinds of samples after user has delivered insulin. Further, when creating an HK sample with this identifier, you should use the international unit. To support the international unit, or the IU, we've extended HKUnit to now return an international unit.
The international unit is the biological effectiveness of the substance. It does not represent a specific amount, but it represents the biological effectiveness of the amount that's been delivered. It's also important to know that this unit cannot be converted to other units, but it does support, common SI prefixes such as mili and micro.
All right let's take a look at how to insert insulin delivery sample. Let's say our insulin pump has delivered 0.85 units of basal insulin over the past hour. First, let's create an HKQuantityType using the identifier insulinDelivery. Next, let's create an HKQuantity using the international unit and a double value of 0.825.
After that we're going to create an HKQuantitySample. We're going to use the quantity type and the quantity we previously defined. We're going to set the start and end date to be the start end date that the pump had delivered the insulin. Next, we're going to remember to include the required metadata for the deliver reason.
In our case, this is basal. After the sample has been created, we can save into HealthKit by passing it to the save function on our healthStore. Now, an insulin delivery sample has been saved into HealthKit, which can be utilized by your app and any other app which helps users manage diabetes.
With the sample in HealthKit, now we can retrieve it back out. To do this we're going to perform a statistic query for all basal samples. I'm interested in every basal sample that's ever been delivered to the user on an hour by hour basis. First, we're going to create a predicate. We're going to base this on the metadata key, insulineDeliveryReason, and we're going to pass in basal. Because that's the type we're interested in.
Next, we're going to define the quantity type, just like we did before. We're going to use the insulinDelivery quantityType identifier. After that we're going to create an HK statistics collections query. We're going to pass in the quantity type and predicate that we previously defined. Next, we're going to pass in a couple of options. These are cumulativeSum and separateBySource. It's important to pass in separateBySource, because HealthKit would normally only return samples within a certain time period.
Because the user has the option to be delivered insulin over the course of an hour, and also by manual injection, these time periods can overlap. So, we use separateBySource to get all the information. Next, I'm interested in all the basal samples. So, I'm going to use date, distantPast for the anchor date. After that, to specify an hour by hour basis, I'm going to pass DateComponents of an hour of 1 to the intervalComponents.
Now that my query is created, I could create the initial results handler. The results pass back from this handler are going to contain all the information that we need to sum up to find out this information that we need. Finally, I'm going to execute this query by passing it to the execute method on healthStore. Now you know how to insert samples and retrieve them back out.
Today we learned about a lot of new features available in HealthKit. First, you learned about new data types and new activity types, which will make your great workout apps even better. Next, you learned about the new WorkoutRouteBuilder API. This allow you to record workout routes and associate them to workouts.
After that we introduced sync identifiers. Now, you can take advantage of HealthKit to manage all data duplication and prevent data duplication across your devices. And Finally, we added additional support for users managing diabetes. For more information and to download the full-implementation of Speedy Sloth, please visit this URL.
[ Applause ]