WWDC2018 Session 707

Transcript

[ Music ]
[ Applause ]
>> Good morning, everyone, and
welcome to New Ways to Work with
Workouts.
My name is Niharika [phonetic].
I'm an engineer on the Fitness
team here at Apple, and today
I'm going to be joined by my
colleague Karim, an engineer on
the Health team.
And we are so excited to share
with all of you some of the
brand-new features and
capabilities that have been
added to HealthKit in iOS 12 and
watchOS 5.
Apple Watch was released 3 years
ago now, and since the
beginning, our users have loved
engaging with its health and
fitness features.
They've loved closing their
rings and earning achievements,
sharing with their friends, but
they've especially loved to do
workouts.
And the watch has really become
just the beginning of what is a
vibrant, evolving ecosystem, and
that is thanks to contributions
from developers like all of you.
In just the last quarter of
2017, there were over 200
million downloads of apps in the
Health and Fitness category on
the App Store, and that number
itself is incredible.
Two hundred million is so, so
cool.
But more than that, it's a
testament to 2 things.
Number 1, it's a testament to
the dedication all of you have
shown in getting these great
user experiences out there.
But number 2, it's a sign that
people really care.
People are engaged in the space,
and they are so excited about
what all of you are putting out
there.
And at the heart of this
ecosystem are these 2 apps,
Activity and Health.
Activity is a home for you to
visualize just that -- your
activity data.
You can see your workouts.
You can see calories, exercise
minutes.
And Health is all of that and
more.
And of course, at the heart of
all of that, which is the reason
we're here today, is HealthKit.
And so we have a ton of exciting
things to talk about today, but
before we get started with any
of that, we have to talk about
something extremely important,
and that is privacy and
authorization, because data is
sensitive, especially health
data, and it is so important to
make sure that you have a
comprehensive privacy story when
working with your apps.
Next, Karim is going to share
our brand-new workout features,
which have made it easier than
ever before to develop a robust
workout app.
And lastly, I'm so excited to
share our new Quantity series
API, which is a brand-new way to
both store and relate
high-frequency data.
So let's get started.
Like I mentioned, privacy and
authorization has to come at the
beginning of any development
story.
And at Apple, we think about
privacy in 1 simple way: Privacy
is about people.
And HealthKit is designed with
this in mind.
HealthKit is designed to put
users in control of their data.
Users have the ability to grant
access as well as revoke
whenever they want.
And as developers, all of you
are the last link of that
puzzle, and we want to make sure
we are giving you the tools to
make sure that your users'
privacy is respected.
And we think about this in a few
simple rules.
Number 1 is this idea of
proportional collection, and
this is the idea that you should
only be collecting data relative
to what your app needs.
And this isn't a fixed set of
data.
At the beginning, this amount
might be small.
As your use cases, as your
features grow, the amount of
data you need to collect might
be more, but it is so important
that you only collect what you
need at a specific time.
And second is the idea that
HealthKit authorization can
change.
And this one's a little nuanced.
For example, if a user resets
their location and privacy data,
their location and privacy
setting, it is so important for
you to make sure you're honoring
that, and it is your
responsibility to make sure you
do that.
And the best way to do this is
to treat HealthKit as the source
of truth because HealthKit will
update to reflect that
authorization status, and it is
so important to make sure you
don't cache any of that and make
sure you're always turning to
HealthKit to ask what data you
have access to.
And these 2 ideas can be
distilled into 3 simple rules.
And when we're thinking about
privacy and authorization, you
should ask for only what you
need, only when you need it, and
every single time you need it.
And in code, this is just as
simple.
Let's say I'm creating a workout
app, and I'm just getting
started, and, of course, I need
to start with privacy and
authorization.
First, since I'm creating a
workout app, I want to share the
workout type, so I explicitly
declare that I'm going to be
sharing the workout type.
Next, since I'm just getting
started, I only have a few
things that I'm trying to track.
I want to be able to track my
user's heart rate, calories, as
well as distance walking and
running.
And this is all I need right
now, and so that's all I'm going
to ask for.
And lastly, on my healthStore, I
request authorization for just
that.
I request my types to share and
my types to read.
And with these 3 simple steps,
we can really make sure that we
are taking the steps necessary
to respect our user's privacy.
And privacy is so important
because great apps have great
privacy, and it's so important
that you give attention to this
at the beginning of any
development process.
And now that we have access to
our users' data, I'm so excited
to introduce Karim [phonetic] to
tell you all about the brand-new
workout features.
[ Applause ]
>> Hello, everyone.
Thank you so much for joining us
today.
I am really excited to tell you
all about the new workout API.
If you're new to HealthKit,
you'll see just how easy it is
to create a full-featured
workout app from scratch.
If, on the other hand, you have
a workout app on the App Store,
we'll show you all of the great
new features of the API that
your app can benefit from.
So let's dive in.
First, I would like to take a
look at the general workout app
life cycle.
So let's say we're building a
workout app to track our users'
activity, and the first step is
to set up the app.
So at this state here, we know
that the user wants to perform a
workout, the type of activity,
and we need to make sure that
our UI is ready for that.
Once the setup is done, we can
go ahead and start the workout.
So after this point, the user
will be actively working out.
And then, of course, after some
time, the user will want to end
the workout.
And from there, we can save this
workout and all of its
associated data in HealthKit.
So this is the general life
cycle of a workout app.
Now, let's see what happens when
the user is active.
With your user interface at the
center, one of the tasks that
you want to accomplish is to
collect data that is relevant to
the workout and displayed in
your user interface.
And you also want to make sure
that the user can control the
state of the session so that the
user can pause or resume the
workout.
And if you're familiar with
HealthKit, you might know about
HKWorkoutSession on watchOS.
HKWorkoutSession allows you to
handle a few of these steps in
the life cycle.
It will prepare the device's
sensors for data collection set
so that you can accurately
collect data that's relevant to
the workout like calories and
heart rate.
It will also allow your
application to run in the
background when the workout is
active.
You'll be, also be able to use
HKWorkoutSession to control the
state of this, the workout.
And finally, you can also
generate events like swimming
laps in segments.
But of course, this is not
enough, and you also need to
collect data that is relevant to
the workout that's generated by
the device and be able to save
it all in HealthKit.
And to do just that, I'm really
happy to introduce a new class,
HKWorkoutBuilder.
The workout builder is your
one-stop shop to collecting data
and saving it in HealthKit.
So the builder will create and
save an HKWorkout object, which
is a representation of a workout
in HealthKit.
You can then add samples,
events, and also custom metadata
to the builder while building
the workout.
And if you're on watchOS, you
can use its subclass,
HKLiveWorkoutBuilder.
So it's watchOS only, and
because it's a subclass of the
workout builder, it has all of
the great benefits of the
builder, but because it's on
watchOS and it works closely
with HKWorkoutSession, it has
all of these really cool
features like automatic sample
and event collection, which
we'll talk about later.
So let's go back to the workout
app life cycle, and we'll see
how we can now set up and start
the workout using the new
workout builder.
So first, we need to create a
workout builder.
And to do that, we use the
initializer, and we have to pass
a healthStore object, a
configuration which contains
information like the workout
type, whether it's indoor or
outdoor, and other information.
And finally, an optional device
if the data comes from an
external device, for example.
Once my builder is created, all
I need to do is call
beginCollection and pass a start
date.
It's that simple.
If you are on watchOS, you can
use the live workout builder.
And so first, you need to create
a workout session by passing the
healthStore and
workoutConfiguration, and then
you do not create the builder
yourself, but you retrieve it
directly from the session by
using this call.
Once we have the session and
builder, we can go ahead and
start them up.
And so it's as easy as calling
startActivity on the session and
beginCollection on the builder
by passing a start date.
So this is how you set up and
start a workout using the
builder.
Now, let's see how we can
collect data displayed directly
in our user interface and be
able to control the state of the
workout when the user requests
it.
So to collect data, if we want
to add samples that are relevant
to the workout, like calories,
or distance, or heart rate, we
can do that using a very simple
call, builder.add, and give it
an array of HKSamples.
If we have events that we want
to add to the workout, it's very
similar.
We just call builder
.addWorkoutEvents.
For metadata, you guessed it.
With the dictionary of metadata,
we just call builder.addMetadata
and pass the dictionary.
But on Apple Watch, because the
device is on your wrist and has
all this wide range of sensors,
you can actually generate data
like calories and distance.
And wouldn't it be cool if we
could have all of this data
automatically collected by the
builder?
Starting with watchOS 5, we now
have automatic data collection,
and it all starts with
HKLiveWorkoutDataSource.
So the data source will collect
samples that are relevant to the
workout currently running.
You do, however, have the option
which types you want to collect,
so you can add or remove types
as you wish.
And this is how it works.
First, we create a data source.
And here, I pass the healthStore
as well as the
workoutConfiguration.
And since I'm passing the
configuration that contains
information about the activity
type, the data source will be
able to infer which types should
be collected for this workout.
Next, I simply need to set the
data source on the live workout
builder.
And optionally, you can add or
remove types that are being
collected.
So let's say I want to add the
type.
I can specify which type I want
to add here as well as an
optional predicate if I want to
further limit which types are
being collected.
And finally, we just call
dataSource.
collectStatistics for and pass
the type as well as the
optionalPredicate.
So now that we have data being
collected, let's see how we can
display it directly in our user
interface.
So every time new data comes in
the builder's delegate method,
workout builder did collect data
of collected types will be
called.
And so here, if, for example,
I'm interested in the heart rate
type, I will make sure that it's
one of the collected types.
And then, I simply make use of
the builder's statistics for
quantity type method, which
gives me an HKStatistics object
that will contain information,
in this case, like minimum,
maximum, average, as well as
most recent value for the heart
rate that's recorded throughout
this workout.
From there, I can simply update
my user interface.
One thing that's also very
common in workout apps is to
have an elapsed timer.
And so, of course, we start the
timer at the beginning of the
workout, but that's not enough
because pause or resume events
will affect duration of the
workout.
So every time new events are
added to the builder, the
builder's delegate method
workoutBuilderDid CollectEvents
will be called.
And from here, you can simply
use the builder's elapsedTime
property, which will give you
the current elapsed time for the
workout.
It's that easy.
Next, let's see how we can
control the state of the
workout.
So first, I'd like to talk about
all of the different states that
the workout session can be in.
And the first one is Not
Started.
So here, the workout has not
started yet, and we're not yet,
the user is not performing the
workout.
So after that, we'll be moving
to the Prepared state.
And so the Prepared state will
put the system in Session Mode,
which means that your
application will have background
running, the sensors will also
be ready to track the activity,
but the workout has not started
yet.
So if you, for example, have a
countdown in your app before a
workout, you can move the
session to the Prepared state
before that to make sure that
the device is ready to start
tracking the activity.
After that, we can move to the
Running state.
And here, the user is actively
working out.
And of course, the user can move
to the Paused, back to Running,
and back and forth between these
2 until the workout is Stopped.
So here, the device is still in
Session Mode, which means that
your app is still running in the
background, so that gives you
some extra time that, if you
needed to save the workout.
After that, we can simply end
the workout.
And from there, the system will
exit Session Mode.
And to be able to move between
all these states, you simply
need to use these calls directly
on the workout session.
So that's how we can collect
data displayed in our user
interface as well as control the
state of the session.
Let's see now how we can end the
workout and save it in
HealthKit.
So to end the workout, you first
call session.end, and then you
also call builder.
endCollection and pass the
workout's end date.
At this point, no data is going
to be collected for this
workout.
And then, if you want to save
the workout in HealthKit, you
just call builder.
finishWorkout, and you will get
back a workout object in the
completion handler that's
already saved in HealthKit with
all of its associated data.
So that's how you now build a
workout app using the new API.
And now, I'd like to show you
how this all works in a demo
where we will be building a
workout app for Apple Watch.
[ Applause ]
So I'm actually currently
building a workout app that will
allow my users to track running
workouts.
And it's a very simple app.
I have a big Run button here.
When I tap on it, I'm presented
with this user interface that
will show an elapsed time of
their workout as well as some
metrics, like calorie burned,
the most recent heart rate, as
well as the distance that's run
in the workout.
Of course, my user will have the
option to control the state of
the workout, so my user can
pause, resume, and end the
workout.
So I already went ahead and
implemented the user interface.
So all that's left for me now is
to use the new workout API and
make my app functional.
And the first thing that we need
to do here is make sure that the
project is properly set up.
And so to do that, I'm going to
head over to my Project
Settings, go to the Capabilities
tab, and here just make sure
that the HealthKit Capability is
turned on.
And you should also do the same
for your WatchKit app extension.
So again, make sure that the
HealthKit Capability is turned
on.
Once I did that, we should also
add the 2 purpose strings in the
info.plist file that will tell
my users why I need access to
their health data.
And so here, the first string is
"health share user description"
that tells my users why I would
like to save data in HealthKit
as well as help update usage
description.
That will tell them why I need
to read data in HealthKit.
Once that's done, I can now
start using the new workout API.
And of course, the first step
before being able to use the API
is to make sure that I request
authorization for the data that
I need.
And so, of course, every app is
different.
And in my case, if we go back to
my application, I will be saving
a workout here.
So that's one of the types that
I will be needing access to.
And I also need to be able to
read calories, heart rate, as
well as distance.
So let's do that.
And of course, we need to make
sure that we always ask for
authorization when we need it.
In my case here, I want to do
that every time the user is
presented with this UI so that I
can make sure that I have the
authorization before starting a
workout.
So this view here is backed by
my workoutStartView Watchkit
interface controller, and I'm
going to go ahead and implement
the authorization in the
didAppear method.
So first, my typesToShare here
is workout because I want to be
able to save a workout at the
end.
Next, I would like to read
heartRate, activeEnergyBurned,
as well as
distanceWalkingRunning.
Once I have my typesToShare and
typesToRead, I just need to call
requestAuthorization on the
healthStore and pass the types.
So let's go ahead and run this
code now and see what happens.
And because the watch screen is
small, the user will be
presented with an authorization
sheet on the phone.
So you need to make sure to also
handle authorization on your
iPhone app.
Going to go ahead and dismiss
this view here.
And so let's open my iPhone app.
And here, I'm presented with an
authorization sheet where I can
decide if I want to grant this
application access or not.
For the purpose of this demo,
I'm going to turn all the
categories on and tap Allow.
And that's how we have now
granted the authorization to my
application.
Next, we can now finally get
started with the workout API.
So when I tap on this button
here, this view will be passed,
all the workoutConfiguration
object that contains information
about the workout.
In this instance here, it's a
running workout.
And from there, I can go ahead
and set up my workout.
So this view here is backed by
my workout session, WatchKit
interface controller, and I'm
going to go do the setup in the
awake method.
So first, I'm going to create
the HKWorkoutSession as well as
the HKLiveWorkoutBuilder.
So we create the session using
its initializer, and we pass the
workout configuration to it.
From there, we simply retrieve
the builder directly from the
session.
Creating a session might fail if
the workout configuration is
invalid, so that's why I wrap my
code here in a do-catch block
and dismiss if I have any
failures.
Next, let's set up the session
and builder.
So in this case, I want my
interface controller to be the
delegate of both the session and
the builder.
And because I also want to
collect samples generated by the
device, I'm going to use here a
live data source and pass it a
workout configuration so that
the types are automatically
inferred for me.
Finally, I simply start the
session as well as the builder.
So now that my workout is
started, the first thing that I
need to do is to start my
elapsedTime timer.
And so I'm going to do that here
in the callback of the
beingCollectionCall.
I have a method here that will
do that for me.
And of course, because pause and
resume events can also affect
the duration, I'm going to have
this call in the builder's
didCollectEvent call as well.
Now, let's implement this
method.
So in my UI, I'm using a
WatchKit interface timer.
If you're not familiar with this
object, this object needs a date
in the past from which it will
start counting from.
So I'm going to go ahead and
create my date object using the
builder's elapsedTime property.
And because I want the date to
be in the past, I'm going to
make sure that I have a minus
sign here.
Next, I simply dispatch on the
main queue because I'm doing UI
work, and then I just set the
date on the timer.
I also need to make sure that
the timer is only running when
the session is running as well.
So let's do that.
First, I grab the session state,
and then, again, I dispatch on
the main queue.
And if the session is running,
I'm going to start the timer.
And if it's not, I'm going to
call Stop on the timer.
And that's all you need to do to
get the timer tracking the
elapsed time.
Next, let's make sure that our
metrics displayed in the UI are
also accurate.
So every time new samples are
collected by the builder, this
method here will be called.
And so here, I'm just going to
go and iterate over the
collectedTypes.
And, in my case, all of the data
that I'm interested in is
quantity samples.
So I'm going to make sure that I
only handle those.
And from there, we can make use
of the new builder's method,
builder.statistics, for quantity
types, which will give me an
HKStatistics object that
contains information like
minimum, maximum, average, and
most recent values for each
type.
So I grab the statistics object
here, and from there, I have a
method that I already
implemented that will give me
the user interface label for
this particular quantity type.
And from there, it just update
my label using the statistics
object.
Next, let's make sure that we
can control the state when the
user requests it.
So every time my user will tap
the Pause button, this method
here will be called.
And from there, I simply need to
call session.pause.
For resume, it's very similar.
I'm going to do session.resume.
And finally, to end the workout,
we're going to call session.end
and then builder.endCollection
and pass the workout's end date.
And from there, we're just going
to call finishWorkout to
actually save the workout and
all of its associated data in
HealthKit.
And then, we just dismissed it
here.
So let's go ahead and run this
code and see what happens now.
So I'm going to go ahead and
start my workout here.
And as we see now, I have my
timer updating.
I have data directly collected
by the builder and displayed
right in my user interface.
And of course, I can also react
to the state changes, so going
to go ahead and pause the
workout.
My timer has paused.
Data collection as well.
I can resume.
And just like that, with a few
lines of code, I now have a
fully functional workout app.
[ Applause ]
So I'm going to stop the workout
now, and let's see what we have
just saved in the health app on
the iPhone.
So this is my workout here that
I have just saved, and we have
now a very rich representation
of this workout saved directly
in HealthKit.
I have the device details as
well as all of the samples that
were associated, like heart
rate, distance, and active
energy, and, also, the workout
events that were generated when
I paused and resumed the
session.
And that's how easy it is now to
build a full-featured workout
app on Apple Watch.
[ Applause ]
But of course, sometimes things
don't go as planned.
Let's say I'm running my first
marathon and I'm really excited
about it and using my favorite
workout app to track this
marathon, but I only realize at
the end that the app actually
crashed sometime in the middle,
making me lose all of this data.
Well, with watchOS 5, we are
introducing workout recovery
that will solve that problem for
you.
[ Applause ]
If your application happens to
crash during an active workout,
we will automatically relaunch
it and give it a chance to
recover the workout.
The workout session as well as
the builder will be recovered in
their previous state, so you
should not call startActivity or
beginCollection on the builder.
If you are using a data source
to automatically collect data,
you do need to set it up again,
and this is how it works.
If your app is relaunched after
a crash, we will be calling the
WatchKit extension delegate
method, handleActive
WorkoutRecovery.
From there, you simply need to
create a healthStore object and
then call recoverActive Workout
Session, and you will get back a
session in the completion
handler.
It's that simple.
So this is our new workout API.
It makes it easier than ever to
build great workout apps.
We also have a new Quantity
series API.
And for that, I'd like to hand
it back to my colleague Niharika
to tell you more about it.
[ Applause ]
>> Isn't that incredible?
In less than 10 minutes, Karim
was able to build a fully
functional workout app.
And this is so important
because, like I mentioned
earlier, our users love doing
workouts.
And with these workouts come a
lot of data.
Let's say I'm interested in
making a soccer app.
I really want to understand
exactly how my users are moving,
and I want to track the distance
they're moving as they go side
to side, across the field during
the duration of a workout.
And what happens when we start
the workout is that samples
start coming in.
My user first goes 2.25.
They then go 1.6.
They go a little more.
And then, they finish it off.
And in the past, each one of
these distances would be stored
as 1 individual HKQuantity
sample.
Each one would be independent,
and they would be stored
separately.
And so we thought that, wouldn't
it be great if you could
actually just have 1 sample that
tracks that cumulative total but
is still backed by those
individual quantities behind it?
And that's why we've introduced
HKCumulative
QuantitySeriesSample, which is a
brand new sample type that
allows you to more efficiently
store high-frequency data, and
it's great for 2 reasons.
Number 1, you only have to store
1 sample that is backed by
multiple quantities, and so
you're more efficiently storing
the high-frequency data that
comes from things like workouts.
And number 2, you now have a
sense of connection between the
quantities that make up a
sample.
So our new quantity series
sample is a subclass of
HKQuantitySample, which some of
you may already be familiar
with.
And if you've interacted with
HKQuantitySample before, our new
sample type will be very much
the same.
So why would you want to use
this?
Let's say you're interested in
data visualization.
Your app requires complex
charting or graphs, and you're
trying to really visualize the
data that your users have in a
beautiful way.
We actually suggest that you
continue using our
HKStatisticsQuery or our
HKStatistics CollectionQuery.
And that's because those are
preexisting queries that do just
this.
You have access to really
fine-grained, rich data, and
these queries will already get
the fine-grained data on our new
series samples, so you won't
have to do any extra work to get
those details.
If, for example, you're
interested in data analysis, you
want to go 1 step deeper.
You want to understand the
actual quantities that make up a
sample.
We suggest you use our new
series sample query.
And lastly, if you're like me
and have a soccer app [laughs]
or any app that has really
high-frequency data collection,
we suggest that you use our new
Quantity series sample builder,
which is a brand-new way for you
to actually be able to create
those new cumulative Quantity
series samples and store that
data in a more efficient way.
So let's start with our sample
query.
Like I mentioned, this is a way
for you to be able to really
look deeper and understand the
individual HK quantities that
make up a Quantity series
sample.
And in code, we, of course,
first want to start with
figuring out where we're going
to store our new quantities.
In my case, I'm going to store
it in an array of quantities.
We next want to initialize our
query.
We initialize it with the sample
that we're interested in.
And in our completion handler is
where the bulk of the work
happens.
In my case, I have a method
called analyzeQuantity which is
going to do the work that I'm
interested in with the quantity.
In my case, that's adding it to
the array of quantities that I
declared earlier.
And lastly, we execute our
query.
It's really that simple.
For those of you familiar with
some of our other queries, this
behaves in a very similar way.
Next, we have our Quantity
series sample builder, which,
like I mentioned, is really
great for apps that do have
high-frequency data ingestion
because it's a more efficient
way to store that data but also
allows you to kind of create
connections between that.
So first, we, of course, want to
start with creating our sample
builder.
We initialize with the
healthStore and we initialize it
with the quantityType that we're
interested in.
Next, for whatever cadence
you're interested in inserting
samples, you do that.
Let's use my soccer app as an
example.
I am super interested in the
actual distances that my users
are moving, so I'm going to
declare the meter unit as the
quantity that I'm interested in,
and I insert that into my
seriesBuilder.
And at the end, we call finish
on my series, and we're actually
going to get the new Quantity
series sample and be able to
have stored that in a more
efficient way.
And so we're really, really
excited for you to start
implemented our new Quantity
series APIs in your apps because
it's a brand-new way to store
your data more efficiently, and
its ability to be able to relate
data together really allows for
some interesting analysis.
So we have talked about a lot of
interesting things, and we think
that the best way to think about
them is actually as parts of a
whole because together they
really make up a way for you to
respect your users' user
experience from start to finish.
Of course, we have to start with
respecting user privacy.
Our users are giving access to
one of their most sensitive
data, and it is so important for
us to make sure that we're
treating it with the respect
that it deserves.
Next, with the brand-new workout
features Karim mentioned, we're
able to create seamless user
experiences.
With things like workout crash
recovery, we're able to really
deliver an experience to our
users that they expect.
And lastly, by implementing our
new Quantity series APIs, you're
respecting the device's
capabilities and really making
sure that, from start to finish,
you are giving your users the
best experience possible.
And as the health and fitness
space and as workout apps just
keep growing and users keep
using them more, we're so
excited to see how these steps
come together to create really
incredible experiences.
So for more information, please
check out our developer
documentation, but we'd actually
love to see you in person today.
We're going to be at a lab today
from 1 to 3 as well as our
Health, Fitness, and Research
Get-Together later this evening,
and we'd love to answer any of
your questions, and hear your
stories, and meet you in person.
We also want to mention that we
have a brand-new features in
HealthKit, and we'd love for you
all to check out their talk,
Accessing Health Records with
HealthKit.
So thank you so much.
We're so excited to see what you
all do with this.
Thank you.
[ Applause ]