Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
>> All right.
Good afternoon everyone.
Thank you for coming here
to Adopting Handoff
on iOS and OS X.
I'm Michael Jurewitz, I'm the
Lead Engineering Project Manager
for Continuity and I'm super
excited to be here today to talk
to you about Adopting
Handoff in your applications.
A little bit later, I'm going
to be joined by Vince Spader
and Keith Stattenfield to
talk more in this session,
but as you saw on Monday,
Handoff is a huge part
of the releases for
both iOS and OS X.
And I'm really happy to be the
second person at least this week
to be able to talk to
you more about them.
So if you look at this feature,
you can really see the big goal
is that we want it to be easy
for the user, for all of you to
take what they're doing and move
that between whatever Apple
device they happen to be using.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that between whatever Apple
device they happen to be using.
So all continuity, Handoff in
particular, is all about having
that seamless experience between
apps on different devices.
So now today, we'll take a look
at what we're actually going
to learn in this session today.
So first of all, we'll take a
quick look at what is Handoff.
How does this feature work?
You saw a little bit
about that on Monday,
and so we'll dive
into that a bit more.
Next, we'll take a look at
actually adopting Handoff
in your applications, so
that's a super-simple API.
I'm very happy to be
able to tell you that.
There are different integration
points in both AppKit and UIKit
for being able to take
advantage of this in your app,
and so we'll take
a look at those.
And then we'll round things
out by actually taking an
in depth look at some pretty
advanced things you can do
with Handoff that
are super cool.
All right, so what is Handoff?
Well, I think we've all been
stuck here before, right?
You're using one
of your devices.
Maybe you're browsing
on a website,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Maybe you're browsing
on a website,
and what you really
want to be able to do is
to take what you were
doing on that device
and move over to another one.
And a lot of times, that
can be kind of cumbersome.
Maybe you're having to go and
fish out the app on your iPad.
You have to go back to
where you were in that app,
reload a bunch of state.
Maybe you didn't even
have your data sync
across in the first place,
so it's even more of a pain.
And so Handoff is all about
making it super simple to make
that jump between your devices.
So as you're on your Mac and
you're browsing something
like Safari, on the lower left
corner of your iPad or any
of the rest of your devices,
you'll see an icon shows up.
You can swipe that icon up and
we immediately bring you back
to exactly what you were
doing in that application.
Wonderful seamless experience.
And so how do we
actually do all this?
What's going on?
So the first thing to know
is that this is entirely
about the proximity of your
devices to your other devices.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
about the proximity of your
devices to your other devices.
So we use BTLE to
actually let devices
around you know what they should
show in that lower left corner.
And which devices actually
show your apps is linked
to the devices that
you are signed
into with the same
iCloud account.
So what we do is we know that
you're signed into this device
and we actually will
use the Cloud
to BTLE pair your different
devices to each other.
And when you actually go to
actually continue an activity
on another device,
we're actually moving
that data directly
from where you were
to where you want to go.
So it's all about
directly moving information
about that activity itself.
Now, where are you
actually going
to find the UI for Handoff?
Well, there's two
places on both platforms.
First, what you'll find is that
on iOS in the lower left corner
of the Lock screen, if
you're using your Mac
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of the Lock screen, if
you're using your Mac
and other iOS device,
you'll see this icon show up
and this is your clue
that you can swipe this
up to get right back
to that application.
You'll also find that if you
double tap the Home button
and bring up the Multi-tasking
Switcher, you can go all the way
to the other side of the home
screen, and you'll see a pane
for an app that we see
that we know is nearby,
so you can resume from there as
well; so two different places,
super convenient
to get to those.
Now on the Mac, you've actually
got UI that's going to stick
around for you here in the
Dock if you're using an app
on a different device.
So for example, if I'm
on my iPad, or my iPhone,
or maybe even a different
Mac that I'm using something
like Mail, you'll
see it actually show
up in the Dock right at the end
there, and you can click on it
to get back to what
you were doing,
essentially to start
the handoff.
You'll also find it in Command
tab so that you can go ahead
and hit Command tab, cycle
through your apps that are there
and choose to pull
over the activity
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and choose to pull
over the activity
that you want to
be working with.
Here you see this is you know
mail that came from my iPad.
Okay, so when it comes
to adopting Handoff,
there's really three main things
that you need to think about.
The first, and really this is
kind of the most important,
is deciding what activities
in your app you really
want to support.
Now this, it's kind of an
interesting thing to talk about,
but the fundamental unit
in Handoff is this
notion of an activity.
And what you really
want to be thinking
about are what does
the user think
about as they're using your app
about constituting say
different types of things.
So for example, in Mail,
you've got a very clear split
between I'm reading email
versus I'm writing email.
Those are two different types of
activities that you would want
to have and indeed that we do
have, in the case of Handoff.
So what you're trying to do is
not only provide a nice grouping
of these discrete tasks not
only just to make things simpler
for you, but also because,
as we'll talk about later,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
for you, but also because,
as we'll talk about later,
when you go to resume these
activities on another device,
having some clear
separation between what type
of activity you're
actually trying to resume,
can be really important.
It can help you do
some much better things
in the user experience.
All right, so step two,
is actually creating the
activities in the first place.
This is also really easy to do.
We'll have a lot
to go into to talk
about how to actually do this.
And third and final,
you need to be able
to handle incoming activities
into your application.
So if your app gets
launched or gets resumed
and the system hands you
this activity to deal with,
you need to make sure that you
are basically taking the user
back to where they want
to be in your application.
So I mentioned this before,
but the fundamental unit here is
the activity when you're dealing
with Handoff, and specifically,
this is a single class.
It's NSUserActivity, and
I'm very happy to tell you
that it is only one class.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that it is only one class.
It has only a few
methods and it's the same
across both iOS and OS X.
So you get a chance to learn
how to do this in one place
and it's going to map completely
to the other platform.
You don't have to worry
about huge differences in API
or anything, so super
simple to use.
So as you're working
with NSUserActivity,
so you've got something
like Mail, for example.
As you are composing an
email, this is a great example
of a discrete activity
that's being done.
This is Mail saying
I'm composing.
And when you actually tell
this activity to become active
and become current, what
Become Current does is tells us
that this device should
start broadcasting to devices
around it that hey, this --
the user is doing something that
can be handed off and the rest
of the devices then can know
to be able to show things
like the icon that
corresponds to that app.
Now again, when you make
one of these activities,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
this broadcasting goes out.
You end up with in the very
lower left corner there,
you get this icon that shows
up, and the user can go ahead
and resume from there.
Now the key thing here is that
when the user actually slides
up on the Lock Screen, the
device where they're going
to actually connects back
to the original device,
and what it's basically saying
is hey, the user is here.
They want this data quick.
Give me information
about the activity
that they were actually doing.
We handle all that for you.
The activity gets packaged up.
We send it across to the device
where it's being resumed.
The system gets it.
Your app gets launched,
and that's it.
Super simple to work with.
Now Handoff also has
two really cool features
that you can take advantage
of even beyond just
basic activities.
The first is the ability to
actually create live streams
between two different instances
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
between two different instances
of your app on different
devices.
So it's part of doing
that handoff
and resuming on another device.
You can actually have
that app connect back
to the original machine and
just get an open NS stream
on either side to be able to
keep talking back and forth.
So as you can imagine,
there's some pretty cool stuff
that you can do with that.
Now second, and I imagine
for some people in this room,
this will be a really big
deal, we support Handoff
between native apps that you
own and websites that you own.
So if you've got [applause]
-- so if you've got an iOS app
and a really great website,
you could actually handoff
seamlessly between the two,
so that's really, really, cool.
All right.
So now for today's
agenda, we're going to go
into a few things here.
We're going to take a look at
specifically AppKit and UIKit
as support for Handoff and what
that looks like, how
you adopt things.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We'll talk about working with
NSUserActivity directly just
so you can understand
the API there,
some more advanced
things that you can do.
We'll get into Native App to
website Handoff and also look
at using these continuation
streams between different apps.
And with that, I'd like to hand
it off to Vince Spader to talk
to you about adopting
Handoff in your app.
Thank you [applause].
>> Thank you Jerry.
So adopting Handoff in your
app, there are three key pieces
to the AppKit and UIKit
support in Handoff.
The first is creating these user
activities and advertising them
to your nearby devices.
The second is updating the
user activity with your state
about what the user
is doing in your app.
And the third is using that
information that you put
in to resume what the user
is doing on another device.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in to resume what the user
is doing on another device.
So starting at the beginning,
creating user activities.
The first step is to kind
of take a step back
before you write any code
and ask what do users
do in my app.
Hopefully this is a pretty
simple question to answer.
Here's an example from OS X.
What's the user doing here?
Well, they're composing
an email message.
Here's another example from iOS.
What are they doing?
Composing an email message.
It's the same activity
on a different device.
Some other examples of
activities would be things
like reading messages,
picking an item from a list
or editing a document.
These are fairly
continuous things
that the users engaged
in in your app.
Things like clicking a
button or typing the subject
of an email are not
really user activities.
Those are kind of parts
or actions that are part
of the user activity,
so they're fairly broad
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of the user activity,
so they're fairly broad
and they're continuous.
So once you've identified
what your app is --
what users are doing
in your app,
you'll find that usually
there are some UI elements
that are responsible for
presenting that to the user.
Maybe it's a window
or a View Controller.
And on iOS8 and OS X Yosemite,
NSDocument, UI Document,
NSResponder and UIResponder
have a user activity property.
And remember that
Responder is a base class
for a lot of UI elements.
It includes views, windows, view
controllers, window controllers,
so odds are good
if it's in your UI,
it has the user activity
property.
And you use it kind
of like this.
So the first step is to create
an NSuserActivity instance
and you do that with
[Inaudible] ActivityType.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and you do that with
[Inaudible] ActivityType.
And you're passing it in
ActivityType, which is a string
that identifies the kind
of activity that this is.
It will also appear
in your info key list,
and we'll talk a little
bit more about that later.
So after you have the
NSUserActivity instance,
you can configure it.
It has some properties
like a title.
And then you set it on your
document or responder just
like any other property.
So for document-based
apps, it's even easier.
All you need to do
is add NSUbiquitous
DocumentUserActivity
Type to your info P list
under each CFBundleDocumentTypes
entry.
The basic idea is you're giving
us an activity type for each
of your document types
and it's perfectly okay
to have the same activity type
for multiple document types.
And when that is
in your INFOP list,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And when that is
in your INFOP list,
we will automatically create
the NSUserActivity and set it
on your document when the
document is in iCloud.
On OS X, since NSDocument can
move out of iCloud and back
into iCloud, we will be
updating that user activity
when that happens, so we
will set the property to nil
for example, when it
moves out of iCloud
and then create a new one if
it gets moved into iCloud.
And if you want to
know that is happening,
if you're using the UserActivity
and sharing it maybe,
you can use KVO and just
observe the UserActivityKey
on the document.
So for other apps that are not
document based, you still need
to put your activity
types in the INFOP list,
but it goes under this
NSUserActivity Types array
that's at the top level.
And again, we'll talk more
about activity types later.
For documents and responders,
when they have a UserActivity
property set on their document
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
when they have a UserActivity
property set on their document
or responder, AppKit and
UIKit will manage it for you.
What this means is We
call becomeCurrent,
and becomeCurrent is a
method on NSUserActivity
that makes the NSUserActivity
the one user activity,
the current user activity
that the users engaged in
and it gets advertised to all
of your nearby applications.
So here's what this
kind of looks like.
So the iPhone has
an NSUserActivity
and become Current gets called
on it, and that gets advertised
to all of your nearby
devices, and they show
up in the Lock screen, or
the DOC, or what have you.
So becomeCurrent, you can
call it yourself, but AppKit
and UIKit will also call
it at some key points,
so you probably don't have to.
On iOS, this means that
when your app is launched,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
On iOS, this means that
when your app is launched,
comes into the foreground
or tabs are switched,
UIKit will walk the View
Controller hierarchy including
presented view controllers,
and we're only looking
at view controllers that
have views which are
in the view hierarchy.
And if those -- if we
find a View Controller
that has a user activity,
that's the current user activity
and we call becomeCurrent on it.
Additionally, when
userActivity is set initially
on UI View Controller,
if the View Controller's
in a transition, will
wait until it's finished,
but if the View Controller's
views in the window hierarchy,
we will automatically
becomeCurrent on it immediately.
So that's UI View Controller.
UI Document will not become
current automatically,
but it's really easy
to share it with a --
share your user activity with
a View Controller that will.
All you need to do is set the
View Controller's User Activity
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
All you need to do is set the
View Controller's User Activity
to that document's User
Activity and whenever
that View Controller is
found according to the rules
in the previous slide,
it will become current.
On OS X, it's a little
bit different.
AppKit looks for a
userActivity in two places.
The first is the main
windows responder chain.
We go -- we start at the
first responder and go
through the next
responders looking for one
with a user activity
set, and if we find it,
that's the current activity
and we call becomeCurrent
on that userActivity.
And we'll also look at the main
window controller's document,
and if it has a User Activity,
we'll call becomeCurrent
on that.
And we will also reevaluate
this when appropriate
as the main window changes or
the user activity gets set.
We'll do this search
and find the right thing
to become current on.
So we manage it for you.
We call becomeCurrent.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
We call becomeCurrent.
We will also call Invalidate
and Invalidate is a method also
on NSUserActivity, which means
that this activity is finished.
The user's done with it.
They put it away.
They're no longer doing
what they were doing.
So that looks something
kind of like this.
So the same set up as before.
The iPhone has the current
activity that's being advertised
to your nearby devices,
and Invalidate gets called
and it stops viewing
the current activity
so the user can't
continue it anymore.
They've closed the window.
It's finished.
It's done.
So that's creating
user activities
and letting your nearby devices
know what the user is doing.
The next step is
putting your information
into the UserActivity to let --
so that you can restore the
state when it gets continued.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so that you can restore the
state when it gets continued.
So NSUserActivity has a userInfo
dictionary for this purpose.
It is yours to use.
You fill it out when
you get this call back,
so this is a method on
NSDocument, UIDocument
and the responders, update
UserActivityState, and you --
in this method, you fill out
the user activities user info
with the state of your responder
document and whatever you want
about what the user is doing.
Note that the userInfo is
emptied each time we make these
callbacks, so before
UpdateUserActivityState is
called on your documents
or responders,
we'll empty that out
on the userActivity.
So all you need to do
is add to the User Info.
You don't need to worry
about clearing out old data
or anything like that.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So it's looks something
kind of like this.
UpdateUserActivityState.
Don't forget to call Super.
There's a convenience method
on NSUserActivity called
UpdateUserInfoEntries
fromDictionary, and
that's a really easy way
to get your information
into the UserActivity.
And we will call this
at an appropriate time;
not necessarily when
the user continues,
but just at an appropriate
opportune time,
we will call this to
gather the information
about the user activity.
And when the info that you put
in the UserActivity
becomes stale,
there's a needs Save property on
NSUserActivity that's a Boolean
and all you need to
do is set that to yes
and we will call the
UpdateUserActivityState method
again later.
Set Need Save is very cheap.
You should call it
as your data changes
and we'll call you back later
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and we'll call you back later
with the
UpdateUserActivityState.
So what can you put
in the user info?
It's a dictionary and it
can store most POS types,
or all POS types and a few
others, including URL's.
And for URL's, file URL's are
obviously a little problematic.
The same path might point
to different places
on different devices.
The actual file might
not be there.
However, file URL's
in iCloud are okay,
and if you're iOS only, from a
document provider are okay too.
We will automatically
do some translation
so the file URL is
pointing to the right file.
Couple other things; keep the
minimal amount of information
in the userInfo, so
just put what you need
to get the user back
to where they are.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The transfer times for these
things can vary quite a lot,
so every byte really counts.
Just include the State.
So don't include your document.
If you're using iCloud,
fantastic.
All you need to do
is store the file URL
and then you can use that.
If your data is stored on the
web somewhere, put a reference
to that in your userInfo.
Also try to avoid
platform specifics.
Since this user activity
might be continued on a Mac,
or an iPad, or an iPhone,
there are a lot of things
like the visible rec
of a scroll view,
which don't really make sense
across all those contexts.
It's better to use a heuristic
like the middle item that is
in my scroll view and
storing or offset a reference
to that instead of
storing a visible rec
for each potential thing
you are continuing on.
Also, NS and UIDocument will
add their file URL automatically
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Also, NS and UIDocument will
add their file URL automatically
in their UpdateUserActivityState
implementations
with the NSUserActivity
Document URL Key.
So you don't need to --
it'll already be there.
Don't repeat it.
A couple other things;
think about versioning.
You're going to want to
future proof your app
as you make new versions.
You're going to have changes
to your user activities,
so you might want
to do something
like include a version
in your userInfo.
It could be something like this,
and here we're actually
using the application
didUpdateUserActivity app
delegate method, which is called
after any of your
documents or responders get
that UpdateUserActivityState
called on them.
So this is actually a
really good debugging point.
If you want to know what's
exactly is in your userInfo,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you want to know what's
exactly is in your userInfo,
set a break point
here and check it out
and it will tell
you what's in there.
So now we have put
our information
about what the user's doing
in the NSUserActivity.
The next step is using that
information on another device
to continue and get
the user right back
to what they were doing.
So again, this is on another
device, so your -- you --
the device has received an
advertisement that this user,
other user activity is
current on another device
and it's showing up
in the Lock Screen,
or the doc, or what have you.
And the user indicates
that they want
to continue your application,
and we will call this
on your app delegate.
Application will
ContinueUserActivityWithType
and note that there is
no NSUserActivity here.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and note that there is
no NSUserActivity here.
All you have is the
activity type,
and that's because we
haven't actually fetched the
UserActivity completely yet.
We will start fetching
it immediately after --
when you get this call.
And you should use this to show
the user what's being continued.
You have the activity type so
you have an idea of what kind
of activity it is and maybe you
can animate a View Controller in
or something so the
weight is perceived less.
So you should return yes
if you're handling this
and you're showing some kind
of feedback to the user,
or return no, or if
it's not implemented
and you'll get the
systems behavior.
Now, on iOS, this
means the default ping,
but on OS X, you get nothing.
So it's a really good idea
to implement this if you can.
Here's an example of what
that might look like.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Here's an example of what
that might look like.
So as you can see, we're
checking the activity type
and if it's a viewing
message activity,
we know that we're going to
need a MessageViewController
and so we set that up and
we show it to the user,
and then we return yes because
we are handling showing feedback
to that user about
that activity type.
So once the user
activity has been fetched
from the other device, we will
reconstruct the NSUserActivity
and give it to you in
this app delegate method,
ApplicationContinue
UserActivity RestorationHandler.
And this is the place where you
reconstruct the user's activity.
You get them back to whatever
it was they were doing.
Again, return yes if you've
handled this user activity.
Return no or leave
it unimplemented
and we can continue some
user activities for you.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and we can continue some
user activities for you.
So this restorationHandler; it's
a block that we pass into you
and you call it and you give
it an array of documents
and responders that are
presenting the user activity,
and we'll call a method that's
RestoreUserActivityState
that is paired with that
UpdateUserActivityState
from before.
Here's an example of what
that might look like.
So we're checking
the activity type,
and it's a viewing
message activity,
so we get our
MessageViewController
and we return yes, and note
we are also calling the
restorationHandler with
the ViewController.
And when you do that,
RestoreUserActivityState
is getting called
on that ViewController and
you can use the information
in the user info to kind
of reconstruct whatever
it is the user was doing
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of reconstruct whatever
it is the user was doing
but at a more local level, so
just for your ViewController.
You can also call this manually
as you can see at the bottom.
It's perfectly fine to call
RestoreUserActivityState
yourself, and that's
actually a useful pattern
to restore the UserState
and build your ViewController
hierarchy at the same time.
So that's if everything
goes well.
It's entirely possible
since there are bits flying
through the air that some
might collide and will fail
to retrieve the UserActivity
information
from the other device.
And if there was an
error, we will call this
on your app delegate;
applicationdid FailToContinue
UserActivity with type error.
And you should present
the error to the user
and do whatever clean
up you need to do here.
We make a guarantee
that for every time
that you get
WillContinueActivity,
you will get exactly one of
either ContinueUserActivity
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
you will get exactly one of
either ContinueUserActivity
or DidFailToContinueuserActivity
so you can rely on that.
We'll pair those.
Also take note that the error
can be NSUserCancelledError,
in which case you probably don't
want to bug the user about it.
Usually that will
happen if the user tries
to continue something while
we're still fetching information
about a previous activity.
We'll automatically cancel
that previous activity.
So that's all you need, but
for document based apps on iOS,
you continue the
UserActivity kind of like this.
So you want to get the URL
out of the user info using
that NSUserActivity
Document URL key
and create your Write
UI document subclass.
And then you can pass it
to the restorationHandler
and that UI document will get
its RestoreUserActivityState
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and that UI document will get
its RestoreUserActivityState
method called on it.
So it's pretty easy.
On OS X, AppKit can use
NSDocumentController
to do this for you.
So it will automatically
get out the file URL,
find the right document subclass
to create and create it.
And then we will pass -- we will
call RestoreUserActivityState
and give you the
user activity object
so you can restore
the UserState.
Here's the diagram to kind of go
through this continuing process.
So it's similar to before.
The iPhone has advertised
a User Activity
and the user is continuing
on their Mac.
And when the user indicates
they want to continue,
we will call ApplicationWill
ContinueUser Activity
with type under App Delegate.
And you've implemented it to
give the user some feedback
that it's being continued.
Maybe you've animated in a
ViewController or something
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Maybe you've animated in a
ViewController or something
so that by the time we
actually get the activity,
maybe the animation
is still happening
and the user doesn't
even have to wait.
So we will then ask the
device we're continuing
from for the information
about the User Activity
and we'll get it back, and we'll
recreate the NSUserActivity
instance and pass it to you
in applicationcontinue
UserActivity restorationHandler.
In that method, you use the
information in the User Activity
to get the user back to
whatever it was they were doing.
This might include
passing some things
into the restorationHandler
and if you do that,
we will call
RestoreUserActivityState
on them.
And then the user's right
where they left off.
You've created UserActivity on
one device, put your information
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
You've created UserActivity on
one device, put your information
about what the user is doing
into the NSUserActivity Object
and handed it off to another
device that is able to continue
and get the user back to
whatever it is they were doing.
So there's still a few more
details about NSUserActivity,
and there's a couple
other really cool things
that NSUserActivity can do,
and Keith Stattenfield will
be explaining them [applause].
>> Thank you very much.
It's exciting to be here
and show you what we've
been working on for a while.
And as Vince said, I'm going
to explain a little more
about how you might use this.
You've already seen
almost everything that's
in NSUserActivity by this point.
And AppKit and UIKit provide
a lot of functionality
that makes it really
easy to adopt this.
But if you have some need
to do something that's a
little outside the norm,
it's useful to understand at
the base of things what's there
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it's useful to understand at
the base of things what's there
and what you can do with things.
Jerry originally said, and
Vince pointed out you know,
NSUserActivities are just
objects that are alloc inited,
and we've talked about
this activity type string a
couple times.
So let's go into
that a little more.
You create one of these
UserActivity type strings
on your application and you
pass it to the Alloc init
or you've put it in
your CF bundle types
in the NS ubiquitous
document types key.
And where did you
get this string from?
In a sense, you just made it up.
That string is a string
that we as a system use
when an activity gets
received in a device,
to pick the application that
we want to show to the user.
And so in that sense, it's a
lot like a file type extension.
You know files have extensions
and those extensions bind them
to particular documents
when they're double clicked
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to particular documents
when they're double clicked
on or otherwise opened.
UserActivity type strings
are the same thing.
You make them up.
We don't display them to
users so they don't need
to be terribly comprehensible,
and we recommend that you start
with the base of
your developer ID
or your company's reversed DNS
name, and then add some suffix
to the end of that
to make it unique,
one for each extension
type that you have.
And then once you pick those,
you put them in your code
at the point you'll be
creating each of those types
of activities, and you would
put them in your Info P lists
so that we know that
your application would
like to handle these
types of activities.
In your Info P lists, they
go in one of two places.
They either go in the
CFBundleDocument Types array
as the NSUbiquitousDocument
userActivity type,
or they go as a string in the
NSUserActivityTypes at the top
of your applications'
Info P list.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of your applications'
Info P list.
We've talked about
how they're used
to pick the application
that's shown in the corner.
We allow all of the applications
from a single developer
to exchange activities
amongst themselves.
So if you have an
iOS application
and an OS X application,
you can exchange activities
between them.
If you have several
iOS applications,
each of your applications
could create activities
that were continuable in your
other iOS or OS X applications.
We do this based on the
Team Identifier that's used
in the way you've
built your application.
That means in order to use
Continuity to continue an item;
you either need to be an
application in the App Store
or you need to be assigned
a developer certificate.
Now applications don't
have to claim all
of the activity types they
create, and the corollary,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of the activity types they
create, and the corollary,
applications can
claim activity types
that they themselves
don't create.
And as an example, if you have
kind of a monolithic application
on OS X, say it's an application
that creates many different
types of things for the user
from the user's perspective.
The user can edit
pictures of cats.
The users can add queue
to videos about cats.
Users can keep all of their cat
books, or all of their pictures
of their cats in
their one application.
You'd have that as
an application.
It would have a bunch of
different document types.
When you're creating
activities, you know,
there are different activities
a user might be doing.
But on iOS, you might have
three separate applications.
You might have one
application where the user looks
at their cat movies or
plays their cat sounds.
You might have a different
application for their cat books.
You might have a
third application
where they can see
their cute cat pictures
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with the cute cat captions
on the bottom of them.
And when you built your
applications this way,
you would create four different
activity types; one that kind
of mapped to each
of your file types
and then your iOS
application in this case,
one of your applications
would claim two types
and your other applications
would claim other types.
And if a user continued
from OS X to iOS,
the appropriate icon would show
in the corner when they went
to continue from iOS back to
OS X, they would all come back
to your same monolithic
application.
Likewise, we've talked about
the NSUserActivity Object,
and it actually doesn't have a
lot that you need to set in it.
Activities have a
title, which you can set.
Keep it short just in case
it gets shown to the user
to know what they're doing.
They also have this User Info
Dictionary that anything you set
in there, the machine that
creates an activity is going
to show up in there and the
machine that receives it,
with minimal translation only of
URL's to point objects that are
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with minimal translation only of
URL's to point objects that are
in Cloud containers to
the same logical object,
the receiving device.
We do have the AddUser
ActivityEntries FromDictionary
to efficiently merge some
additional information
to the dictionary.
If you just set the user info,
then you're saying this is
exactly the user info I want
to be there.
After you've created an
activity, you or AppKit
or UIKit will call
becomeCurrent on it.
System then knows
this is the activity
that we should be sending out
that a user could continue
on another device, if your
application was the application
that the user was using
at that point in time,
and in the far future, you
would call ActivityInvalidate
to say we are done
with this activity.
The user has closed the document
or it's no longer appropriate.
It'll get removed
from other devices
and that you would
just de-allocate.
There are a few Delegate
calls as well
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
There are a few Delegate
calls as well
that you can take advantage of.
So if you set a delegate on
an NSUserActivity Object,
when we've decided that
we need the information
from your ActivityObject,
if you have implemented
UserActivity WillSave,
we will call that delegate
on a default priority cue
to give you one last chance
to fill in the user info
with whatever you think is
the correct info right now.
And that means the model
you should adopt is instead
of constantly trying to
set user info you know,
after every keystroke, if
the user is typing something
on one device, you should
set the NeedSaveProperty
of the user activity whenever
the user does anything
that would essentially
dirty the object
from the last time you told
us that here's an activity
and the last time we
asked you to fill it in.
And at some time in the future,
we will call you via this
userActivityWillSave,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we will call you via this
userActivityWillSave,
or in the UIKit and
AppKit delegates above us,
and we'll ask you okay,
now we actually need the
information for this activity
Perhaps it's being
sent to another device
because the user is
actually continuing it.
There are other reasons
that we sometimes ask you
to give us the info, and
that's your opportunity.
Fill in the user info
and when that returns,
we know that you've given us a
consistent set of information.
We have another delegate
which lets you know
that the activity was
continued onto another device.
When the user does
this, we will call
on the first device
userActivityWasContinued,
give you back your
UserActivityObject.
Many of you probably
won't need this.
We're hoping that you structure
your activities so that even
if they are continued
onto a second device,
that they remain perfectly
fine on the first device.
The user might pick it
back up in a few seconds
and keep using it there.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But if you have an activity
that's transactional in nature,
that you really only exist at
one place, if the user's filling
in a form as an example,
in our current lease.
If you're composing
a draft message,
when you continue draft
message to a second device,
back on the first device, Mail
will close that draft message
because Mail doesn't want the
user to have two draft messages
in two devices, and not know
which one is more recent.
So if you do have one of
these transactional kind
of activities, you
can take advantage
of userActivityWasContinued
and to do whatever you
need to do there for that.
Now Jerry talked about
our Website Handoff.
Website Handoff is the
ability for you as developers
to in your native applications,
if you have the kind
of application where you're kind
of really backing data
that's provided elsewhere.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
of really backing data
that's provided elsewhere.
It's not sitting in
a file in iCloud.
You know, perhaps it's some kind
of social browser application
where you have a
lovely iOS application
that presents a great
interface that takes advantage
of the features and the
device like the camera
and the touch screen to provide
a very high quality interface
to let people do things.
But you of course
also have a website
that other users can go to,
to kind of see the same thing,
or to interact with your Meta
application in the same way.
Your native application
can tell us that if a user
on another platform
doesn't have an application
which claims the activity
type that we're creating,
the user could also choose
to continue this activity
in a browser on that platform.
You notice here, we've
brought up our second device
and in the corner we see
Safari, and if the user clicks
on Safari, they would
get brought to a URL
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on Safari, they would
get brought to a URL
that you provided back
on the originating device
and from that, you know
hopefully, the user is able
to continue what they were doing
in your native application.
And if we take a quick look
at what that looks like,
you know you would
create an activity just
as we've seen elsewhere.
You would fill in the user
info so that if it is continued
to another native application
of yours on a second device,
you'd get a high
fidelity, full pass through,
and you'd also set
this additional field;
the webpage URL, and you would
say here's an HTTP or HTTPS URL.
If a user goes there
that's also okay,
and that will get them
to what they need.
Similarly, we can do the same
type of thing to allow a user
in a browser to continue into
a native application on iOS.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in a browser to continue into
a native application on iOS.
And you know what that would
look like is if the user was
at your website, say on desktop
using the information there,
when they pulled
out their iOS device
in the corner they
would see the icon
for your native application,
and if they clicked on that,
you know, they would get brought
into your native application
and you could restore them
in your native application
to the same thing they would've
been seeing in the web browser
on the originating device.
Now to do this, and you'd take
advantage of some new API's
that Apple's providing on iOS
that allow you to securely kind
of claim a domain name
for your application
so that you can say
I'm this developer.
I truly own these domain
names and I'm willing
to let these applications take
advantage of that domain name.
And the way you do that is
you add these domain names
to the com.apple
.developer.associated
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to the com.apple
.developer.associated
domainsentitlement
on your application,
and once you've done
that some code will reach
out to your website
periodically from the device
and after it's verified that
there's some information there,
we'll say this application
is allowed to continue
from this domain if another
device gave it to us.
And if that happens, you know
we bring up your application.
In your native application,
you know again,
your continueuser
Activityrestoration Handler is
going to get called.
And there, what you would do
is you would check the incoming
activity type to see if it was
this special NSUserActivity type
continuing from web browser.
And if it was, you know
there isn't a user info there
that you can take advantage
of because you don't have native
code running on the other side.
It's running in a web browser,
but you do have the webpage URL
that the user was at in
that browser and from
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that the user was at in
that browser and from
that webpage URL you could
bring your native app up kind
of looking at that same thing.
You also of course
would continue
to get any other
user activity types
that your application
has claimed.
So if it's any of those,
you would do the same
thing you saw earlier.
Lastly, let's talk about
Continuation Streams.
Continuation streams are a
way for you to get and set
up after user has chosen
to resume an activity
in a second device,
a bi-directional pair
of input streams
between the two devices
that you can use primarily we
think, for interactive purposes,
if the user's going to
continue to use both devices,
and you want to be able
to throw things back
and forth very quickly.
And what that looks like
you know is the users
in your native application
on your first device
and we see your icon in the
corner, and the user swipes up
and we bring up your
application.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and we bring up your
application.
And then we set up
a set of NS streams
between the two processes.
And once those are up,
you're free to read and write
from those streams
and communicate
between the two devices in a
fairly high performance manner
for whatever purposes
you want to do.
What that looks like in the
code again is you create
an NSUserActivity.
You set a delegate
and then you set this
SupportsContinuationStreams
property to yes
and you make it current.
And on the receiving device,
when the activity comes
in in continueuserActivity
restorationHandler,
you would check if that activity
supports Continuation Streams,
and if it does, you know you
can call this additional method,
get ContinuationStreams
WithCompletionHandler
and that gets past a block
and we, when you call that,
will reach back, try to set
up these pair of streams
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
will reach back, try to set
up these pair of streams
to the original device
and after they're up,
we will call your
CompletionHandler
with the streams.
If something goes wrong, we'll
call you with an NS Error.
So if you don't get an
error, you have streams.
You can start reading
and writing back
and forth between the two apps.
Simultaneously, back
on the first device,
the delegate method that
you set the delegate method
of the delegate you
set earlier is going
to get the didReceive
InputStream outputSteam call,
over there.
That's going to get the input
stream and the output stream
that are connected to the
other client and you're free
to start reading and writing
back and forth between the two.
[ Applause ]
So wrapping up, you learned
about the AppKit and UIKit.
It's very extensive.
It does almost everything
you need.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
It does almost everything
you need.
And particular, you
learned about NS
and UI documents support,
which makes it almost completely
free to implement this.
Many of you can add
about four lines of code
to your application
and you'll discover
that it starts continuing
between iOS device
to iOS device, or if you have
iOS and OS X applications,
from iOS to OS X merely
by adding a couple keys
to your InfoP lists
and making sure
that you're putting your
documents in iCloud.
You learned about the
ContinuationStreams
that we have.
If you want to set up
interactive, you know,
real-timish performance
between devices
after the user chooses
to continue.
You've also learned about how
you can continue to a website
or continue from a website
into your native applications.
For more information,
our Framework Evangelist,
I'm not sure if he's here,
but it's Jake Behrens.
There's his email address.
We have a very good programming
guide that's out and available
on developer.apple.com, The
Handoff Programming Guide,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
on developer.apple.com, The
Handoff Programming Guide,
which we hope answers
any questions
that you didn't get answered
today and has some examples
of how you might do things.
We have the Apple Developer
Forums where we're sure you
and other Apple employees
are going
to start helping each other
take advantage of this.
In terms of related sessions,
tomorrow morning there's a
session on Cloud documents.
As we said, Cloud
documents are a great way
to get the actual data
behind user activities sync
through the Cloud so that
when the activity goes
from one device to
the other device,
the data is available for it.
There's also this session
yesterday, which you can watch,
Your App, Your Website
in Safari,
which talks about
shared web credentials
and how you can let iOS devices
know that you have the right
to do things with domains.
And thank you all for coming.
Hope you have a good afternoon.
[ Applause ]