How to: Handoff to a Xamarin iPhone app from Apple Watch

# How to: Handoff to a Xamarin iPhone app from Apple Watch

There are two ways to activate the parent (aka container) app from an Apple Watch app. You can either directly activate the container app using WKInterfaceController.OpenParentApplication or you can use Handoff.

Using Handoff is a little more complex, so I thought I’d write a quick little how-to. There are a few different Handoff scenarios, but perhaps the most common for the  Watch is: “On my watch I want to begin a task that I complete later on my iPhone.” So, for instance, some task that requires either more data-entry than is appropriate for the watch or some capabilities not available on the watch.

I want to keep the focus on the APIs, so instead of a real-world sample, I’m going to create a minimal example: a button on the Watch activates handoff and a status label on the phone app is updated when the user activity is continued on the phone.

As always, we have a single Solution with 3 projects: the parent App, the Watch extension, and the Watch App.

Napkin 10 05-06-15, 4.21.12 PM

Every handoff activity has a unique identifier. By convention, this is a domain-reversed string such as: com.xamarin.HandOffDemo.verb.

To trigger the Handoff behavior, the watch extension calls the WKInterfaceController.UpdateUserActivity method, with its first argument set equal to this identifier:

[code lang="csharp"]
partial void ActivateHandoffClicked (WatchKit.WKInterfaceButton sender)
{
var userInfo = NSDictionary.FromObjectAndKey(new NSString(“value”), new NSString(“key”));
this.UpdateUserActivity(“com.xamarin.HandOffDemo.verb”, userInfo, null);
}
[/code]

The third argument is a NSUrl object that can be used for Handoff tasks that should be handled by Safari. But in our case, we’re handing the userInfo dictionary containing the very complex data associated with our handoff.

Your parent app registers its interest in this type of handoff within its info.plist. In the parent app info.plist, add a new array called NSUserActivityTypes and add to it a string with value com.xamarin.HandOffDemo.verb

Napkin 11 05-06-15, 4.29.20 PM

It’s possible that an app could be interested in certain handoffs, but not always be in a position to actually handle them. That logic can be placed in an override of the UIApplicationDelegate.WillContinueUserActivity method:

[code lang="csharp"]
public override bool WillContinueUserActivity (UIApplication application, string userActivityType)
{
//Yeah, we can handle it
return true;
}
[/code]

Assuming that we return true from that method, the next step is to override the UIApplicationDelegate.ContinueUserActivity method:

An architectural issue that needs to be addressed is that this Handoff re-entry point is in the UIApplicationDelegate object, which of course does not have an associated user interface. There are several ways to handle this, but as a fan of reactive programming, I think the proper design is to create either an IObservable sequence or a more traditional C# event, which is what I do here:

[code lang="csharp"]
public event EventHandler HandoffOccurred = delegate {};

public override bool ContinueUserActivity (UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
HandoffOccurred?.Invoke (this, userActivity.UserInfo);

return true;
}
[/code]

The third parameter, completionHandler used if you have references to custom UIResponder objects that should handle the user activity. In the case, you put those references in a NSArray and pass them to completionHandler, which will cause each of their ContinueUserActivity methods to be called. (Update: Apple would probably prefer this technique to my event, but I am not sure if it's necessary, and it requires the UIApplicationDelegate to maintain a reference to the subscribing UIResponder, so you either have an ugly dependency or you have to implement some kind of Observer / Subscriber pattern. So I still would suggest an event or IObservable as the better solution.)

In the parent app’s main UIViewController class, I have:

[code lang="csharp"]
public override void ViewDidLoad ()
{
base.ViewDidLoad ();

//Configure user experience for Handoff
var myAppDel = (AppDelegate) UIApplication.SharedApplication.Delegate;
myAppDel.HandoffOccurred += HandoffOccurred;
}

public void HandoffOccurred(object sender, NSDictionary userInfo)
{
InvokeOnMainThread( () => statusLabel.Text = userInfo[“key”].ToString() );
}
[/code]

And that’s all there is to it. Obviously, a real use-case would involve building a more complex NSDictionary holding the context of the watch interaction and a similarly complex handler in the parent app.

Now, when the Handoff is activated from the Apple Watch, my iPhone lock screen shows the icon of the parent app in the lower-left corner. If I drag that up, the parent app opens, the re-entry process begins, with UIApplicationDelegate.WillContinueUserActivity and UIApplicationDelegate.ContinueUserActivity.

https://www.youtube.com/watch?v=XKt-7EVfEDs