Implementing Handoff with the Apple Watch and iOS

| | 0 Comments| 10:47 AM
Categories:

Editor’s note: This tutorial requires a paired Apple Watch and iPhone. No simulators here.

You often hear Apple fans express their admiration for the Apple ecosystem with a single phrase: “It just works.” Handoff is a new and exciting technology that aims to continue to provide an immersive and seamless “it just works” experience when using Apple devices. Apple describes Handoff this way: In iOS 8 and OS X Yosemite, Handoff lets users start an activity on one device and seamlessly resume the activity on another device. Provide continuity for users with multiple devices by supporting Handoff in your apps and websites..

Add Watch OS (the official name of the Apple Watch operating system) to the list of Apple’s products that are capable of utilizing Handoff. To see it in action, open the Messages application on your Apple Watch and navigate to a conversation. Activate the Lock Screen on your iPhone and notice the frosted Messages icon in the lower left corner. If you swipe up from the frosted Messages icon and unlock your phone iOS will launch Messages and go directly to the conversation you were viewing on the Apple Watch.

handoff_icon

In this tutorial we’ll go through implementing a basic handoff from an Apple Watch app to the iPhone. Our application, Airports!, presents a list of major airports. Selecting the airport will bring up a MapKit-driven map of the airport. The Apple Watch companion application provides the same functionality.

As with Messages, the Airports! UI on the Apple Watch is limited. With Messages you are limited to selecting canned responses or relying on Siri. With Airports! the map is static and doesn’t provide zoom or pan functionality. With Handoff, however, we can provide a (mostly) seamless transition from looking at the map on the watch and looking at the map on an iPhone.

Starter Application

To get started, download the Airports! starter application. Open the handoffexample project and select the Airports! scheme. Prior to implementing Handoff you can run the basic application with the iPhone and/or Apple Watch simulator. However, to test Handoff itself you will need a paired Apple Watch and iPhone.

Implementing Handoff

There’s a distinct set of steps to go through to get your application “Handoff enabled.” Let’s take a look at each of the following:

  • Defining user activities
  • Updating Info.plist
  • Activating or deactivating Handoff notification broadcast on the Apple Watch
  • Handling Handoff on the iPhone application

Handoff is based around the concept of allowing the user to seamlessly resume an activity started on one device on another. For example, composing an e-mail is an activity. Reading a web page is an activity. Interacting with a specific airport map is an activity. When thinking about adding Handoff support to your application you should first start with enumerating the list of activities you want to allow your user to resume on a target device.

Once you’ve identified the activities you should transform them into a list of NSUserActivity activity types.

Activity NSUserActivity Activity Type
Viewing a Map it.iachieved.handoffexample.viewing.map

The assigned NSUserActivity activity type will be added to the Info.plist of the applications that support the activities. An application declares support for a given set of activities by enumerating each of its NSUserActivity identifiers in the NSUserActivities array in the Info.plist:

handoff_nsuseractivitytypes

For Handoff support between the Apple Watch and iPhone, will only need to do this in the Info.plist of the iPhone application. This is because the Handoff direction between Apple Watch and iPhone is one-way; while you can resume what you are doing on Apple Watch on the iPhone, the reverse is not true.

Before continuing complete this step by opening the Info.plist under handoffexample/Supporting Files and create an array named NSUserActivityTypes and add one element of type String with the value it.iachieved.handoffexample.viewing.map.

Broadcasting the Handoff Activity (or Lack Thereof)

When you first launch your application on the Apple Watch the initial WKInterfaceController awakeWithContext method will be called. At this point I recommend adding a call to invalidateUserActivity(). For some reason in my testing I’ve found that without this call a Handoff activity is broadcasted prematurely causing the iPhone to display the Handoff icon before an activity is actually available.

Add the following line to the willAwakeWithContext method in InterfaceController.swift, right after the call to super.willAwakeWithContext(context) call:

Now, when a user presses on an airport in the list we transition to the Apple Watch map controller. At this transition point we want to broadcast (via Handoff) that a map viewing activity is taking place on the watch. We use the method updateUserActivity to accomplish this as follows:

There are a couple of things to note here:

  • the updateUserActivity is a method of the base WKInterfaceController class
  • We are not creating an NSUserActivity instance (the method used in the Handoff Programming Guide)
  • We supply it.iachieved.handoffexample.viewing.map as our activity name
  • The userInfo dictionary provides information as to which airport we are viewing

Let’s look at these in turn.

First, if you start with the Handoff Programming Guide from Apple you will not find any mention of the updateUserActivity method, but instead are given an introduction to using NSUserActivity. Using NSUserActivity routines in the Apple Watch extension don’t work (or rather, I couldn’t use them); you need to use updateUserActivity, a method of the WKInterfaceController instead.

Second, we supply our activity name it.iachieved.handoffexample.viewing.map. It bears repeating that this must align with the value we declared as a supported activity in the Info.plist for the iPhone application.

Finally, our iPhone application needs to know which airport we are viewing on the Apple Watch. We provide that information by supplying a userInfo dictionary with a key-value pair of the IATA airport code we are viewing (which is available via the Airports dictionary). Note that what we are providing here is arbitrary; we could have just as easily provided the index location in the Airports array if that value was readily available in the WatchMapController class. Since we had an airport dictionary (which contained the iata key), we used that instead.

Before continuing let’s add the call to our WatchMapController willActivate method as follows:

Once we call updateUserActivity the Handoff routines built into Watch OS will broadcast this information (via BTLE) and the paired iPhone will display a Handoff icon for the Airports! app. Swiping up on the icon and unlocking the phone will launch the iOS Airports! application to the initial view controller (the table view). This is a good start, but we want it to go to the same map we were viewing on the watch. Let’s add the plumbing in the iOS application to be able to take action with the Handoff notification and data.

Handling Our Handoff Notification

We’re interested in implementing one method to add to the AppDelegate of our iOS application. It can be easy to forget that the AppDelegate is in large part a collection of application entry methods that iOS will call. Since Handoff is a part of the OS, it stands to reason that our initial interaction with Handoff will be a call to an application delegate method. In this case we want to implement continueUserActivity. Here is our implementation:

Go ahead and add the above to the iPhone AppDelegate code.

Let’s take a look at our implementation of continueUserActivity. First, we want to make sure that we have a non-nil root view controller, because we will be making use of its childViewControllers property and the first entry of the array (which is our TableController). Our TableController will need to be able to act upon the restoreUserActivityState call, so add the following to the TableController class:

Recall that the NSUserActivity object is just like an NSNotification; it has a name and is bundled with a dictionary of attributes. In our Apple Watch routines we called

and provided a key-value pair for the IATA code of the airport we were viewing. Unpack this value in the restoreUserActivityState routine of the iOS TableController to find the appropriate Airports array index. Once we find the index set the selectedIndex property and then invoke the segue for the TableController. That’s it!

That’s it! You should be able to successfully test Handoff with your paired Apple Watch and iPhone!

  • Connect your iPhone to your Mac
  • Open the handoffexample project in Xcode
  • Select the Airports! scheme with your iPhone as the target to run on
  • Run the application

The Airports! application will load and run on your iPhone, and at the same time the iPhone will communicate to your Apple Watch the availability of the application. Now,

  • lock your iPhone
  • go to the Home screen of your Apple Watch
  • launch Airports! (white airplane in a blue circle)
  • select an airport from the list

In this example, we’ve selected Hartsfield-Jackson International in Atlanta, Georgia:
IMG_4011

While the airport map is being displayed on your Apple Watch, press the Home button on the iPhone to bring up the lock screen. You should see a frosted airplane logo in the lower right, like so:

IMG_4010

Press the airplane icon and swipe up, and unlock your phone. iOS should go immediately to the Airports! app and display the same map as being displayed on the iPhone!

IMG_4012

Final Thoughts

Our Handoff implementation is straightforward. Note that we don’t inspect the contents of the activity in any of our routines because we have only declared a single activity type for our application. If our application provides additional user activities we can inspect the activity type to determine how to walk through our view controllers and update the application. See the Raywenderlich.com Handoff tutorial for an excellent example of this.

Get the Code

Get the code! Below are links for the starter application, a full Handoff implementation, or the Bitbucket repository for the code.

Additional Handoff Tutorials

There are more Handoff tutorials on the web that you may find useful (I know I did):

These tutorials are focused on a more generic implementation of Handoff which can be used for iPhone to Mac handoff, or Mac to iPad, etc. Our tutorial has focused on Handoff from the Apple Watch which is a much more specific use case. For example, Handoff can be used for establishing activity continuation streams between devices.

Follow us on Twitter

Did you enjoy this tutorial? Follow us on Twitter at @iachievedit!

Leave a Reply

Your email address will not be published. Required fields are marked *