WWDC2015 Session 713

Transcript

X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[Applause]
>> CHRIS JENSEN: Good morning.
Welcome to Introducing
WatchConnectivity.
My name is Chris.
Today I'm joined by
my coworker Alex,
who will join me on stage later.
We are excited to show you
what we have been working
on for both watchOS 2 and iOS 9.
We think what we are going
to talk about today is going
to help you make more responsive
and better user experiences
for your Watch apps.
To do a brief recap of
where we are coming from,
let's look at what the world
looked like in watchOS 1.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
let's look at what the world
looked like in watchOS 1.
Your iOS app and your WatchKit
extension were both living
on the iPhone, and we were
taking care of the communication
to the WatchKit app, and they
could both share a data store.
In watchOS 2, we've moved
the WatchKit extension
over to the Watch, and now
your app is running natively
on the Watch.
This has a lot of benefits,
but it also means they now
each have their own data store.
The obvious next question is
going to be: How do we get data
over to the Apple Watch?
That's what we will
discuss today.
So we are going to show
you two main ways to deal
with this problem, of
getting the data to the Watch.
There's the new
WatchConnectivity framework,
which we are introducing
in watchOS 2 and iOS 9.
And then there's the
NSURLSession APIs available
in Foundation.
These are still available
to your WatchKit extension,
and it's now available
native on watchOS 2.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We think the topics we are
going to discuss today apply
to most Watch apps,
and we think it's going
to apply to most of you.
This is exemplified by
the amount of examples
that we will be using in
our presentation today.
You can see there's a wide range
here, and we hope that every one
of you will be able to identify
with at least one of these.
So let's get started with
discussing WatchConnectivity.
So this is the new
framework we introduced
in watchOS 2 and iOS 9.
It's available in both
platforms, and pretty much all
of the APIs are available
in both sides.
There are couple of
iPhone-specific APIs
that we will get into.
The first thing you want
to do when you are starting
to adopt WatchConnectivity
is you want
to go through the setup.
We recommend that you
set this up very early
in your app's life
cycle on both sides,
both on the WatchKit
extension, running on the Watch
and in your iOS 9 apps.
You want to make sure you
do this setup in a code path
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You want to make sure you
do this setup in a code path
that will be executed even
if you are being
background launched.
So, don't put it inside, like, a
view controller's View Did Load,
because when you're being
launched in the background,
those won't get called.
So the first thing
you want to do
in your iOS app is
you will check to see
if the WCSession is supported.
You might have a
universal iOS app.
Which means this code may
be executing on an iPad,
where WatchConnectivity
is not available.
So check this up front before
you do any Watch-specific work
because we don't want you to
waste a bunch of CPU doing work
that won't be used anywhere.
The next thing you want to
do is create an instance
of our object, and you do that
by calling Default Session.
And then you want to set a
delegate on the session object,
and then finally you
want to call Activate.
This will go and set up
the WCSession object,
initialize all the properties,
and once this call returns,
all the properties
will be updated
with the correct
initialization values.
And also at this point,
any delegate callbacks,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And also at this point,
any delegate callbacks,
any cued-up content
can start coming in.
That's another reason
why you want
to do this very early
and always.
There might be content waiting
to be delivered to your app.
So make sure you do
these steps up front.
The next thing is you
are going to want to do,
once you completed that step,
is look at the session states.
This will inform your app about
what the current relationship
between the iOS app
and the Watch is.
So these properties are only
available on the iPhone app.
It's informing the iOS
app what its relationship
with the Watch is.
So you don't need to use
this in your Watch app,
and they are mostly
not available.
So for this example,
we will use a news app.
Something that pulls
down the most recent
interesting articles.
And it's going to first go
through the setup process
that we just discussed.
It will do this early
in its life cycle.
Next, it will want to check, is
this device paired with a Watch?
If it's not paired, then it's
almost as if this is running
on an iPad, there's
nothing more to do.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on an iPad, there's
nothing more to do.
There's no one else to talk to.
So at that point you might
as well stop doing any
Watch-specific work.
But the user will go out and
buy a new Watch, and he will go
through the pairing process.
He will launch the
Apple Watch app.
Work his way through
the pairing,
setting these devices up.
And now, when your app launches,
you will get a delegate
callback.
The session Watch
state did change.
When you check the value
of the paired property,
you will see it will
return True,
because the devices
are now paired.
So this is a trigger
for you to go ahead
and check this next
property, which is,
is your Watch app installed?
If it is not installed,
then there's no one
to talk to, your work is done.
But -- and by default, the
Watch app will be default --
but the user might choose to
uninstall it for some reason.
In this case, the user will
go ahead and reinstall it.
He will go back into the Apple
Watch app and he will flip
that switch, and now if
your Apple is running,
you will again get
the delegate callback
and you can check
this properties value,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and you can check
this properties value,
and you would see that Watch
App Installed is now True.
Now you do have someone
to communicate with.
This should be a trigger for you
to start communicating
with your Watch app.
It will need content that
only you can provide for it.
Whenever the Watch App
Installed switches to True,
there's this other
property that's going
to be available,
Watch Directory URL.
You will find that this,
whenever Watch App
Installed is True,
Watch Directory URL will
have a non-nil value.
It will be a path to a directory
that we create in
your container.
Let's discuss this a
little bit more in depth.
So the directory and its
contents, its lifetime is tied
to the Watch App
Installed property.
This means whenever Watch App
Installed switches from True
to False, this directory and
all of its contents goes away.
Whenever it switches
back to True,
the directory will be present,
but this time it will be empty.
We recommend you only
use it for data relevant
to the specific instance
of your Watch app.
What do I mean by instance?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
What do I mean by instance?
Well, things like last queued
item marker would be a good
thing to store there.
If the user uninstalls
and reinstalls your app,
that Watch app is starting
with a clean container.
Therefore, this directory will
start clean, and you will need
to sync up where your
app is communicating.
Other things you can put in
is something like preferences.
When the user is running the
iOS app for the first time,
you might like him to
set up what he would
like his Watch app
experience to be.
Maybe he doesn't want
to show the full content
but the top ten news items
for a particular topic
like international
news or sports.
This would be a good
place to store them.
Also if you are taking
your full-sized assets
and generating Watch-specific
assets,
you are compressing
images, audio, video,
this would be a good place to
store those while they are cued
for transfer, using the
WatchConnectivity APIs
that we will discuss later.
So that's the Watch directory.
We suggest you store
content in there,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We suggest you store
content in there,
because then we will clean it
up, if the Watch goes away,
if he unpairs his watch,
we will automatically clean
up this content, so you don't
have to micromanage all of this.
The final property that's part
of the session state is
Complication Enabled.
Currently, the user does not
have the complication enabled,
but he will go in and edit his
Watch face and he will enable it
and you will get the
same delicate callback,
session or stated change.
And when you check this
property, it will now be True.
Now you have set up your
WatchConnectivity session.
You have figured out what
the state of the world is,
what your relationship
between your iOS app
and your Watch app is.
The next thing you will want
to do is start communicating
information over to the Watch
or from the Watch
to the iOS app.
And to do that, I will hand
over to Alex, who will talk
about the communication APIs.
[Applause]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[Applause]
>> ALEXANDER LEDWITH: Thank you.
Okay. So like Chris said,
you set up your session.
You have checked that
devices are paired,
your Apple Watch
app is installed.
Now let's start talking
about how you can communicate
between these two devices.
We have a couple of different
categories for communication.
First category is
background transfers.
Background transfers
are meant for content
that is not needed immediately
on the receiving side.
Because the content is
not needed immediately,
this means the system can
do more intelligent things
when transferring that content.
In addition to background
transfers, we are also going
to talk about interactive
messaging.
Interactive messaging is
meant for communicating
between two apps with
live communication.
So both apps are up and running.
They are sending
messages back and forth,
request response,
that kind of thing.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
request response,
that kind of thing.
Some examples of
when you might want
to use interactive messaging.
Say you have a game where
the user is using both apps
at the same time.
Or you are on your Apple
Watch, and you need
to trigger something to
happen on the iOS side.
Like you want to
trigger the iOS device
to start tracking
the user's location.
So let's dig into this first
one, background transfers.
The first thing to talk
about for background
transfers is the type
of content your apps have
and how the user is going
to interact with
these two devices.
So let's take that
news app example again,
this news app has some content.
It's fetching some more
content from the server,
and it determines some of
this content could be useful
for your Watch.
Now, the user isn't using the
Watch at this point in time.
So the content isn't needed
immediately on the Watch side.
Rather, the iOS app, the news
app, wants to just pick some
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Rather, the iOS app, the news
app, wants to just pick some
of that content and queue
it up with the system,
and then allow the system
to pick the right conditions
to transfer that content across.
The system will look at things
like power, performance,
when the user is actually
using the receiving side,
in this case the Apple Watch.
When conditions are right, that
content will transfer across,
and it will wait on
the receiving side,
in this case the Apple Watch,
until the user launches
the receiving app.
When the receiving
app is launched,
then that content
will be delivered
and the app can update
its state.
So this is what background
transfers provide.
It allows you to
queue up content.
The system is going to
transfer the content for you.
This allows the sending side,
the sending side app, to exit.
The system will handle the rest.
It allows the system to pick
the opportune time to transfer
that content, and it allows the
system to store the information
on the receiving side and wait
for the receiving app to launch.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on the receiving side and wait
for the receiving app to launch.
For a lot of the
content your apps have,
we definitely recommend that
you use background transfers.
The reason is most of that
content will not be needed
immediately on the
receiving side.
Rather, it will be needed
when the receiving
app actually launches.
So let's get into
some nuts and bolts.
We have three different types
of background transfers.
The first type is the
application context.
The application context
represents a single set
of the most interesting
information your app has
to offer to the other side.
So, for example, let's say on
the iOS side you have an app
that tracks the user's location,
and based on that location,
the app picks a restaurant
in that location and wants
to recommend it to the user.
In addition to sharing
restaurant on the iOS side,
you also want to
show that restaurant
in the Apple Watch app.
So you could package
up that suggestion
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So you could package
up that suggestion
into the application context,
and that will get
transferred across,
and then the next time
the user launches the app
on the Apple Watch side,
the content will be there,
that suggestion will
be there to show.
Another example of
when you might want
to use application context, say
you have a social networking app
on the iOS side, it fetches a
bunch of posts and determines
that there's a top 10 set
of really interesting posts
that it wants to show
on the Apple Watch side.
That way the user can
look at the Apple Watch
and see interesting
information right away.
You can also package up
of those top 10 posts
into an application context,
which will get sent across.
Now application context
is the simplest way
of transferring content
in the background,
but if you need something
a little more complex,
or you need to queue up
more than a single set
of information, we are
offering two ways to do that.
The first way is
user info transfer.
This allows you to transfer
user info dictionaries,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This allows you to transfer
user info dictionaries,
in-memory content that
you want to pack up.
An example of this is
let's say you have a game
on the Apple Watch side.
The user progresses
through levels,
and as the user progresses
through levels,
you want to sync back that
progression to the iOS app.
The iOS app will
show some nice graphs
on how the user did
in each level.
In addition to the
user info transfer,
we are also offering
file transfer.
This is very similar.
It allows you to queue up
content, except in this case,
the content is a file.
One example that we are going
to use in this presentation
for file transfer is let's
say you have an iOS app
that allows the user
to edit images
and after they edit
those images,
the user can pick
their favorites.
And those favorited images
are the ones you want
to show in Apple Watch.
You can use file
transfer to transfer
across those favorited images.
So they're available
on Apple Watch
so the user can show them off
to their friends,
that kind of thing.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to their friends,
that kind of thing.
So let's dig into
these individually.
We will start with
application context.
The example we will use
for application context is the
social networking app example.
I mentioned before, this app
on iOS fetches all the posts
from the social networking site
and then it picks the most
interesting ones to send
over to the Watch app.
The first thing to talk
about with application context
are these two properties.
The first property is
Application Context.
It is the property that
stores the latest content
on the sending side, and
then on the receiving side,
there's Received
Application Context,
which will store the
latest received content
on the receiving side.
So let's say this iOS app
has fetched a bunch of posts,
and it's packaged up the most
interesting ones for the Watch.
It's going to want to call
Update Application Context.
This method takes a dictionary,
representing the latest,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This method takes a dictionary,
representing the latest,
most interesting state
you want to send across.
This content, we
take this content
after Update Application Context
is called, and we push it
down into the Application
Context property.
And this content will sit here,
and the system will determine
a good time to transfer
that content across, maybe
when the user starts
actually using their Watch.
Now, in the meantime, this iOS
app could fetch more content
and determine that
there's a newer set
of interesting information
that it wants
to send over to the Watch.
In this case, it's going to
want call Update Application
Context again.
And then we're going
to push that content
down into the Application
Context property.
This is going to bump out the
old relevant state and put
in the new relevant state
because what we really care
about is the most
interesting, latest set of data.
Now, this new content is
going to sit here, again,
waiting for the system
to pick a good time
to transfer that content across.
When the system does
pick a good time,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
When the system does
pick a good time,
that content will come across.
It will sit on the
Apple Watch side.
And it will wait for the user
to launch the app,
the Apple Watch app.
When that app is launched,
we will deliver that content
to your WatchKit extension,
the place where all
of your code is executing
on the Apple Watch side.
So that's the flow of
application context.
Now, let's take a
look at some code.
The first thing you want to
do is you want to package
up your context dictionary,
representing the latest state
that you want to send across.
And then you will call
Update Application Context
with that dictionary.
The last thing to mention
about this code example is
that the call to Update
Application Context is wrapped
in a Do Catch block, and the
call is prepended by a Try.
This is new error
handling in Swift.
Update Application Context
can return an error.
And if an error is returned,
the Catch block will be invoked,
and we strongly suggest
that you handle your
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and we strongly suggest
that you handle your
errors appropriately.
So that's the sending side
for application context.
Now, let's take a look
at the receiving side.
On the receiving side,
the receivers will get
this delegate callback,
Did Receive Application Context.
It's going to pass
through the dictionary
that the sender packaged up.
And at this point, the
receiver can take that content
and update its app state.
One thing to know about
this delegate callback
and all delegate callbacks in
our API is that they're returned
on a non-main serial queue.
If you need to do
something on the main queue
because maybe you're updating
some UI, you're going to need
to dispatch over to the main
queue to do that updating
of your UI based on this
content or something else.
So that's application context.
It's the most interesting
relevant content
that your app has
for the other side.
It does have overriding
behavior,
and this is because you should
treat the latest content
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and this is because you should
treat the latest content
as the content that the
receiving side cares about
and anything that's not
latest isn't relevant anymore.
Application context
takes a dictionary.
This dictionary takes
property list types.
Property list types are basic
object types such as numbers,
strings, basic collection
types, dictionaries, arrays.
Apple has some great
documentation online
if you want a refresher
on property list types.
So we have some specific
recommended use cases
for application context.
Application context works really
well for many Apple Watch apps
because many Apple
Watch apps show a subset
of the information
the iOS app has.
So, if your app works like
this, we suggest that you put
that subset of the information
into the application context
and let it get sent across
to the Apple Watch side.
In addition to those apps,
application context also
works really well for glances.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
application context also
works really well for glances.
Glances take the single
most interesting piece
of data your apps have to offer.
So we suggest you put
that piece of data
into an application context on
the iOS side, so that it gets
across to the Watch side.
Then when the user swipes
up on the clock face
to show your glance, that
data will be available.
So moving on from application
context, now we are going
to talk about user
info transfer.
An example here that we are
going to use is you have a game
on the Apple Watch side.
The user progresses
through levels in this game,
and as the user passes
one level, you are going
to sync back the progress
that they made to the iOS side
so that the iOS app can
show some nice graphs
on how the user did
in that level.
The first thing to talk
about for user info transfer
is the outstanding user info
transfer queue.
This holds on to all the
content that's waiting
to be transferred across.
The current state of the world
as the user progressed
through two levels.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
as the user progressed
through two levels.
The progression is sitting
in the outstanding
user transfer queue.
And currently the user is
working on level three.
Once they finish level three,
you are going to want to package
up that content and call
Transfer User Info and pass
through a dictionary that
represents their progress.
This will take that dictionary,
and it will package it up
and it will put it into
the outstanding user input
transfer queue.
Now this content will wait here
until the system determines
it's a good time to transfer
that content, based on power
considerations or maybe
when the user starts
using their phone.
The content will then transfer,
and like the previous API,
we are going to wait until the
app on the iOS side launches.
And when it does, we will
deliver that content,
and now the iOS app
can update those graphs
to show the progress the user
made in their Apple Watch game.
So that's the flow for
user info transfer.
Let's take a look at some code.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let's take a look at some code.
First thing you want
to do is package
up your user info dictionary
with all the content
that represents this
current state
that you want to send across.
And then you want to
call Transfer User Info
with that dictionary.
Transfer user info returns
a user info transfer object.
This object contains the
dictionary that's being sent
across, and it allows you
to cancel this transfer
if the transfer is still
in the outstanding queue.
In addition to this transfer
object being returned,
we also offer a way to get all
of the outstanding user info
transfers that are in the queue.
This returns an array, and you
can iterate over the array,
look at all the contents,
and potentially cancel
if you need to.
So that's the sending side
for user info transfer.
Let's take a look at
the receiving side.
On the receiving side,
you will get this call,
Did Receive User Info Transfer.
Like application context and all
the other delegate callbacks,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Like application context and all
the other delegate callbacks,
this is returned on a
non-main serial queue.
Once you get that call,
you can take that content,
that dictionary content, and
you can update your app state.
That's user info transfer.
It takes user input
dictionaries.
These dictionaries, like the
application context dictionary,
take property list types.
It's good for in-memory
content, like game progression.
And we give you access
to the outstanding user
info transfers in the queue.
Next, let's talk
about file transfer.
An example we will use
for file transfer is this
image-editing app.
The user can edit
images on the iOS side,
and then they can
select their favorites,
and those favorites are the
ones we want to transfer
across to Apple Watch.
So the first thing to talk
about is the outstanding
file transfer queue.
This is where all the
file transfers will sit
when they are waiting
to be sent across.
And then on the receiving
side, the files will be put
into the Documents/Inbox
folder while they are waiting
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
into the Documents/Inbox
folder while they are waiting
to be delivered to the
receiving side app.
The state of the world is the
user has favorited two pictures,
two images, that are
sitting in the queue,
and they are working on a third.
Once they have completed that
third and have selected it
as a favorite, you will
want to call Transfer File.
And you will pass in a file
URL pointing to the file
that you want to transfer,
and we are offering a way
to transfer additional metadata
in the form of a dictionary.
One example of when you might
want to add some metadata is
if you want to group some
of these files together
by putting an identifier in
each metadata dictionary.
That way the receiving side
can pull out that identifier,
group the incoming files.
So the user's favorited
this image.
We called it transfer file.
Now we will take that
packaged-up content,
and we will put it into the
outstanding file transfer queue.
And it will wait here until the
system determines a good time
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And it will wait here until the
system determines a good time
to transfer the content.
When the system does determine
a good time to transfer
that content, it will move that
content across and it will wait
for the receiving side to launch
and take care of that content.
One thing to note about files,
files can be a little
bit larger.
And the larger the file,
the longer it's going
to take to transfer across.
Potentially you might
hit power conditions,
performance conditions while
those are transferring.
Just be aware if you have
large files that are trying
to transfer across,
they may take longer
than the transferring in
some of the other APIs.
Now the receiver will launch,
and we will deliver
these images.
And now the Apple Watch app
can show those images off.
So that's the flow
of file transfer.
Now let's take a
look at the code.
First thing you want to do
is you want to get your URL
to the file that you
want to transfer.
Then you want to package up
your metadata and finally,
you want to call Transfer
File, passing through that URL
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you want to call Transfer
File, passing through that URL
and that metadata dictionary.
This returns a file
transfer object,
the file transfer
object contains the URL,
the metadata dictionary, and
also gives you the ability
to cancel any file transfers
that are outstanding.
Just like user info transfer,
we offer you the ability
to get the array back of all
the outstanding file transfers.
You can iterate over this
array, check the contents,
and cancel if need be.
So that's the sending
side for file transfer.
Now let's take a look
at the receiving side.
On the receiving
side, you are going
to get this delegate
callback, Did Receive File.
There's a few things to mention
about this delegate callback
that are slightly different
than the previous two.
First, you are going
to be getting this
WCSession file object.
This object just contains the
file URL and the metadata.
The second thing to talk
about with this callback is
that the file is now in
the Documents/Inbox folder
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that the file is now in
the Documents/Inbox folder
of your app's container.
But to take control of this
file, you need to move that file
out of the Documents/Inbox
folder
into a more permanent location.
So the main reason you
need to move this file is
that the Documents/Inbox
folder will be cleaned
up after this delegate returns.
This means the file will be
deleted out of there along
with any additional content.
So it's really important
that you move this file
into a more permanent location
inside this delegate callback.
One thing to keep in mind
if you are dispatching
to a different queue
because this is returned
on a non-main serial
queue, you will need
to move the file before
you do that dispatching,
if that dispatching is async.
So that's file transfer.
It's very similar to user info
transfer, except it allows you
to transfer files
or queue up files.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to transfer files
or queue up files.
We do offer the ability
to access the outstanding
files in the queue.
And we provide the ability to
transfer additional metadata.
We suggest you keep
this metadata small,
and this metadata dictionary,
like the other dictionaries
we talked about,
takes property list types.
So those are the three
background transfer modes.
Use these if the
receiver does not need the
content immediately.
If, however, you need to
send messages back and forth
in a live fashion, you can
use interactive messaging.
And interactive messaging
is meant
for that live communication,
both apps are up and running
and they are sending
messages back and forth.
Like I mentioned
before, some examples
of when you might
want to do this.
Let's say you have a game
where both UIs are up
and you want the user to
be interacting with both.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and you want the user to
be interacting with both.
Or if you are on the Apple
Watch side and you need
to trigger the iOS
app to do something,
like start tracking
the user's location.
Now, there are certain
conditions that need to be met
for interactive messaging
to be used.
So let's talk about
those conditions.
It all relates to this idea
that we are introducing
called reachability.
And what reachability means is
that the other app is
available to receive content.
It is required that the
other app is available,
the other app is reachable,
to use interactive messaging.
And the way that you check
that the other side is reachable
is we have this property
on the default session,
Reachable, that you can look at.
Now, the conditions
for reachability are
slightly different depending
on what slide you are on,
whether your code is executing
in your iOS app or
it's executing
in the WatchKit extension.
So let's look at
those individually.
We will start on
the iPhone side.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The first condition that
needs to be met for Reachable
to be True is that the
devices needs to be connected.
This is connected over
Bluetooth or over Wi-Fi,
but if the user leaves their
Watch at home, takes their phone
with them to work, the
devices won't be connected
and interactive messaging is
not going to work in this case.
The second condition that
needs to be met for Reachable
to be True on the iOS side is
that the Watch app
must be foreground.
This means the user must be
interacting with their Watch app
for interactive messaging
to work from the iOS side.
Once these two conditions
are true,
the Reachable property will
be True in your iOS app.
So that's the iPhone side.
Now let's talk about
the Apple Watch side.
The first condition for
Reachable to be True
in your WatchKit extension is
that once again devices
must be connected.
This means that if the user goes
for a run and leaves their phone
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This means that if the user goes
for a run and leaves their phone
at home and takes
their Watch with them,
Reachable will not be True.
The devices will
not be connected.
The second condition is that
the WatchKit extension needs
to be foreground.
We mentioned the WatchKit
extension here being foreground
because there are certain cases
where the WatchKit extension
can run in the background.
They mainly relate to
complications, and we will talk
about this a little
later in the talk.
For now, when the user
is using your app,
your WatchKit extension
is going to be running
and your WatchKit
extension will be foreground,
which means you can use
interactive messaging
and the Reachable
property will be True.
One other thing to note
about this diagram.
We are not saying that
the iOS app is running.
The iOS app has to be running to
respond to messages coming in,
to send its own messages.
So how do we get into a state
where the iOS app running
in addition to the
WatchKit extension?
Well, for this direction only,
sending messages from the Watch
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to the phone or allowing
the iOS app to be launched
in the background upon
receiving a message.
So let's take this example.
You have a run tracker app
and it needs to send a message
over to the iPhone side
to talk to CoreLocation
to start tracking
the user's location.
So this app is going
to package up a message
that tells its iOS app to
start using CoreLocation,
and it's going to send
that message across.
When the system receives
this message, we are going
to launch the iOS
app in the background
and deliver that message.
Now, both apps are running, and
now they can do communication.
This app in this example
can start tracking the
user's location.
So that's kind of flow
of interactive messaging.
Kind of how it relates
to reachability,
when you can use it.
Now let's get into
the nuts and bolts
of how you use it in your code.
We are offering two
different types of messages.
The first type takes
a dictionary
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and you use this
call, Send Message,
which takes that dictionary,
plus a reply handler
and an error handler.
This dictionary, like the
dictionaries we talked
about before, takes
property list types.
In addition to dictionaries,
we are also introducing
a way to send data.
You can send data by
calling Send Message Data.
This takes that data plus
that same reply handler
and that same error handler.
For sending data, we suggest you
use this if you have custom data
that you're storing
your information in
or if you have your own
serialization format.
If you are using your
own serialization format,
we strongly suggest you use
one that's quick and compact.
This way the user
experience is faster
because the content is
transferring faster.
One thing I want to point out
about these calls is replying.
You probably noticed the
previous two calls have
reply handler.
This handler is optional.
However, we do recommend
in most cases you use it.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
However, we do recommend
in most cases you use it.
The reason is that this
allows the receiver
to confirm the incoming message.
The receiver can confirm
that it received the message.
The message contained the
right content, and it was able
to process that content.
And then this way the sending
side knows that it doesn't have
to send anything else.
It doesn't have to
send anything new
because it sent the wrong stuff.
The other part to talk about
for the replying is what happens
on the receiving side.
What happens if the sender says,
"I want to reply, so I'm going
to supply a reply handler,"
versus if the sender says,
"I don't want to
reply, I'm not going
to supply a reply handler."
In these cases, we have separate
delegate callbacks the receiver
is going to get,
depending on whether
or not it should supply a reply.
So let's talk about
those delegate callbacks.
In the first case, the sender
says, "I do need a reply,
I'm giving the system
a reply handler."
This means that the receiving
side will get this delegate
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This means that the receiving
side will get this delegate
callback, Did Receive
Message, it has a Reply block
that you can call after the
receiver has received the
message and processed it, and
the receiver can then determine
if it wants to send
back some content
or maybe send back an error
if the message is wrong.
Now, on the other hand,
if the sender doesn't
supply a reply handler,
the receiver is going
to get this delegate
callback, Did Receive Message.
It doesn't have a Reply block.
The receiver can process
the incoming content
and they are done.
The last thing to note
about these two delegate
callbacks is they pass a
dictionary through.
This means that the sender
used the send message,
sending on the sending
side, to send a dictionary.
If instead the sender used
send message data to send data,
there is analogous callbacks
on the receiving side
that pass through data.
So now that we kind of have a
feel for interactive messaging,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So now that we kind of have a
feel for interactive messaging,
let's put it all together and
code for the sending side.
The first thing you
want to do is you want
to check reachability,
make sure the other side is
actually reachable.
Then if Reachable is True, then
you can package up your message.
And once you have your message,
you can call Send Message
with that dictionary.
We expect a reply.
So we will supply
the reply handler,
and we want to handle
our errors,
so we will implement
an error handler.
So those are the different ways
to transfer content
using WatchConnectivity.
So let's sum up what
we've talked
about for WatchConnectivity.
The first thing you
want to do is you want
to set up your session.
To do this, you set your
delegate, and you call Activate.
You want to do this early
in the lifetime of the app
so the app has the ability
to start receiving content
and the ability to start
checking properties.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and the ability to start
checking properties.
To check those properties,
you look at the session state.
And once everything is okay,
once you know there's
a paired Watch,
once you know your Apple
Watch app is installed,
you can start communicating.
The first type of communication
is background transfers.
We offer three types.
The first type is
application context.
This is for the single set of
really interesting information
that your app has
for the other side.
Or if you need to
queue up content,
you can use user info
transfer or file transfer.
In addition to background
transfers,
you can use interactive
messaging
for live communication.
So that's WatchConnectivity.
It allows device-to-device
communication between your apps.
And we are excited to see
what you do with this API
to get content back and forth
and provide better
user experiences.
Next, we're going to talk
about NSURLSession briefly.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Next, we're going to talk
about NSURLSession briefly.
So what is NSURLSession?
It's an existing
foundation class.
It allows you to
make HTTP requests
to your servers to
fetch content.
It's available in watchOS 2, and
we strongly suggest you use it
if your servers have content
that needs to be fetched.
And it takes advantage of
the Tetherless Wi-Fi feature.
The Tetherless Wi-Fi feature
allows Apple Watch to connect
to known Wi-Fi networks when
the phone is not around.
If the Apple Watch does
connect to known Wi-Fi networks,
you can use NSURLSession to
go over that Wi-Fi to connect
to your servers and
fetch content.
So what do you want
to use NSURLSession?
You want to use it any time
your server has new content.
This is very similar to how
you might be doing stuff
in your iOS apps.
We do suggest, however, that you
tailor the content that's being
delivered to Apple Watch based
on how Apple Watch works.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
delivered to Apple Watch based
on how Apple Watch works.
So if you have images
on your server,
we suggest you scale those
images for the screen size
of Apple Watch, or if you are a
news app and you are only going
to show some of article, maybe
just the text, on Apple Watch,
we suggest you only
fetch the parts you need.
So that's a very brief
introduction to NSURLSession.
There's a great WWDC
session on this API as well
as great online resources.
So we definitely suggest
you check those out.
Now, the last thing
we want to talk
about for NSURLSession
is using NSURLSession
with WatchConnectivity.
So once again, we have the
example of our news app.
This news app has
fetched a bunch of content
from its server, and it knows
that the Apple Watch
app probably will have
to fetch this same content the
next time the user launches the
Apple Watch app.
Instead of making the Apple
Watch app refetch that content,
we will use application context
to transfer across the content
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we will use application context
to transfer across the content
from the iOS side
to the Watch side.
That content is going to come
in, and it will be delivered
to the Apple Watch app
the next time it launches.
And now the Apple Watch
app has the ability
to show the same content that
was seen on the iOS side,
and it provides a more
cohesive experience.
Now the next time the user
launched the Apple Watch app
could be a couple
of hours later,
which means the server
has even newer content
that might want to be fetched.
So we suggest that
in addition to taking
in the application
context that was sent over,
you use an HTTP request
with NSURLSession
to fetch the absolute latest
content from your server.
But this way, while the user
is waiting for that content
to come down, they will see
the same content they saw
in the iOS side and they will
have a better experience.
So that's NSURLSession,
NSURLSession
and WatchConnectivity.
Now, we want to take these two
APIs and we want to show you how
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, we want to take these two
APIs and we want to show you how
to use them to get data
to your complication.
And to do so, I will
bring Chris back
up to the stage to
talk about this.
[Applause]
>> CHRIS JENSEN:
Thank you, Alex.
That's some cool stuff, right?
I think it's going to be
great to see what you guys end
up doing with the
WatchConnectivity APIs
and the NSURLSession APIs.
Now let's discuss complications.
But before we dig in too deep,
let's make sure we are
all on the same page.
These are three Watch faces,
three clock faces
on the Apple Watch.
If you remove the timepiece,
the remaining pieces
are complications.
They provide small snippets
of information every time the
user looks at their clock face.
And this will allow them to get
sort of the most important piece
of information really quickly.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of information really quickly.
So when you are implementing
your complication,
there's two primary tasks
that you will have to solve.
You will have to figure out
how to update the clock face,
and the second one is you
need to get the content to use
to update the clock face.
So let's discuss how you would
update the clock face briefly.
This is covered in
depth at other sessions.
For this example, we
will use a weather app
that has a moon phase
complication.
The moon phase complication
doesn't need any external data.
It already has all the
information it needs
because it just needs
the date and time.
So all it needs to
concern itself with is how
to update the clock face.
To do that, it's going to
use the new ClockKit API,
the ClockKit framework
introduced in watchOS 2.
The way the flow
works is we're going
to launch our WatchKit
extension in the background.
When this happens, you are
going to want to get an instance
of CLK complications server.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You call Shared Instance
to do that.
And you will call Extend
Timeline For Complication,
and you pass in the complication
that you are updating.
The next thing that will happen,
that will trigger our process
with ClockKit and they will
start asking you a couple
of questions.
They will ask for the
current timeline entry.
This is the one that's
going to be shown right now.
They will ask for
previous timeline entries,
future timeline entries, and
finally, they are going to ask
for a suggestion of when you
think this data will be stale.
This is a suggestion to the
system so that we can know
when you think that you
need to get launched again.
So you can update
your timeline further.
That was a quick summary
of updating the clock face.
You would use the ClockKit
framework to do so.
You will be able
to provide content
for past, present, and future.
Your WatchKit extension will
get launched in the background
to do these updates, and
you will be given a chance
to specify when the content
provided is going to be stale.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to specify when the content
provided is going to be stale.
One thing to keep in
mind is that all the work
that your WatchKit
extension is doing on behalf
of updating the complication
is budgeted, so you want to try
to keep this as fast and
efficient as possible
so that you can keep getting
launched throughout the day
to update your complication.
As I mentioned, there's a great
talk dedicated to this topic,
Creating Complications
with ClockKit.
We want you to check that
out if you haven't done so.
The next you will have
to deal with is how
to get content to
your complication.
There's a very special instance,
which is the initial activation.
The first time the user
goes into his clock face,
he's going to go in and
edit it and then he's going
to enable your complication;
in this case,
the news app complication.
At this point in time,
that complication very likely
has very little or no data
to populate its timeline.
So it has a large need
for a lot of content.
So what's going to happen
is we are immediately going
to launch your WatchKit
extension in the background.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to launch your WatchKit
extension in the background.
And now you have a couple
of different ways of getting
that content so you can
initialize that timeline.
You could use NSURLSession to
communicate with your servers
to get that content,
or you could choose
to use WatchConnectivity.
If you are going to
use WatchConnectivity
in this very special
circumstance,
and the devices are
connected, you will find
that Reachable is True.
This is what Alex was
referring to earlier,
where this property
might be True
in certain special
circumstances.
This is one of those.
What you are going
to be able to do
in this case is you will be
able to call Send Message,
which will send a message
across to the iPhone.
And we will wake up the
weather app in the background,
and at this point, the weather
app on the iOS side can use any
of the WatchConnectivity APIs to
communicate the information back
to populate that timeline.
So in summary, when
you are going
through the initial activation,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
through the initial activation,
your WatchKit extension will
get launched in the background.
You can use NSURLSession or,
because this is a very special
circumstance, you will be able
to use the WatchConnectivity
APIs to wake up the iOS app
because Reachable is True.
We suggest you use this
to populate as much
of the ClockKit timeline
as possible
because it's starting
with nothing.
The next issue is
how to stay current.
Your timeline is now populated,
and now future updates
will be happening.
How do you keep updating
your complication?
So there's a couple of different
ways that you could use
to update your complication.
You could get content
pushed to your complication.
This makes sense if you
have an external source
such as a web server
that knows specific times
when there is new content and
it's not a regular cadence.
What you are going to want to
do then is have the content push
from the clouds to the iPhone,
and then it gets relayed
over to the Apple Watch.
An example of where we think
this makes sense is something
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
An example of where we think
this makes sense is something
like a sports app, where
the complication is showing
game scores.
Most of the time, those scores
are going to be happening
in a short period of
time during the day,
and it will be very
rapid updates.
So then we think
it makes more sense
to use the pushed approach.
The other one is what
we're calling requested
interval fetch.
This is more for when you
know there's a regular cadence
where you can keep
updating your complication.
And then you could use
something like NSURLSession
to go directly to the cloud.
Something like a surfing app
with a tide complications
that shows what the tidal
patterns will be so you know
when to go out surfing.
So let's take a look
at this case first.
In this case, you will want to
use NSURLSession and ClockKit
to update the complication.
As you can see in the
corner of clock face,
the surfing complication
is already enabled.
The flow is going to go
a little bit like this,
where the WatchKit extension
gets launched in the background.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
where the WatchKit extension
gets launched in the background.
You're going to want to
generate an NSURLSession request
and you send that up to
your servers to get content.
The server will produce
a response
and it will get delivered back
down to the WatchKit extension.
Now you want to turn
around and update ClockKit.
So you will request them
to extend your timeline.
They will start asking you these
questions, and you're going
to give them the timeline
updates, both past, present,
and current, and finally, you
are going to suggest a time
for when you should
be launched again.
And the last thing that will
happen is when you provide
that time for when you
next should be launched,
that's a hint to the system
that your work is done,
and your WatchKit
extension will get killed.
So now, let's pretend
that some time passes,
and the system has decided
based upon your hint
and system conditions
that now is a good time
to relaunch your complication.
Again, you are going to get
launched in the background.
You are going to produce a
request, using NSURLSession.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You are going to produce a
request, using NSURLSession.
You are going to send
it up to the servers.
The servers are going to produce
a response, and you are going
to turn around and update your
complication using ClockKit.
So in summary, and a couple
of tips is that we suggest
that you use NSURLSession
background session if possible.
This is because the NSURLSession
request might not complete
until the next time
the extension runs.
Using the background
session enables it
to deliver the content
the next time you run.
The requested time that you are
providing is just a suggestion
to the system.
It's not a guarantee.
We'll try to get you close to
that time, but conditions apply
and it may not always be exact.
We suggest that you keep the
runtime as short as possible,
and you will use ClockKit
to update the clock face.
You want to keep runtime as
short as possible and make sure
that you make your next
requested update time as far
out as possible because these
are budgeted and you don't want
to run out of budget
before the day is over.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to run out of budget
before the day is over.
The other approach to
getting the content,
which was in our example
of the sports app,
was to get the content pushed.
We will look at this
in a couple of stages
because it uses two
very distinct processes.
The first one is you
are going to use PushKit
to get the content from
the cloud to the iPhone.
The second part is using
WatchConnectivity to get
that content from the iPhone
across to your Apple Watch.
So let's look at
those separately.
So part number one is
where you are using PushKit
to get the content
to your iPhone.
We have updated the PushKit
framework to add support
for these complication pushes.
The way you use it is you create
an instance of PKPushRegistry.
Next, you will want to
set yourself as a delegate
so that you are ready
to receive callbacks.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so that you are ready
to receive callbacks.
And finally, you will set the
decide push types and pass
in the new PK push
type complication
that that was added in iOS 9.
Once this is done, you will
get a delegate callback
with a new Push token, which
you're going to want to upload
to your servers, which is
going to enable your servers
to send pushes to this device.
Finally, when the server
does send the push,
you will get the Did
Receive Incoming Push
With Payload callback.
And this when you turn around
and use the WatchConnectivity
APIs
to send the content
over to the iPhone.
So for the second part,
this is where you are going
to use WatchConnectivity
to get the content
across to the Apple
Watch app now
that you received
it in your iOS app.
The first thing you
are going to want
to use is the transfer user info
API that Alex discussed earlier.
This will allow you to
queue up timeline entries
for both the past and the
future that your iOS --
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for both the past and the
future that your iOS --
sorry, your Watch
complication might need.
Once you have queued up
all the timeline entries,
the last thing you want
to do before you are done
with your work is
called a special API.
It's also part of
WatchConnectivity.
It's called transfer current
complication user info.
This is a special version
of the transfer user info,
and at any point in time,
there can only be one current
complication user info.
If you call this twice, only
the most recent call is the one
that is tagged as the current
complication user info.
When you call this, this
is a hint to the system
that the work is done, and
on the receiving side all
of these callbacks will
produce this delegate callback,
Did Receive User Info.
So let's look at what
this all would look
like from a full flow.
All right?
So the user has launched
a sports app
for the very first time.
Your app early in its life
cycle will want to set
up the PK push registry
and set the desired types.
This will register this push --
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
This will register this push --
this device with the
Apple push servers.
So that will get pushed
up to the Apple servers.
It will turn around and produce
a Push token, and you will get
that delegate callback
in your iOS app.
You will want to take that token
and upload it to your servers,
enabling your servers
to send pushes
to this device in the future.
At that point, the initial
setup of the PushKit is done
and your app can go away.
Let's say the game starts, and
the server decided it needs
to update the complication.
It will send a push
down to your device.
That will get received on
the device, and we will wake
up the sports app in the
background and deliver the push.
That was that other
delegate callback.
At this point, you are going
to want to look at the data
in the push payload and figure
out what needs to get sent
across using WatchConnectivity.
You will call Transfer
User Info to queue
up the timeline entries,
both past and future,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
up the timeline entries,
both past and future,
and that will go into
the outstanding user info
transfers queue.
And then you call the special
transfer current complication
user info with the one
that's most important,
and that's the one
that should be shown
on the Watch face right now.
That will also go into the
outstanding user info transfers
queue, but it will
skip to the front
because it's the
most important one.
And because this is considered
urgent, we will try to get
that across to the
Apple Watch right away,
and wake up the WatchKit
extension in the background
and deliver that current
complication user info.
Given conditions, some of other
content might also transfer
at this point in time and you
will get those other timeline
entries, but at least the most
important one made it across.
Finally, you want
to use ClockKit
to update your complication.
So there we go.
You have updated your
complication using PushKit.
We have added the new PK push
type complication to enable you
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We have added the new PK push
type complication to enable you
to very quickly update your
complication using information
that's on your servers.
There's a couple of
restrictions to be able
to use these push types.
The complication must be active
on the current clock face,
otherwise these pushes
will not be delivered,
and there will be a limited
number of pushes per day,
so use these sparingly.
Roughly one to two
per hour on average,
but the sports app may use them
all in a short period of time.
You would use transfer
user info to queue
up your timeline entries.
And finally, you would use
transfer current complication
user info to queue
up the current
or present timeline entry.
Finally, you would use ClockKit
to update the clock face.
Keep in mind that a lot of
these things are budgeted.
Any work you do on behalf
of complication update,
both on the iOS and
on the WatchKit side,
counts against this budget.
So we recommend that
any information you need
to update your complication
is included in that push.
The complication push type has
a 4 K payload, which is larger
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The complication push type has
a 4 K payload, which is larger
than the standard, so that
should enable you to put most
of the content you needed.
If you receive one of
these pushes and you turn
around in your iOS app and
do an NSURLSession request,
you will run out of
budget much faster.
So make sure you include all the
content you need in the pushes.
That brings us to the
end of our session.
Briefly going to discuss
what we talked about today.
We went through the
WatchConnectivity framework
and APIs.
We look forward to seeing what
you guys can do with these APIs.
We briefly discussed
NSURLSession and its use.
There's other sessions that
go more in depth on what
and how to use NSURLSession.
And finally, we discussed how to
get data to your complications,
which is a more advanced topic.
There's a lot of other great
resources to check out.
We have some great sample code,
and we have our evangelist
that's ready
to answer any of your questions.
As far as other related
sessions,
we especially suggest you check
out Creating Complications
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we especially suggest you check
out Creating Complications
with ClockKit and
Networking with NSURLSession,
as those are closely related to
the content we discussed today.
Thank you very much.
[Applause]