Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
>> Hello everyone.
Welcome to Introducing
HealthKit.
My name is Justin, I'm an iOS
engineer and I'll be joined
up here in a little while
by my colleague, Siji.
Siji and I got to work
together on HealthKit
and we're really excited
to show it to you today.
So, there are a ton of
health and fitness apps
on the app store today.
They do all sorts
of awesome things
that let our users get
healthy and stay healthy.
The problem is not a lot
of these apps can share
data between each other.
This makes it really hard for
our users to get a cohesive view
of their current health.
The few apps that can share
data have to write all sorts
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The few apps that can share
data have to write all sorts
of custom code to do so, which
means you spend your time
and energy working on
features that aren't core
to the experience of
your applications.
Now, we recognize that
different applications have
different needs.
The way we see it
they can be grouped
into three main categories.
First, there are applications
that perform statistical
analysis using graphs or trends.
Next there are applications that
let users enter information.
Last, there are applications
put out by health providers
that let users sync data with
their personal medical record.
These are three very different
scenarios but we built HealthKit
so that it can handle all
of them and we're going
to show you how it
does that today.
Throughout this talk we're going
to refer to the health app.
This is talking about the
new health application
that we're introducing
with iOS 8.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that we're introducing
with iOS 8.
So here's our agenda for today.
We're going to start
off by walking you
through the HealthKit API so you
can learn how to create data,
how to save data and how
to ask for data as well.
Then Siji is going to come
up here and give you a demo
so you can see how to
incorporate HealthKit
into your own applications.
We'll finish things off with
some HealthKit best practices.
HealthKit is a framework
that lets you store
and retrieve health and
fitness information.
So data is really important.
We're going to start
off by talking
about what data is in HealthKit.
Storing health information in a
way that can scale to work all
across the world has presented
some challenging problems.
Every time you step on your
scale, your weight may be pounds
in the United States,
stones in England
or kilograms in many
other areas.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We didn't want you to have to
do unit conversion yourself
so we handle unit conversion
for you right in HealthKit.
Units are handled by HKUnit
and HKUnit represents a
particular unit in the metric
or imperial unit system.
These can be simple such as
grams or they can be complex
like milligrams per deciliter.
Base units are classified
into types.
Types define which units are
compatible with each other.
For example, a mass units
value can be converted
to any other mass unit.
Here you can see that milligrams
per deciliter is an example
of a mass per volume unit.
We give you a couple of
different ways to create units.
First, you can specify
them explicitly in code.
Here we're creating a gram unit
and then we're creating
a deciliter unit
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and then we're creating
a deciliter unit
and we're combining the two to
make a grams per deciliter unit.
This works really
well for simple units
but as you can see it
gets pretty unwieldy
as we expand to more
complex ones.
To make things easier on you,
we let you specify units
using unit strings like so.
So this line will
create an identical unit
to the three lines above it.
Once you have your unit, the
next thing you're going to want
to do is create an HKQuantity.
An HKQuantity is a double value
relative to a particular unit.
What's really cool about
quantities though is
that they can be used to
do unit conversion for you.
Let's take a look
at how they do that.
Here we start off by making
a quantity of 20 grams.
Next we can ask for its
double value in kilograms.
This will give us back .02.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This will give us back .02.
Unfortunately, not all
units play nicely together.
If you ask a quantity
for its value
in a unit it's not
compatible with,
we're going to throw
an exception.
You can always guard yourself
in code by asking a quantity
if it's compatible
with a particular unit.
So return a BOOL.
So now that you have your
quantity the question you should
be asking yourself is what
is this a quantity of?
To answer that question
we're going
to take a look at object types.
Object types represent all of
the different kinds of data
that we can store in HealthKit.
There are over 60
different types.
Major groups of types
are organized
into their own classes.
All of these classes
inherit from HKObject Type.
Characteristic types are
characteristics of the user
or traits that don't
change over time.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or traits that don't
change over time.
These are things such as their
blood type or date of birth.
Sample types on the other
hand are traits of the user
that do change over time.
As such, they can be sampled
at a particular point in time.
One kind of sample
type is HKQuantityType.
These are the kinds of data
that can be represented
by an HKQuantity.
They are by far the most common
kinds of data in HealthKit
and represent everything from
blood pressure to potassium
to the number of
steps you've taken.
Last we have HKCategoryType.
Category types describe the kind
of data that can be categorized
into an enumeration of values.
An example of category
type is sleep analysis,
which is how we store
sleep in HealthKit.
Let's talk about how you
would create an instance
of an HKObject Type.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of an HKObject Type.
Every type has its
own type identifier.
You cannot create your own
types or type identifiers,
but it's important to understand
how they're structured
so that you can use auto
complete to your advantage.
Here we have our
identifier for heart rate.
The first part of
the identifier is
which object type
subclass it represents.
As you can see, heart
rate is a quantity type.
The second part of the
identifier is the type name
or what you would call the type.
Once you have your type
identifier, you can pass it
to one of these three
constructors on HKObjectType.
It's important to note
that your identifier
and your constructor
need to match up.
So if you were to pass a
category type identifier
to quantity type for identifier,
you're going to get back nil.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to quantity type for identifier,
you're going to get back nil.
So, now we have a quantity
and we have an object type.
It sounds like you're
ready to create some data.
All data stored in HealthKit
is a subclass of HKObject.
Here we have a diagram of
the HKObject class hierarchy.
You might notice this
is pretty similar
to the hierarchy we just
say for HKObjectType.
This is by design
as object types
and objects are very
closely tied together.
Let's take a look at some
of these different classes.
We'll start with
HKQuantitySample.
This is by far the most common
kind of HK object in HealthKit.
They're how we store
quantities in the database.
Every quantity sample
has a quantity type.
This is what kind of data
the sample represents.
They also have a quantity, which
is the value of the sample.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
They also have a quantity, which
is the value of the sample.
It's important to note
that your quantity's unit
and your quantity
type need to match up.
Every quantity type expects
a certain kind of unit
and if there's a mismatch here
then we'll throw an exception.
Next we have HKCategorySample.
Category samples are very
similar to quantity samples.
They have a category type,
which again describes what kind
of data the sample represents
and they have a value.
Now, remember the category
types are the kinds of data
that can be categorized into
an enumeration of values.
As such, every category
type is paired
with a corresponding enum.
This value property must be one
of the members of that enum.
If an invalid value is provided,
we throw an exception just
like we do for quantity samples.
Both of these inherit
from HKSample.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Both of these inherit
from HKSample.
Remember that samples
are the kinds of data
that can be sampled at a
particular point in time.
To support this every
sample has both a start date
and an end date.
Why both a state date and
end date you might ask?
Well, for some kinds of data
such as your weight the
reading is pretty instantaneous
so your start date and end date
should probably be the same.
For other kinds of data though
your reading might take place
over a period of time.
You might want to say that
your heart rate was an average
of 80 beats per minute over
the course of 30 seconds.
Every sample also
has a sample type.
This is going to be the
same as your category type
or quantity type depending
on which subclass you
choose to implement.
All of these inherit
from HKObject.
Every object has a UUID
which is a unique identifier
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Every object has a UUID
which is a unique identifier
that has persisted for the
lifetime of the object.
So you can count on this
always being the same.
They also have a source.
Because health information is so
sensitive we wanted to make sure
that every object could
identify where it came from.
Each application
represents its own source
but sources can also
be pieces of hardware
such as a Bluetooth
heart rate monitor
or the step tracker
in your phone.
Metadata is an important part of
the extensibility of an object.
You can create an object with
whatever metadata you like.
Note that metadata is a regular
NSDictionary except it can only
store strings as
keys and strings,
numbers and dates as values.
We give you a few
predefined metadata keys
which we think might be
useful but we encourage you
to create your own
metadata keys as well.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to create your own
metadata keys as well.
Now you may have
noticed that all
of the properties you
saw here are read only.
This is because all data
in HealthKit is immutable.
As great as it would be to
go back and change my height,
it really doesn't
make any sense.
So, you can create
objects using constructors
on HKQuantitySample
and HKCategorySample.
This is an example of how
you might create an object
to represent someone's
body temperature.
First we create our
quantity type,
which in this case
is the quantity type
for body temperature.
Next, we create our quantity.
Let's say this person is
feeling pretty healthy today
so their temperature is
98.6 degrees Fahrenheit.
Last we create some metadata.
This piece of metadata says
that the person probably took
their body temperature using an
ear thermometer.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
ear thermometer.
We can then pass all of
this to HKQuantitySamples,
quantity sample with type
and give it a start date
and end date of right now.
Now that you have your data,
what do you actually do with it?
Well, to really do anything
with HealthKit the first thing
you need is an HKHealthStore.
You can think of
your Health Store
as your link to the database.
It lets you save
objects and as we'll find
out in a little bit it lets
you ask for data as well.
Remember though that you really
only need one Health Store.
They should be long lived and
you don't really get anything
out of having multiple
ones floating around.
So create one, hold on
to it and you're done.
Here's an example of us
saving some data to HealthKit.
First, we create
our Health Store.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
First, we create
our Health Store.
Next, we create a
quantity sample.
We can then pass our sample
to our Health Store using
safe object with completion
and voila we have officially
saved data in HealthKit.
So, saving data is great
but it's not really any
fun unless we can ask
for it back, right?
HealthKit gives you lots
of ways to ask for data.
The easiest information to get
are characteristics of the user.
Because these don't change
over time we really only care
about the current value.
So you can just ask your
Health Store directly.
Here's an example of us
asking our Health Store
for the user's date of birth.
This doesn't work for everything
though so to ask for the bulk
of our data we're going to
need something a little more
heavy duty.
This is where queries come in.
Queries provide a structured
mechanism for you to look
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Queries provide a structured
mechanism for you to look
at health data in
a variety of ways.
All queries inherit
from HKQuery.
An HKQuery has a sample
type which is what kind
of data is being queried for.
They also have a predicate.
Predicate is an NSPredicate
that filters which objects are
to be returned by the query.
You can create predicates in
a couple of different ways.
First, you can use regular
NS predicate constructors
which you may already
be used to.
We give you a constant
for every key path
that you can use
with predicates.
This is a really powerful tool,
but we wanted to make it easier
on you to create the predicates
that are most commonly used.
So we provide some convenience
constructors right on HKQuery.
Note that these two
methods are going
to create identical predicates
so you can use whichever method
you feel more comfortable with.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, let's say that
you're working
on a brand new application.
This app is for a fancy
new health tracker
that can measure the
user's blood sugar
and sync it to their phone.
Your app already saves
all its data in HealthKit.
Now you want to create a screen
that will show the user their
most recent blood sugar reading.
You can do this with
HKSampleQuery.
Every sample query has a limit.
This defines how many
objects are going
to be returned by the query.
If you don't want to
limit your results,
you can specify
HKObjectQueryNoLimit.
They also have a sort order.
This is an array of
NSSortDescriptors
that dictates the order
for which you will
receive your result.
Here's an example of us
creating a sample query
to get our most recent
blood sugar reading.
First, we create
our quantity type,
which in this case
is blood sugar.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Next we create our
sort descriptor.
Because we want the most recent
we want to order our results
in end date in descending order.
When we create our sample query,
we admit it with a sample type
of blood sugar, a nil
predicate because we don't want
to filter our results,
a limit of 1
because we want the
1 most recent
and an array containing the
sort descriptor we just created.
In our result handler because
we know there's only going to be
at most 1 result, we ask our
results for the last object.
That worked pretty
well but you realized
that every time a
new reading comes
over from your health
tracker, nothing happens.
It will be really great if every
time you received a new reading
the display would just
update all by itself.
You could create some kind
of pull mechanism to do this
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You could create some kind
of pull mechanism to do this
where every 30 seconds or so you
checked what the most recent is,
but it would be a lot nicer
if you could just be alerted
when things actually change.
This is the job of
HKObserverQuery.
Observer queries
are very simple.
All they do is watch for
changes in the database.
They are a little bit different
than sample queries in the sense
that they're long running.
So your update handler is going
to be called every time
a new object is saved
or removed from the database.
You might notice that in
your update handler there's a
completion handler.
This is for a feature that
we won't have time to go
into today called
background delivery.
If you have any questions
about background delivery,
please come by one of our labs.
Here's us creating
an observer query.
All we do is tell it that we
want all blood sugar readings
and our update handler will be
called every time a new blood
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and our update handler will be
called every time a new blood
sugar reading is saved or
removed from the database.
Now, let's say that your
health app, that your app,
is partnering with
a health provider.
What it's supposed to do is take
all the blood sugar readings
that its created and
sync them to the Cloud.
You could do this using a sample
query where every time you query
for all the objects in
the database and figure
out which ones you haven't seen
yet probably using their UUID,
but this is a lot of work and
it would be a lot nicer if,
and it would be a waste
to pull all of the objects
from the database just to find
out that nothing has
actually changed.
This is the perfect job
for HKAnchoredObjectQuery.
An anchored object
query has a limit,
which again defines
how many results are
to be returned by the query.
It also has an anchor,
but what's an anchor?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It also has an anchor,
but what's an anchor?
Well, behind the scenes
every object stored
in HealthKit has its own
anchor in the database.
Your query anchor says
that you want everything
after a certain point.
So, if you're to provide
a query anchor of 0,
you would get back everything.
If you were to provide
a query anchor of 3,
you would only get back
objects 4, 5 and 6.
So in creating an
anchored object query,
your anchor should
represent the last piece
of data that you've seen.
If you haven't seen any data
yet, then you can specify 0,
which means you don't
have an anchor.
In your call back, you
will receive a new anchor,
which you can then use
in subsequent queries.
Here's an example of us creating
an anchored object query.
At some point in time probably
during our initializer we set
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
At some point in time probably
during our initializer we set
our last anchor to be 0.
When we create our query,
we tell it we want all
blood sugar readings
that have occurred
since our last anchor
and we don't want to
limit our results.
In our completion handler, we
will get our results as well
as a new anchor, which we can
then use in our next query.
Let's see how this would
work with some actual data.
Let's say our database
has 3 objects in it.
Since I haven't seen anything
yet we'll say my
current anchor is 0.
When I run my query, I'll
get back objects 1, 2 and 3
and my current anchor
will now be 3.
Next, 2 objects are
added to the database.
When I run another query,
I'll get back objects 4 and 5
and my current anchor
will now be 5.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and my current anchor
will now be 5.
So, I've shown you a
few different queries
and some situations that they're
useful in but we haven't talked
about how to actually
run your queries.
This is done using two
methods on HKHealthStore,
executeQuery and stopQuery.
ExecuteQuery tells a
query to begin running.
At any point in time
you can call stopQuery
which will kill your query
and prevent any callbacks
that haven't happened yet.
You can call stopQuery as
many times as you want,
it doesn't matter, but you
can only execute a query once.
This is because once a query
is stopped its callbacks are
invalidated to prevent
retained cycles.
Remember, though
that you only need
to stop long running queries
such as the observer query.
Other queries know to stop
themselves once they received
their initial data.
Now, all of the queries
that we've talked
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, all of the queries
that we've talked
about so far let you get actual
objects from the database,
but oftentimes we don't care
about the individual
objects themselves.
What we really care about
are statistics on our data.
You could use a sample
query and iterate
over all your results summing
their quantities together
but this would be a lot of
work and a lot of objects
in memory considering all we
really care about is the sum.
To support operations like this
statistics are a first class
citizen in HealthKit.
Statistics are handled
by HKStatistics.
An HKStatisticsObject is an
aggregation of multiple kids
of statistics such as
sum, min, max and average.
You can ask for statistics
across all data or only
for statistics that came
from a particular source.
Because we're talking
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Because we're talking
about numerical analysis
statistics are only valid
for quantity types.
Remember though that not
all types are the same.
We care about different
statistics depending
on what kind of data
we're looking at.
We classify types as either
discrete or cumulative.
Discrete types are
things like your height,
weight or blood pressure.
They're the kinds of data where
an individual sample has all
of the context you need to
make sense of its quantity.
The only statistics we care
about for discrete types
are min, max and average.
If I were to take all
of my weight readings
and add them together,
I wouldn't get any useful
information whatsoever.
Cumulative types are things like
your step count or the number
of calories that you've burned.
In contrast to discrete
types, I really don't care
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In contrast to discrete
types, I really don't care
about an individual sample.
What I care about is an
aggregation of samples
over a period of time.
For example, that I
took 7,000 steps today.
So the only statistic we care
about on cumulative
types is sum.
So every quantity
type is classified
as either discrete
or cumulative.
In our type identifier's header,
every type identifier is listed
with its aggregation style.
If you want to check
in code though,
HKQuantityType has an
aggregation style property
that will return either
cumulative or discrete.
Back to statistics.
As you may or may not
know, generating statistics
on large amounts of data
can be rather expensive.
Because of this we had you
tell us what you want ahead
of time using a bit mask
of HKStatisticsOptions.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of time using a bit mask
of HKStatisticsOptions.
Average, min, max and
sum each have their own
statistics option.
These are marked as either
discrete or cumulative.
If you ask a cumulative
type for discrete statistic,
we're going to throw
an exception.
A little bit less
straightforward is separate
by source.
This lets you ask in
HKStatisticsObject
for the statistics that came
from a particular source,
but why is this important?
Let's say we have 2 sources,
Source A and Source B.
Both of these sources
are writing step data
to HealthKit at the same time.
Now let's say that I
want to sum up my steps.
I could take all of the
samples in the database
and add them together,
which would give me 41,
which is also wrong.
Because these sources were
saving data at the same time,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Because these sources were
saving data at the same time,
in many cases I was
actually double counting.
To handle this we let the
user specify in the health app
which sources are
most important to them
for a particular quantity type.
We then use this
information to try to figure
out what actually happened.
If you don't like
our merge strategy,
then you can always perform your
own by separating by source.
Now back to your application.
Let's say that your health
tracker just got an awesome
new feature.
Now it can count the user steps.
To support this you want
to have a new screen
that shows the user their
total steps for the day.
This is a sum so you know
you want to use statistics
but how do you actually
generate statistics?
This is done through
HKStatisticsQuery.
Statistics queries take a bit
mask of statistics options
and return a statistics object.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So here's an example of us
creating a statistics query.
First we get our quantity type
which in this case
is step count.
Next we perform some
date calculations
to only return objects
that happen today.
We create our statistics
options,
which in this case is sum,
and then when we create our
query we tell it we want all
steps that happen today and
we want to take their sum
and in our completion
handler we'll be given an
HKStatisticsObject that we can
then ask for its sum quantity.
Well, you feel like a
rock star right now.
Using your UI code from your
blood sugar screen you're able
to bust this out
in like 5 minutes.
With all of that extra time you
decide it would be really cool
if you could show the user their
daily step count throughout
the week.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
How do you do this?
You could execute 7 statistics
queries, 1 for each day,
and once they all
return use their results
to draw your chart.
This sounds like a
lot of effort though.
This is actually the perfect
job for HKStatisticsCollection.
From a high level statistics
collection is just a collection
of statistics objects.
It splits time up into a
series of time intervals
and generates statistics on
each of those time intervals.
Let's take a look at
how this actually works.
In this diagram, each
of our gray boxes represents
its own HKStatisticsObject.
We call our Delta T
our interval components
and this is an NSDateComponents
object.
The reason why we chose
to use NSDateComponents is
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The reason why we chose
to use NSDateComponents is
because when we talk
about health information
in a historical way, we tend
to talk about it in days,
weeks, months and years.
To do this properly, you
need to use date components.
If you're curious about
date calculations,
please see last year's video,
Solutions to Common Date
and Time Challenges.
So, now let's say we have
interval components of 1 day.
The problem is we don't know if
you want your day to be midnight
to midnight, noon to noon or
something completely different.
To handle this we have you
give us an anchor date.
All the anchor date does is
tell you what the edge of one
of your time intervals
is and that's all we need
to create a collection
of statistics for you.
Let's see how this would
look with some actual data.
Once we get your anchor date
and your interval components,
we split up all of time in
the series of time intervals.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we split up all of time in
the series of time intervals.
We can then group data into
these different time intervals
and collect statistics on
it such as ask for it sum.
So, this is what a
statistics collection is,
but how do you actually
interact with this object?
Because we don't force you
to give us a start date
or an end date, there's
potentially an infinite number
of statistics.
The first thing you can do
is ask for an array of all
of the populated statistics.
Populated statistics
are the ones
where their time interval
actually had data in it.
You can ask for the statistics
that occurred on
a particular date.
What this will do is find
out which time period
your date falls into
and give you back the statistics
from that time period whether
or not there was any data.
Last you can enumerate
across all
of the statistics
between two dates.
This will return both populated
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This will return both populated
and non-populated
statistics objects.
So this seems like it's
going to work pretty well.
All you need to do is specify
an anchor date of Sunday
at midnight and interval
components
of one day and that's about it.
So, how do you actually create
a statistics collection?
This is done with an
HKStatisticsCollectionQuery.
These take a bit mask
of statistics options,
an anchor date and an
interval component's object
and give you back an
HKStatisticsCollection.
It's that easy.
Now I'm going to hand
things over to Siji
so that she can show you
how to incorporate HealthKit
into your own applications.
>> Thank you, Justin.
Hey everybody, good morning.
I am Siji Rachel Tom and I'm one
of the engineers on HealthKit.
I'm really excited
to be here today
to give you this quick demo.
So there are 3 things that
you will see in this demo.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So there are 3 things that
you will see in this demo.
First you'll see how you
can leverage HealthKit
to retrieve existing information
about the user from
Health Store.
Now this information
might have been inserted
into Health Store
by some other app.
Second, you'll see
how you can read
and write your own data objects
into Health Store, and third,
you'll see some examples of the
queries that Justin spoke about.
Now for the purpose of this
demo I'm going to walk you
through making a
fitness tracking app.
The goal of this app would be
to track your net energy burn
through a given day
and we are going
to define the net energy
burn as a total energy burn
by activity subtracted by
the total energy consumed.
With that in mind let's go
take a quick look at the demo.
So we know we are going
to be using HealthKit
for our data storage
needs, but before we can do
that we need the user's
permission and this is the code
that you will write to
get the user's permission.
So first you check whether
HealthKit is supported
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So first you check whether
HealthKit is supported
on the current iOS device and
this is because some devices
such as the iPad does
not support HealthKit.
Next you create an
instance of Health Store
and then use Health
Store's authorization APIs
to request read and
shared access
to these different
sets of data types.
Now I have already set up
my device to be authorized,
but the first time you run
this piece of code you're going
to get this dialogue popup that
requests permissions from you.
So now let's look at the app.
Since we are dealing with
user's specific energy burn
calculations, let's assume
that we need some user's
specific statistics as well.
For instance the user's
age, height and weight.
Now I've set up the app
such that the user can enter
these details, but it will be
so much cooler if we can
look at Health Store and see
if these values already exist
and if so just retrieve it
and display it for the
user's confirmation.
So let's go and write
code to do just that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So let's go and write
code to do just that.
A quick note before that I'm not
doing much edit handling code
in this demo but
there is sample code
for this app uploaded online
and that will be a
much better guide.
Now, to get the user's
age, we need birth date.
Birth date is a characteristic
data type in HealthKit
and Health Store has this
convenient synchronous
that directly access
user's birthday.
So let's go ahead and
use that method here
and once we have the
birth date it's simple
to get the user's age.
Notice here I am passing
the age completion block.
Let's run this code and see.
Tah-dah. Age does show up
and how this happened is
because earlier today I
used Apple's health app
to insert my birth
date into Health Store.
Let's look at user
height and weight next.
So, height and weight are
quantity data types in HealthKit
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, height and weight are
quantity data types in HealthKit
and to get quantity samples
out of Health Store we need
to write a sample query.
So let's go ahead and write
our first sample query
in this convenient
method which will give us
like the most recently quantity
sample of any given data type.
So this is our first
sample query.
Now I want a single most
recent quantity sample
of a given data type and
so for that I'm first going
to sort my existing
samples in descending order
and then I'm going to
specify a limit of 1
to get just the first
one out of it.
I don't want to filter the
data so my predicate is nil
and the data type
that I'm interested
in is passed into this method.
Now when this query
returns, I get an array back
and this is an array
of quantity samples.
In our case if there is a
valid quantity sample existing
in Health Store,
then you're going
to get just 1 quantity
sample back.
So what we're going to do is get
the quantity object associated
with that quantity
sample back and then pass
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with that quantity
sample back and then pass
that in this completion block.
So let's go and write
code to that.
So this gets the first
object and passes it
in the completion block.
Very important once you've
written a query is please don't
forget to execute the query.
Okay. Now let's use
this convenient method
to actually get the
user's height.
So let's first go can call
that method we just wrote.
Now user height is a quantity
type with identifier height
and so I pass that in and when
the query returns I get the most
recent quantity object
and as Justin was talking
about earlier a quantity
object is nothing
but a double value associated
with a particular unit.
I want to show my height
in inches and so I am going
to specify HKUnit inchUnit.
Once I have the height
we need to pass it back
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Once I have the height
we need to pass it back
to the view controller who
wants it but note that queries
from Health Store always are
done anonymous background queues
so we need to make sure we
switch back to the main queue
and it should give
us the height.
Now, I have already set up
code to do the exact same
for getting the user's weight.
The only difference is
that the identifier here is
that for body mass and the unit
I'm interested in is pound.
Now let's run this code.
There it works, you guys.
Okay.
[ Applause ]
Let's move on.
So we are interested in
getting the net energy burn,
which we define as
the total energy burn
by activity subtracted by
the total energy consumed.
So let's give the user a
chance to enter details
about the energy consumed.
I've already setup this
app to show a possible list
of food items consumed but
this app doesn't do much else
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of food items consumed but
this app doesn't do much else
right now.
What I want it to be able to
do is when I select an item
and I want to save that item
details into Health Store
and when I come back to
this main screen I want
to retrieve those
details and display it.
So let's go and write
code to do that now.
Again a quick note so food
item is a lot more rich
than just calories but for the
purpose of this demo we're going
to focus on saving just
the calorie information.
So, I'm going to treat each
food item as a quantity sample
and I want to save
the food item's name
and calorie information.
I'll save the name as metadata
and calorie information
as quantity object associated
with that quantity sample.
So let's go create our
first quantity sample.
Now the data type that I'm going
to save this food sample as is
that of dietary calories
and so let's specify that.
The quantity object as we
just mentioned will be the
calorie count.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So it will be kilocalorie unit
and the double value is passed
into this method and
that's our quantity object.
Now I'm just going to
save this food sample
with the current date and time
and as I mentioned earlier the
metadata will have the food name
associated with it.
Notice here that HealthKit
actually provides these
convenient strings to be
used as keys in metadata
and so we're just going to use
the food type key and that's it.
We have officially
created our first sample.
Let's go and save
this into the database
into Health Store
and there, done.
Awesome. So now that we have
saved these items let's write
code to fetch these
items out and display it.
We already know what
we are going to use
to get quantity samples
out of Health Store.
Let's write a sample query.
The sample type I'm interested
in is that of dietary calories
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The sample type I'm interested
in is that of dietary calories
because that's what
I saved it in as.
Now, in this case, I want
to filter samples out
and get only those samples that
were inserted in for today.
So I'll have a predicate with
a start date and an end date
and start date and
end date will be start
of today and end of today.
Now I don't really care about
the number of samples as long
as I get all of them back
and I don't want to sort them
so I'm going to specify
nil here.
Again, when this query returns,
I'm going to get all the
quantity samples back
and what I want to do is
retrieve relevant information
from each quantity sample,
which in our case will
be the food item's name
and calorie information
and I'm going
to package that into an array.
So that's what this
code does here.
Notice here that I get back
the calorie information
and the calorie name.
I put that into this array and
once I have the array I'm going
to switch back to the main
queue and send it back
in the completion block.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
As always, let's not forget
to execute the query once
we have written a query.
Let's see whether this works.
So I had oatmeal.
Yay, that worked and bananas.
So, now that we have entered
these details we can actually go
ahead and compute
net energy burn.
So we're going to
define net energy burn
as total energy burn subtracted
by the total energy consumed.
So ideally the total here would
be this total energy consumed
but what about the
total energy burn?
So let's assume now that I'm
wearing this imaginary fitness
tracker device here and
every time I sync my iPhone
with this fitness tracker device
it inserts active energy burn
samples into Health Store.
So, let's write code to
actually get the total of all
of these quantity samples.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now I could write a sample query
get all the quantity samples
that I'm interested in and
then iteratively go over it
and compute the values,
but really what I want
to do here is use
a statistics query
because that's what
this is meant to do.
So as always let's have
a convenience method
and let's write our
first statistics query.
The quantity type that I'm
interested in will be passed
into this method and, again,
the predicate will be filtering
based on start date and end date
because I'm interested only
in samples that are relevant
for today and start date and
end date are going to be start
of today and end of today.
This is interesting part here.
I'm going to specify cumulative
sum because that's the quantity
that I'm interested in and
when this query returns note
that we are going to get
an HKStatistics object back
and what I want to do is get
the quantity object associated
with this statistics object
back and pass it along.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with this statistics object
back and pass it along.
So I do that and let's not
forget to execute the query.
Now let's actually get the total
energy consumed with this query.
So let's call the convenience
method that we just wrote
and the energy type that
I'm interested in is
that of dietary calories because
that's what I saved it as.
Finally when I get
the quantity back,
I know that I want the
unit as kilocalorie unit.
As always, let's switch back to
the main queue and return this.
Now I've already set up
code to do the exact same
for active energy burn
samples that were inserted
by my fitness tracker device.
The only difference here
is that of the data type,
which will now be
active energy burn.
Now let's see whether
this works.
Okay. I should see 255
calories here and that works
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Okay. I should see 255
calories here and that works
and now let's see
whether syncing works
and so I actually get my net
energy burn and there I'm
on track with my
net energy burned
and that was my quick demo.
So we saw in less than
10 minutes how we manage
to integrate HealthKit
with our app.
I hope you guys enjoyed it.
So the sample code for this
demo is already uploaded online.
Please come to our labs if
you have any more questions.
Over to Justin.
[ Applause ]
>> Thanks so much, Siji.
As you all can see, it's really
easy to incorporate HealthKit
into your own applications.
Next we're going to talk through
some HealthKit best practices.
So, before you can use HealthKit
the first thing you need
to do is let us know that
you intend to use it.
To support this, we've
built HealthKit right
in the capabilities pane of
Xcode so that you can manage it
in the same way you're
used to managing things
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
like Game Center or Passbook.
Once you've given your app the
HealthKit capability the last
thing you need is
permission from the user
to access their health data,
but remember health
data is really sensitive
to many of our users.
We want to encourage our
users to only give apps access
to the information that they
feel comfortable sharing.
To support this we let users
give your app permission
on a per object type basis.
That way if the only thing your
application needs is step data,
they don't need to
give it access
to what may be more
sensitive kinds of data.
We also let them give
your apps separate read
and write permissions
per object type.
A user might be totally fine
with your application
saving data to HealthKit
but not want it reading data
that came from other sources.
To request access
for a set of types,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
To request access
for a set of types,
you can use HKHealthStore's
request to authorization
to share types, read
types, completion.
They should be called
before you start interacting
with your Health Store
so that you can ensure
that the user has been given a
chance to grant your app access.
Once you ask for authorization
to a new type the user is going
to be presented with
this authorization sheet.
This is a little bit different
than the authorization alert
you may be used to seeing
from other frameworks.
This is specially designed
so that the user can
manage multiple permissions
for your application
at the same time.
To give your user the
best experience possible,
you should request authorization
for all types you intend
to use at the same time.
Once you've requested
authorization the next thing
you're probably going
to want to do is check
if you've been granted access.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
if you've been granted access.
In HealthKit, we let you see
whether you've been granted
sharing or write access
to a particular type,
but you can't see if you've
been granted read access.
This is because for some
kinds of information knowing
that the user blocked your
app can be just as private
as knowing the data itself.
For example, if a user
were to block your app
from reading their blood
sugar, it might indicate
that they're diabetic
and we don't want
to leak this information.
You can check your
authorization status
for a particular type using
HKHealthStore's authorization
status for type.
Our users are spread
all around the world.
To be able to reach all of these
people it's really important
that your app supports
localization.
Foundation already has
a set of NSFormatters
that let you localize
things such as numbers,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that let you localize
things such as numbers,
dates and even by count.
You could use a number formatter
to localize a quantity's value
but you'd have to
localize the unit yourself.
To help you localize some
kinds of the data that we keep
in HealthKit, we've added
3 brand new formatters
to foundation; NSMassFormatter,
NSLengthFormatter
and NSEnergyFormatter.
These let you display
things such as weight,
energy and distance in
a way that's pertinent
to the user's current locale.
Let's take a look at how you
might use NSMassFormatter.
First, we create our formatter
and we tell it that it's
to be used for person mass.
This is pretty important because
depending on your region,
you might use a different
unit to measure the weight
of a person than you would to
measure the weight of an object.
Next we create our quantity
and get its double
value in kilograms.
We then pass this weight in
kilograms to our formatter
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We then pass this weight in
kilograms to our formatter
and it gives us a localized
string including the
unit string.
Note that our formatter
explicitly takes its value
in kilograms.
Behind the scenes it
will convert this value
to the proper unit.
This works great if all you're
doing is putting a value
in a label but often
times you need to know
which unit was actually used.
To support this you can pass an
NS mass formatter unit pointer
to your formatter
and it will be filled
with whatever unit was
used by the formatter.
So, we've talked about
a lot of stuff today.
What I really want you
to take away from this is
that HealthKit makes it
easy for you to store
and share health data
which means you can spend your
time doing what you do best
keeping our users
happy and healthy.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Before you go there's one thing
that I would love for all of you
to do after this session.
Try out HealthKit for yourself,
see what you can make with it.
Check out our sample code so
you can see some examples of how
to properly use HealthKit.
For more information
you can reach out to one
of our awesome evangelists Dave
DeLong or David Harrington.
Our sample code is already
available on developer.apple.com
and we encourage
you to ask questions
on our Apple Developer forums.
We have one related
session, which unfortunately,
has already passed, but
you should totally check it
out online and it has
some great information
on how Bluetooth devices are
integrated right into HealthKit.
Thank you and everybody
have a fantastic WWDC.
[ Applause ]