Adopting Handoff on iOS and OS X

Session 219 WWDC 2014

Handoff allows people to seamlessly move activities between devices and pick up right where they left off. Learn how to save, transfer, and restore user activities in apps of all architectures. See how easy it is to add Handoff support to your iOS and OS X apps to make your user experience even better.

[ 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.

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, 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.

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 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 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, 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.

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, 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 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.

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.

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 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.

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, 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 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, 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 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.

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.

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.

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 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.

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 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, 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.

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.

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.

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 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 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 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 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 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 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 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.

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, 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 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 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 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, 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.

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.

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 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.

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 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 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.

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 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.

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, 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 ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US