WWDC2017 Session 221

Transcript

[ Applause ]
>> 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 ]
We have a few related sessions
that we recommend.
Thank you and have a great WWDC
[Applause].