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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Handoff Tutorial: Getting Started
35 mins
- Handoff Overview
- Getting Started
- Device Compatibility: iOS
- User Activities
- Activity Types
- The Starter Project
- Setting Your Team
- Configuring Activity Types
- Quick End-to-End Test
- Creating the View Activity
- Finishing Touches
- Creating the Edit Activity
- Finishing Touches
- Receiving the Activities
- Finishing Touches
- Versioning Support
- Handoff Best Practices
- Where To Go From Here?
Activity Types
When you create a user activity, you must specify an activity type for the activity. An activity type is simply a unique string, usually in reverse DNS notation, like com.razeware.shopsnap.view
.
Each app that is capable of receiving a user activity must declare the activity types that it will accept. This is much like declaring the URL schemes your app supports. For non-document based apps, activity types are listed under the NSUserActivityTypes
key at the top level of Info.plist as shown below:
For an app to support a given activity, there are three requirements:
- Same team. Both apps must originate from the same developer with the same developer Team ID.
-
Same activity type. The receiving app must have a
NSUserActivityTypes
entry for the activity type created by the sending app. - Be signed. Both apps must be distributed through the App Store or be signed with a Developer certificate.
Now that you’ve learned about the basics of user activities and activity types, let’s dive in to an example!
The Starter Project
Start by downloading the starter project for this Handoff tutorial. Once you’ve downloaded it, open up the project in Xcode and run it in an iPhone simulator.
It is called ShopSnap. You can build a simple shopping list in this app. A shopping item is represented by a String and you store the shopping list as an Array of strings. Tapping the + button adds a new item to the list and swiping removes an item.
You’ll define two distinct user activities for this app:
- Viewing the list. If the user is currently viewing the list, you’ll Handoff the entire array of items.
- Adding or editing an item. If the user is currently adding a new item, you’ll Handoff an “edit” activity for a single item instead.
Setting Your Team
For Handoff to work, both the sending and receiving app must be signed by the same team. Since this app is both the sender and the receiver, this is simple!
Select your ShopSnap project, and in the general tab, switch the Team to your team:
Build and run on one of your Handoff-compatible iOS devices to make sure it runs OK, then continue on.
Configuring Activity Types
The next step is to configure the activity types your app supports. Open Supporting Files\Info.plist and click on the + button that appears next to Information Property List to add a new item under the Information Property List dictionary:
Enter NSUserActivityTypes
for the key name and make it an Array
type, as shown below:
Add two items under NSUserActivityTypes
(Item 0 and Item 1) and set their types to String. Enter com.razeware.shopsnap.view for Item 0, and com.razeware.shopsnap.edit for Item 1.
These are arbitrary activity types specific and unique to your app. Since you’ll refer to them from multiple places in the app, it’s good practice to add them as constants in a separate file.
Right-click on the ShopSnap group in the project navigator, select New File \ iOS \ Source \ Swift File. Name the class Constants.swift and ensure your new class is added to the ShopSnap target.
Add the following code to your class:
let ActivityTypeView = "com.razeware.shopsnap.view"
let ActivityTypeEdit = "com.razeware.shopsnap.edit"
let ActivityItemsKey = "shopsnap.items.key"
let ActivityItemKey = "shopsnap.item.key"
Now you can use these constants for the two different activity types. You’ve also defined some constants for the keys you’ll be using in the user activity’s userInfo
dictionary for convenience.
Quick End-to-End Test
Let’s run a quick end-to-end test to make sure that your devices can communicate properly.
Open ListViewController.swift and add the following two functions:
// 1.
func startUserActivity() {
let activity = NSUserActivity(activityType: ActivityTypeView)
activity.title = "Viewing Shopping List"
activity.userInfo = [ActivityItemsKey: ["Ice cream", "Apple", "Nuts"]]
userActivity = activity
userActivity?.becomeCurrent()
}
// 2.
override func updateUserActivityState(activity: NSUserActivity) {
activity.addUserInfoEntriesFromDictionary([ActivityItemsKey: ["Ice cream", "Apple", "Nuts"]])
super.updateUserActivityState(activity)
}
This is a quick test where you hard code a user activity, to make sure that you can receive it OK on the other end.
Here is what the code above does:
-
startUserActivity()
is a helper function that creates an instance ofNSUserActivity
with a hardcoded shopping list. Then it starts broadcasting that activity by callingbecomeCurrent()
. - After you call
becomeCurrent()
, OS will periodically callupdateUserActivityState()
.UIViewController
inherits this method fromUIResponder
, and you should override this to update the state of youruserActivity
here.
Here you update the shopping list with the same hardcoded values as before, since this is just a test. Note thataddUserInfoEntriesFromDictionary
is the preferred way of mutatinguserInfo
dictionary ofNSUserActivity
. You should always callsuper.updateUserActivityState()
at the end.
Now you just need to start this method. Add the following line to the beginning of viewDidLoad()
:
startUserActivity()
That’s the minimum you need to start broadcasting – let’s move on to receiving. Open AppDelegate.swift and add the following code:
func application(application: UIApplication,
continueUserActivity userActivity: NSUserActivity,
restorationHandler: (([AnyObject]!) -> Void))
-> Bool {
let userInfo = userActivity.userInfo as NSDictionary
println("Received a payload via handoff: \(userInfo)")
return true
}
This method on AppDelegate is called when everything goes well and a userActivity
is successfully transferred. Here you log a message with the userInfo
dictionary of the userActivity
. You return true
to indicate you handled the user activity.
Let’s try this out! There’s a little bit of coordination required to get this working on two devices, so follow along carefully.
- Install and run the app on your first device.
- Install and run the app on your second device. Make sure that you are debugging the app in Xcode so you can see your
println()
output.
- Install and run the app on your first device.
- Install and run the app on your second device. Make sure that you are debugging the app in Xcode so you can see your
println()
output.
Note: It is also useful to have the console logs open in this step so you can see any output from iOS, in case there is an issue with the connection. To see the console logs, go to Window\Devices, select your device, and select the icon in the lower left to expand the console area.
Note: It is also useful to have the console logs open in this step so you can see any output from iOS, in case there is an issue with the connection. To see the console logs, go to Window\Devices, select your device, and select the icon in the lower left to expand the console area.
- Put the second device to sleep by pressing the power button. On the same device, press the Home button. If everything works fine, you should see the ShopSnap app icon appear at the left bottom corner of the screen. From there you should be able to launch the app, and see the log message in Xcode’s console:
Received a payload via handoff: {
"shopsnap.items.key" = (
"Ice cream",
Apple,
Nuts
);
}
If you don’t see the app icon on the lock screen, close and re-open the app on the originating device. This forces the OS to restart broadcasting again. Also check the device console to see if there are any error messages from Handoff.