Continuity for Apple Watch and iPhone with Handoff

4 February 2016

Continuity was introduced with iOS 8 and Mac OS X Yosemite, and was primarily a method for first-party Apple applications to allow for transferring a task that was already in progress between an iOS device and a Mac. Very few third party appliations picked up support, so the feature only provided limited usability for most users.

However, with the release of the Apple Watch Continuity got a whole new lease of life. The Watch is designed to be used in very short bursts, ideally no longer than ten seconds. However, often a notification or app interaction can trigger the need to carry out an interaction that will take longer than is appropriate for the Watch, or would be better suited to a larger screen for activities such as data input, or reading a long piece of text. Using Handoff, a user can easily continue a task on their iPhone that they began on their Watch.

Today we will look at how to implement this in our own apps. The examples below are based on adding Handoff support to the Counties app that was originally created for the Adaptive User Interfaces with UIStackView and UICollectionView tutorial. You can download the Xcode project for it here. Specifically, when a user views a county's details on their Apple Watch we want to be able to hand this activity off to an iPhone, which will then display the details of the county that was being viewed on the Watch.

A county being viewing on Apple Watch. It is this task that we will enable Handoff for, so that users can continue viewing the county's details on their iPhone.
A county being viewing on Apple Watch. It is this task that we will enable Handoff for, so that users can continue viewing the county's details on their iPhone.

NSUserActivity on Apple Watch

Our journey begins with the NSUserActivity class which we first mentioned, but didn't use, last week in Indexing App Content with Core Spotlight. To recap, NSUserActivity allows us to capture the attributes of a particular task that is being carried out by a user at any given time.

We want to allow users who are viewing the details of a County on their Apple Watch to be able to handoff this task to their iPhone. For this purpose, WKInterfaceController has an updateUserActivity(type:userInfo:webpageURL:) method, which we can use to advertise a user's current activity. We make use of this in our CountyInterfaceController like so:

override func willActivate() {
    updateUserActivity("com.darjeeling.counties.handoff.countydetails", userInfo: ["CountyName": county.name], webpageURL: nil)
    super.willActivate()
}

The first parameter is a string that identifies the activity that is being advertised. This is used later on by the iPhone to identify the type of activity that the user wants to handoff. In the userInfo parameter we can supply arbitrary details about the activity. Here we just supply the name of the county that is being viewed - in your apps you can supply whatever data is relevant to the user's activity. We leave the final webpageURL parameter empty as this activity does not correspond to any online content; our app's data is local only.

Notice that we have put this in the willActivate() method, so that the user's activity will be advertised as soon as the County interface is displayed. However, when the user stops viewing the County details we will want to stop advertising this activity as it is no longer current. We do this with the following code:

override func didDeactivate() {
    invalidateUserActivity()
    super.didDeactivate()
}

When the interface is removed from the screen, it deactivates. At this point we call invalidateUserActivity() and the county viewing activity we setup earlier is no longer broadcast.

That's it! That is all we need to do on the Apple Watch side to advertise tasks for Handoff.

Responding on iPhone

At this point we need to configure our iPhone app to be able to handle the user activity that the Watch app will broadcast. To begin, we add the user activity type that we passed to updateUserActivity(type:userInfo:webpageURL:) earlier to our iPhone app's Info.plist:

An Info.plist with the necessary data to enable Handoff

This lets iOS know that our iPhone app can handle the user activity that the Watch wants to handoff.

Next, we need to update our application delegate to include an implementation of application(application:continueUserActivity:restorationHandler:) that handles the user activity created on the Watch.

func application(application: UIApplication, continueUserActivity userActivity: NSUserActivity, restorationHandler: ([AnyObject]?) -> Void) -> Bool {
    guard let userInfo = userActivity.userInfo, countyName = userInfo["CountyName"] as? String, selectedCounty = County.allCounties.filter({$0.name == countyName}).first else where userActivity.activityType == "com.darjeeling.counties.handoff.countydetails" {
        return false
    }
    showCounty(selectedCounty)
    return true
}

Here we use a guard's where clause to check that the activity type matches the one we specified when advertising the user activity on the Watch. As we only handle that activity type we return false to let iOS know that we didn't handle the activity.

If the activity type is the one we expect we unpack the userInfo dictionary from the user activity to access the county name passed to us from the Watch. We program defensively here and make sure to return false if we could not find the county specified by the activity.

Having unpacked the county name we use it to access the county that the user was viewing, and pass it to our showCounty() method to present the county to the user. In your own code you will replace this with whatever is necessary to present the UI required to continue the user's activity.


This is all of the code we need on the iPhone to implement Handoff! Let's take a look at it in action. When viewing a county on Apple Watch we see the following on our iPhone's lock screen:

Handoff being advertised on the lock screen.
Handoff being advertised on the lock screen.

Notice that our app icon is showing in the bottom left corner. If the user swipes up on this icon they will be taken straight in to the app and shown the county they were viewing on Apple Watch.

Conclusion

As you can see from the examples, adding continuity to support for your Apple Watch app is a very simple process requiring surprisingly little code. I encourage you to check out the sample project yourself to get a feel for Handoff in action, and to think about how supporting this feature could make your app even more useful to your users, and a better citizen in the iOS ecosystem.