A beginners guide to implementing Handoff

Handoff is one of the coolest new features in OS X Yosemite and iOS 8. It allows me to do cool stuff like start composing an email in one device and finalising and sending it in another.

Magic? No! All it takes in some adventurous development backed up by the proper amount of tea or coffee.

How does it work?

Thanks to a combination of NSUserActivity and the OS that sends instances of those activities back and forth between devices. But that is not the best part. The best part is that NSDocument, UIDocument, NSResponder and UIResponder (and therefore all UI classes on both OS X and iOS) have a new property called userActivity which is, surprise, of NSUserActivity type.

So basically, all you have to do, is create a new instance of NSUserActivity in the view / view controller / window / document that you want to be advertising itself as allowing Handoff. The system will take care of sending that activity to other instances of your app in other devices.

After that, you will also need to add a couple of methods to your application delegate, and done!

By the way, Handoff is allowed between multiple instances of iOS apps, between iOS apps and OS X apps, and even between apps and websites.

Neat! How can I support Handoff in my app?

In order to be able to implement Handoff in your Mac / iOS app, all you need to do is…

Declare your activity types in the app’s Info.plist file

A picture is worth a thousand words, so:

Screen Shot 2015-04-07 at 14.16.00

Create activities

Not that kind of activities.

A view controller that wants to expose itself as “continuable” might do something like this:

let userActivity = NSUserActivity(activityType: "com.ctarda.handoffsample.mainlisting")
userActivity.title = "Main listing"
self.userActivity = userActivity

The system will call becomeCurrent in the view controller automatically if said view controller is in the view hierarchy, but if it is in transition, it will call it after the transition has been completed. Why should you care about that? Because calling becomeCurrent, starts advertising the activity (which is what you are supposed to want, by the way).

Also, when the view controller is removed, or in general, when the system decides that the activity is not needed anymore, it will call invalidate in the activity.

Put information in the activity

Usually, you would want to put some context into the activity. After all, that is the way you will be able to recreate the state in the destination app. You can do that by putting information in the activity’s userInfo dictionary.

In order to do that you should override updateUserActivityState in your UIResponder subclass:

public override func updateUserActivityState(activity: NSUserActivity) {
	//don't forget to call super!!!
	activity.addUserInfoEntriesFromDictionary(["category": listingCategory])

You can put in that dictionary anything that can be serialised to a plist file, in other words, the usual suspects: NSArray, NSDictionary, NSString, NSURL… you get the idea. Be frugal, though, the information you put in the dictionary has to travel the interwebs to reach other devices, you might want the payload to be as light as possible.

Allow your app to reconstruct its state from an activity

This is the neatest part of the whole thing. In your app delegate, the system will call application:willContinueUserActivityWithType. That’s the moment when you need to provide visual feedback to the user indicating that something is about to happen. For example, that would be the perfect moment to start transitioning to a new view controller.

func application(application: UIApplication, willContinueUserActivityWithType userActivityType: String) -> Bool {
	if userActivityType == "com.ctarda.handoffsample.mainListing" {
		let mainListing = MainListingViewController()
		return true
	return false

Now, for each call to willContinueUserActivityType, the system guarantees that there will be a call to either a success or an error handler.

The success handler will be called when the system fetches the user activity. The system will pass a block that you will have to call yourself, passing it an array of whatever you want to respond to that activity (in most cases, that would be an instance of the VC you created in the previous step).

func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]!) -> Void) -> Bool 
	if userActivityType == "com.ctarda.handoffsample.mainlisting" {
		let mainListing = MainListingViewController()

Now, in your MainListingViewController, the system will call restoreActivityState. That’s where you can recreate your UI state, according to whatever you receive in the activity’s userInfo. You can also call this method manually if you need to.

public override func restoreUserActivityState(activity: NSUserActivity)
	//don't forget to call super!!!
	let userInfo = activity.userInfo	


If the system can not fetch the user activity, instead of continueUserActivity, it will call didFailToContinueUserActivity in your app delegate


I would definitely recommend spending one hour watching the relevant WWDC video, and checking out the Handoff Programming Guide.

Handoff is one of the major new features in the latest iteration of Apple’s Operating Systems. It is easy to implement, and the results, in terms of user experience, are definitely worth the effort.

Leave a Reply

Your email address will not be published.