{"id":1425,"date":"2015-05-24T10:47:03","date_gmt":"2015-05-24T16:47:03","guid":{"rendered":"http:\/\/dev.iachieved.it\/iachievedit\/?p=1425"},"modified":"2015-05-24T10:47:03","modified_gmt":"2015-05-24T16:47:03","slug":"implementing-handoff-with-the-apple-watch-and-ios","status":"publish","type":"post","link":"https:\/\/dev.iachieved.it\/iachievedit\/implementing-handoff-with-the-apple-watch-and-ios\/","title":{"rendered":"Implementing Handoff with the Apple Watch and iOS"},"content":{"rendered":"<p><b>Editor&#8217;s note:<\/b>  This tutorial requires a paired Apple Watch and iPhone.  No simulators here.<\/p>\n<p>You often hear Apple fans express their admiration for the Apple ecosystem with a single phrase:  &#8220;It just works.&#8221;  <i>Handoff<\/i> is a new and exciting technology that aims to continue to provide an immersive and seamless &#8220;it just works&#8221; experience when using Apple devices.  Apple describes <a href=\"https:\/\/developer.apple.com\/handoff\">Handoff<\/a> this way:  <i>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.<\/i>.<\/p>\n<p>Add Watch OS (the official name of the Apple Watch operating system) to the list of Apple&#8217;s products that are capable of utilizing Handoff.  To see it in action, open the <b>Messages<\/b> application on your Apple Watch and navigate to a conversation.  Activate the Lock Screen on your iPhone and notice the frosted <b>Messages<\/b> icon in the lower left corner.  If you swipe up from the frosted <b>Messages<\/b> icon and unlock your phone iOS will launch Messages and go directly to the conversation you were viewing on the Apple Watch.<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/05\/handoff_icon.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/05\/handoff_icon.png\" alt=\"handoff_icon\" width=\"312\" height=\"554\" class=\"alignnone size-full wp-image-1462\" \/><\/a><\/p>\n<p>In this tutorial we&#8217;ll go through implementing a basic handoff from an Apple Watch app to the iPhone.  Our application, <b>Airports!<\/b>, 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.<\/p>\n<p>As with <b>Messages<\/b>, the <b>Airports!<\/b> UI on the Apple Watch is limited.  With <b>Messages<\/b> you are limited to selecting canned responses or relying on Siri.  With <b>Airports!<\/b> the map is static and doesn&#8217;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.<\/p>\n<h3>Starter Application<\/h3>\n<p>To get started, download the Airports! <a href=\"https:\/\/bitbucket.org\/joeiachievedit\/handoffexample\/get\/master.zip\">starter application<\/a>.  Open the <b>handoffexample<\/b> project and select the <b>Airports!<\/b> 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.<\/p>\n<h3>Implementing Handoff<\/h3>\n<p>There&#8217;s a distinct set of steps to go through to get your application &#8220;Handoff enabled.&#8221;  Let&#8217;s take a look at each of the following:<\/p>\n<ul>\n<li>Defining user activities\n<li>Updating <code>Info.plist<\/code>\n<li>Activating or deactivating Handoff notification broadcast on the Apple Watch\n<li>Handling Handoff on the iPhone application\n<\/ul>\n<p>Handoff is based around the concept of allowing the user to seamlessly resume an <i>activity<\/i> 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.  <\/p>\n<p>Once you&#8217;ve identified the activities you should transform them into a list of <code>NSUserActivity<\/code> <i>activity types<\/i>.<\/p>\n<table>\n<tr>\n<th>Activity<\/th>\n<th>NSUserActivity Activity Type<\/th>\n<\/tr>\n<tr>\n<td>Viewing a Map<\/td>\n<td>it.iachieved.handoffexample.viewing.map<\/td>\n<\/tr>\n<\/table>\n<p>The assigned <code>NSUserActivity<\/code> activity type will be added to the <code>Info.plist<\/code> of the applications that support the activities.  An application declares support for a given set of activities by enumerating each of its <code>NSUserActivity<\/code> identifiers in the <code>NSUserActivities<\/code> array in the <code>Info.plist<\/code>:<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/05\/handoff_nsuseractivitytypes.png\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/05\/handoff_nsuseractivitytypes.png\" alt=\"handoff_nsuseractivitytypes\" width=\"549\" height=\"39\" class=\"alignnone size-full wp-image-1464\" \/><\/a><\/p>\n<p>For Handoff support between the Apple Watch and iPhone, will only need to do this in the <code>Info.plist<\/code> 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.  <\/p>\n<p>Before continuing complete this step by opening the <code>Info.plist<\/code> under <code>handoffexample\/Supporting Files<\/code> and create an array named <code>NSUserActivityTypes<\/code> and add one element of type <code>String<\/code> with the value <code>it.iachieved.handoffexample.viewing.map<\/code>.<\/p>\n<h3>Broadcasting the Handoff Activity (or Lack Thereof)<\/h3>\n<p>When you first launch your application on the Apple Watch the initial <code>WKInterfaceController<\/code> <code>awakeWithContext<\/code> method will be called.  At this point I recommend adding a call to <code>invalidateUserActivity()<\/code>.  For some reason in my testing I&#8217;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.  <\/p>\n<p>Add the following line to the <code>willAwakeWithContext<\/code> method in <code>InterfaceController.swift<\/code>, right after the call to <code>super.willAwakeWithContext(context)<\/code> call:<\/p>\n<pre>\r\ninvalidateUserActivity()\r\n<\/pre>\n<p>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 <code>updateUserActivity<\/code> to accomplish this as follows:<\/p>\n<pre>\r\n    updateUserActivity(\"it.iachieved.handoffexample.viewing.map\",\r\n                       userInfo:[\"iata\":self.airport![\"iata\"]!],\r\n                       webpageURL:nil)\r\n<\/pre>\n<p>There are a couple of things to note here:<\/p>\n<ul>\n<li>the <code>updateUserActivity<\/code> is a method of the base <code>WKInterfaceController<\/code> class\n<li>We are not creating an <code>NSUserActivity<\/code> instance (the method used in the <a href=\"https:\/\/developer.apple.com\/library\/ios\/documentation\/UserExperience\/Conceptual\/Handoff\/HandoffFundamentals\/HandoffFundamentals.html\">Handoff Programming Guide<\/a>)\n<li>We supply <code>it.iachieved.handoffexample.viewing.map<\/code> as our activity name\n<li>The <code>userInfo<\/code> dictionary provides information as to <i>which<\/i> airport we are viewing\n<\/ul>\n<p>Let&#8217;s look at these in turn.<\/p>\n<p>First, if you start with the <a href=\"https:\/\/developer.apple.com\/library\/ios\/documentation\/UserExperience\/Conceptual\/Handoff\/HandoffFundamentals\/HandoffFundamentals.html\">Handoff Programming Guide<\/a> from Apple you will not find any mention of the <code>updateUserActivity<\/code> method, but instead are given an introduction to using <code>NSUserActivity<\/code>.  Using <code>NSUserActivity<\/code> routines in the Apple Watch extension don&#8217;t work (or rather, I couldn&#8217;t use them); you need to use <a href=\"https:\/\/developer.apple.com\/library\/ios\/documentation\/WatchKit\/Reference\/WKInterfaceController_class\/#\/\/apple_ref\/occ\/instm\/WKInterfaceController\/updateUserActivity:userInfo:webpageURL:\"><code>updateUserActivity<\/code><\/a>, a method of the <code>WKInterfaceController<\/code> instead.<\/p>\n<p>Second, we supply our activity name <code>it.iachieved.handoffexample.viewing.map<\/code>.  It bears repeating that this must align with the value we declared as a supported activity in the <code>Info.plist<\/code> for the iPhone application.<\/p>\n<p>Finally, our iPhone application needs to know <i>which<\/i> airport we are viewing on the Apple Watch.  We provide that information by supplying a <code>userInfo<\/code> dictionary with a key-value pair of the IATA airport code we are viewing (which is available via the <code>Airports<\/code> dictionary).  Note that what we are providing here is arbitrary; we could have just as easily provided the index location in the <code>Airports<\/code> array if that value was readily available in the <code>WatchMapController<\/code> class.  Since we had an airport dictionary (which contained the <code>iata<\/code> key), we used that instead.<\/p>\n<p>Before continuing let&#8217;s add the call to our <code>WatchMapController<\/code> <code>willActivate<\/code> method as follows:<\/p>\n<pre>\r\n  override func willActivate() {\r\n    \r\n    super.willActivate()\r\n\r\n    updateUserActivity(\"it.iachieved.handoffexample.viewing.map\",\r\n      userInfo:[\"iata\":self.airport![\"iata\"]!],\r\n      webpageURL:nil)\r\n    \r\n  }\r\n<\/pre>\n<p>Once we call <code>updateUserActivity<\/code> the Handoff routines built into Watch OS will broadcast this information (via <a href=\"http:\/\/en.wikipedia.org\/wiki\/Bluetooth_low_energy\">BTLE<\/a>) and the paired iPhone will display a Handoff icon for the <b>Airports!<\/b> app.  Swiping up on the icon and unlocking the phone will launch the iOS <b>Airports!<\/b> 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&#8217;s add the plumbing in the iOS application to be able to take action with the Handoff notification and data.<\/p>\n<h3>Handling Our Handoff Notification<\/h3>\n<p>We&#8217;re interested in implementing one method to add to the <code>AppDelegate<\/code> of our iOS application.  It can be easy to forget that the <code>AppDelegate<\/code> is in large part a collection of <i>application entry<\/i> 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 <code>continueUserActivity<\/code>.  Here is our implementation:<\/p>\n<pre>\r\n  func application(application: UIApplication,\r\n                   continueUserActivity userActivity: NSUserActivity,\r\n                   restorationHandler: ([AnyObject]!) -> Void) -> Bool {\r\n    if let window = self.window, rvc = window.rootViewController {\r\n      rvc.childViewControllers.first?.restoreUserActivityState(userActivity)\r\n    }\r\n    return true\r\n  }\r\n<\/pre>\n<p>Go ahead and add the above to the iPhone <code>AppDelegate<\/code> code.<\/p>\n<p>Let&#8217;s take a look at our implementation of <code>continueUserActivity<\/code>.  First, we want to make sure that we have a non-nil root view controller, because we will be making use of its <code>childViewControllers<\/code> property and the first entry of the array (which is our <code>TableController<\/code>).  Our <code>TableController<\/code> will need to be able to act upon the <code>restoreUserActivityState<\/code> call, so add the following to the <code>TableController<\/code> class:<\/p>\n<pre>\r\n  override func restoreUserActivityState(activity: NSUserActivity) {\r\n    self.navigationController?.popToRootViewControllerAnimated(true)\r\n    let iata = activity.userInfo![\"iata\"] as! String\r\n    for var index = 0; index < Airports.count; index++ {\r\n      if Airports[index][\"iata\"] == iata {\r\n        let indexPath = NSIndexPath(forRow: index, inSection: 0)\r\n        self.tableView.selectRowAtIndexPath(indexPath, animated: true, scrollPosition: .None)\r\n        self.performSegueWithIdentifier(\"SelectAirportSegue\", sender: nil)\r\n        break;\r\n      }\r\n    }\r\n    super.restoreUserActivityState(activity)\r\n  }\r\n<\/pre>\n<p>Recall that the <code>NSUserActivity<\/code> object is <i>just like<\/i> an <code>NSNotification<\/code>; it has a name and is bundled with a dictionary of attributes.  In our Apple Watch routines we called<\/p>\n<pre>\r\nupdateUserActivity(\"it.iachieved.handoffexample.viewing.map\",\r\n                   userInfo:[\"iata\":self.airport![\"iata\"]!],\r\n                   webpageURL:nil)\r\n<\/pre>\n<p>and provided a key-value pair for the IATA code of the airport we were viewing.  Unpack this value in the <code>restoreUserActivityState<\/code> routine of the iOS <code>TableController<\/code> to find the appropriate <code>Airports<\/code> array index.  Once we find the index set the <code>selectedIndex<\/code> property and then invoke the segue for the <code>TableController<\/code>.  That's it!<\/p>\n<p>That's it!  You should be able to successfully test Handoff with your paired Apple Watch and iPhone!<\/p>\n<ul>\n<li>Connect your iPhone to your Mac\n<li>Open the <b>handoffexample<\/b> project in Xcode\n<li>Select the <b>Airports!<\/b> scheme with your iPhone as the target to run on\n<li>Run the application\n<\/ul>\n<p>The <b>Airports!<\/b> 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,<\/p>\n<ul>\n<li>lock your iPhone\n<li>go to the Home screen of your Apple Watch\n<li>launch Airports! (white airplane in a blue circle)\n<li>select an airport from the list\n<\/ul>\n<p>In this example, we've selected Hartsfield-Jackson International in Atlanta, Georgia:<br \/>\n<a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/05\/IMG_4011.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/05\/IMG_4011.jpg\" alt=\"IMG_4011\" width=\"312\" height=\"390\" class=\"alignnone size-full wp-image-1456\" \/><\/a><\/p>\n<p>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:<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/05\/IMG_4010.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/05\/IMG_4010.jpg\" alt=\"IMG_4010\" width=\"375\" height=\"667\" class=\"alignnone size-full wp-image-1454\" \/><\/a><\/p>\n<p>Press the airplane icon and swipe up, and unlock your phone.  iOS should go immediately to the <b>Airports!<\/b> app and display the same map as being displayed on the iPhone!<\/p>\n<p><a href=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/05\/IMG_4012.jpg\"><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/dev.iachieved.it\/iachievedit\/wp-content\/uploads\/2015\/05\/IMG_4012.jpg\" alt=\"IMG_4012\" width=\"375\" height=\"667\" class=\"alignnone size-full wp-image-1458\" \/><\/a><\/p>\n<h3>Final Thoughts<\/h3>\n<p>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 <a href=\"http:\/\/www.raywenderlich.com\/84174\/ios-8-handoff-tutorial\">Raywenderlich.com Handoff tutorial<\/a> for an excellent example of this.<\/p>\n<h3>Get the Code<\/h3>\n<p>Get the code!  Below are links for the starter application, a full Handoff implementation, or the Bitbucket repository for the code.<\/p>\n<ul>\n<li><a href=\"https:\/\/bitbucket.org\/joeiachievedit\/handoffexample\/get\/master.zip\">Starter application<\/a> - download this to go through the tutorial yourself\n<li><a href=\"https:\/\/bitbucket.org\/joeiachievedit\/handoffexample\/get\/handoff.zip\">Implementation of Handoff routines<\/a> - download this if you want a working example of Handoff\n<li><a href=\"https:\/\/bitbucket.org\/joeiachievedit\/handoffexample\">Bitbucket repository<\/a>\n<\/ul>\n<h3>Additional Handoff Tutorials<\/h3>\n<p>There are more Handoff tutorials on the web that you may find useful (I know I did):<\/p>\n<ul>\n<li><a href=\"http:\/\/www.raywenderlich.com\/84174\/ios-8-handoff-tutorial\">Raywenderlich.com iOS8 Handoff Tutorial<\/a>\n<li><a href=\"http:\/\/www.appcoda.com\/handoff\/\">AppCoda Handoff Tutorial<\/a>\n<\/ul>\n<p>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 <a href=\"http:\/\/www.appcoda.com\/handoff-continuation-streams\/\">activity continuation streams<\/a> between devices.<\/p>\n<h3>Follow us on Twitter<\/h3>\n<p>Did you enjoy this tutorial?  Follow us on Twitter at @iachievedit!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Editor&#8217;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: &#8220;It just works.&#8221; Handoff is a new and exciting technology that aims to continue to provide an immersive and seamless &#8220;it just works&#8221; experience when [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[16,5],"tags":[],"class_list":["post-1425","post","type-post","status-publish","format-standard","hentry","category-apple-watch","category-swift"],"_links":{"self":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/1425"}],"collection":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/comments?post=1425"}],"version-history":[{"count":39,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/1425\/revisions"}],"predecessor-version":[{"id":1479,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/posts\/1425\/revisions\/1479"}],"wp:attachment":[{"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/media?parent=1425"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/categories?post=1425"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.iachieved.it\/iachievedit\/wp-json\/wp\/v2\/tags?post=1425"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}