Tuesday, 20 January 2015

Apple Watch Kit programming with C# (and Xamarin)

For my "Your First Xamarin.Forms App" talk at Evolve this year I built a very simple version of the classic "Magic Eight Ball" fortune-telling app. Seemed like a perfect example to adapt for my First Apple Watch App. It's built in C# using Xamarin (of course); Xamarin's Watch Kit Preview came out today!

Here is the finished app: a simple display that answers any question you ask ;-) Force Touch on the screen to pop-up the menu to ask another question (Shake), or simply say "Thanks". Grab the code from Github.
^ watch frame screenshots generated with Bezel thanks to the fine folks at infinitapps.

Apple Watch Projects

The solution structure for watch apps consists of three projects (notice that watch apps are split in two parts):
  • an iPhone app which delivers the watch app to the user (and is also obviously a regular iPhone app),
  • an Watch Kit Extension where the code for the watch app runs (on the iPhone, but separate to the iPhone app),
  • a Watch app which is only the storyboard and image resources that go to the watch itself.

Storyboard User Interface

After creating the three projects in a new solution, the first step is to draw the user interface in Interface Builder. Right-click on the Interface.storyboard (already in the template) and open in Xcode.

I did the following:
  • gave the interface controller a Title: 8Ball
  • dragged a Label and centered it
  • Ctrl + dragged an outlet from the label into the header file called result
  • dragged a Menu onto the scene (this is triggered by Force Touch)
  • added and named the two MenuItems: Shake & Back
  • Ctrl + dragged an action from the first menu item called shake
Remember: the storyboard (and any image resources) are the the only things that get installed on the watch itself.

WatchKit C# Code

With the user interface defined in the storyboard, I just needed to wire-up the outlet and action I created in C# to make the app work. The C# code will run in the Watch Kit Extension - on the iPhone itself, not on the CPU of the watch.

First I copied the Magic Eight Ball responses from my Xamarin.Forms app (it's a simple string array, called options) and then I started to implement methods in the InterfaceController.

The Awake method is called when the scene is created, so this is where the code selects its first random response to show the user. I store it in a local variable lastResult and also in NSUserDefaults (for the Glance, explained later).

public override void Awake (NSObject context)
{
 base.Awake (context);
 var rnd = new System.Random();
 lastResult = options[rnd.Next(0, options.Length - 1)]; 
 NSUserDefaults.StandardUserDefaults.SetString 
  (lastResult, "lastResult");
}

Then in WillActivate I set the label's text to the random value:

public override void WillActivate ()
{
 result.SetText (lastResult);
}
Finally, the menu's Shake button should choose a new random response, so the action is implemented to generate new new answer, set the local lastResult variable, the NSUserDefault, and also the label's text.

partial void shake () {
 var rnd = new System.Random();
 lastResult = options[rnd.Next(0, options.Length - 1)]; 
 result.SetText (lastResult);
 NSUserDefaults.StandardUserDefaults.SetString 
  (lastResult, "lastResult");
}

That's all the code required to get the app running! I did a couple more things, however, to demonstrate how to programmatically modify the Menu...

In the Awake method I add another menu item called Thanks:

AddMenuItem (WKMenuItemIcon.Accept, "Thanks", new ObjCRuntime.Selector ("tapped"));
The Selector is implemented as shown - notice the [Export] is required:
[Export("tapped")]
void MenuItemTapped () {
 result.SetText ("You're welcome!");
}

Glance Mode

I also implemented Glance mode, which simply displays the last response generated by the app.

The watch app template already includes a GlanceInterfaceController so I just had to drag a couple of Labels onto it, and Ctrl + drag an outlet for one, so I could change the text programmatically.



Because I always store a generated result in NSUserDefaults the GlanceInterfaceController, the code for the glance is really simple:
var r = NSUserDefaults.StandardUserDefaults.StringForKey("lastResult");
lastResult.SetText (r);


Currently you cannot test Glances from within Xamarin Studio, but it's easy to trigger it using Terminal and the following command (make sure you update path to match your app):

/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/bin/mtouch --sdkroot=/Applications/Xcode-Beta.app/Contents/Developer/ --device=:v2:runtime=com.apple.CoreSimulator.SimRuntime.iOS-8-2,devicetype=com.apple.CoreSimulator.SimDeviceType.iPhone-6 --launchsimwatch=/Users/craigdunn/Projects/Watch8Ball/Watch8Ball/bin/iPhoneSimulator/Debug/Watch8Ball.app --watchlaunchmode=Glance

The finished glance display looks like this:

And that's it! Around 10 lines of C# code to build an Apple Watch app.

Download the Xamarin Watch Kit Preview, get the code from Github and try it for yourself!

No comments:

Post a Comment

Note: only a member of this blog may post a comment.