Handoff Tutorial: Getting Started

Learn how to use the new Handoff API introduced in iOS 8 to allow users to continue their activities across different devices. By Soheil Azarpour.

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Finishing Touches

When the user indicates that they want to continue a user activity on another device by swiping up on the app icon, the OS launches the corresponding app. Once the app is launched, the OS calls on application(_, willContinueUserActivityWithType:). Open AppDelegate.swift and add the following method:

func application(application: UIApplication,
                 willContinueUserActivityWithType userActivityType: String)
                 -> Bool {
  return true
}

At this point your app hasn’t yet downloaded the NSUserActivity instance and its userInfo payload. For now, you’ll simply return true. This forces the app to accept the activity each time the user initiates the Handoff process. If you want to alert your user that the activity is on its way, this is the place to do it.

At this point the OS has started transferring data from one device to another. You have already covered the case where everything goes well. But it is conceivable that the Handoff activity will fail at some point.

Add the following method to AppDelegate.swift to handle this case:

func application(application: UIApplication, 
                 didFailToContinueUserActivityWithType userActivityType: String,
                 error: NSError) {
  
  if error.code != NSUserCancelledError {
    let message = "The connection to your other device may have been interrupted. Please try again. \(error.localizedDescription)"
    let alertView = UIAlertView(title: "Handoff Error", message: message, delegate: nil, cancelButtonTitle: "Dismiss")
    alertView.show()
  }
}

If you receive anything except NSUserCancelledError, then something went wrong along the way and you won’t be able to restore the activity. In this case, you display an appropriate message to the user. However, if the user explicitly canceled the Handoff action, then there’s nothing else for you to do here but abort the operation.

Versioning Support

One of the best practices when working with Handoff is versioning. One strategy to deal with this is to add a version number to each handoff that you send, and only accept handoffs from your version number (or potentially earlier). Let’s try this.

Open Constants.swift and add the following constants:

let ActivityVersionKey = "shopsnap.version.key"
let ActivityVersionValue = "1.0"

The above version key and value are arbitrary key-value you picked for this version of the app.

If you recall from the previous section, the OS periodically and automatically calls restoreUserActivityState(activity:). The implementations of this method were very focused and were limited to the scope of the object that implemented it. For example, ListViewController overrode this method to update userActivity with list of all items, whereas DetailViewController overrode to update with the current item that was being edited.

When it comes to something that is generic to your userActivity and applies to all of your user activities regardless, like versioning, the best place to do that is in the AppDelegate.

Whenever restoreUserActivityState(activity:) is called, the OS calls application(application:, didUpdateUserActivity userActivity:) in the app delegate right after that. You’ll use this method to add versioning support to your Handoff.

Open AppDelegate.swift and add the following:

func application(application: UIApplication, 
                 didUpdateUserActivity userActivity: NSUserActivity) {
  userActivity.addUserInfoEntriesFromDictionary([ActivityVersionKey: ActivityVersionValue])
}

Here you simply update the userInfo dictionary with the version of your app.

Still in AppDelegate.swift, update the implementation of application(_:, continueUserActivity: restorationHandler:) as follows:

func application(application: UIApplication,
                 continueUserActivity userActivity: NSUserActivity,
                 restorationHandler: (([AnyObject]!) -> Void))
                 -> Bool {
  
  if let userInfo: NSDictionary = userActivity.userInfo {
    if let version = userInfo[ActivityVersionKey] as? String {
      // Pass it on.
      if let window = self.window {
        window.rootViewController?.restoreUserActivityState(userActivity)
      }
      return true
    }
  }
  return false
}

Here you do a sanity check on the version of the userActivity and pass it on only if it matches the version you know about. Build and run your app once again to ensure the app runs as usual.

Handoff Best Practices

Before you go, I thought I’d leave you with a few thoughts on Handoff best practices.

  • NSURL: Using NSURL in an NSUserActivity userInfo dictionary can be tricky. The only NSURLs you can pass safely in Handoff are web site URLs that use HTTP or HTTPS and iCloud documents. You can’t pass local file URLs as the receiver won’t translate and map the URL properly at the receiver’s end. The best way to achieve file links is to pass a relative path and re-construct your URL on the receiving side.
  • Platform specific values: Avoid using platform specific values like the content offset of a scroll view; it’s always better to use relative landmarks. For example, if your user is viewing some items in a table view, pass the index path of the top most visible item in the table view in your user activity object instead of passing the content offset or visible rect of the table view.
  • Versioning: Think about using versioning and future-proofing updates of your app. You could add new data formats or remove values entirely from your userInfo dictionary in future versions of the app. Versioning gives you more control over how user activities are actioned in current and future versions of your app.

Where To Go From Here?

Here is the final example project that you developed in the above tutorial.

If you’re curious to learn more about Handoff, streaming, and document based Handoff, be sure to check out Apple’s Handoff Programming Guide for more information now that you know the basics.

If you enjoyed this tutorial, check out our book iOS 8 by Tutorials, which is chock-full of tutorials like this.

If you have any questions or comments about this tutorial, please join the forum discussion below!