Showing posts with label c#. Show all posts
Showing posts with label c#. Show all posts

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!

Saturday, 9 May 2009

Silverlight DataContractJsonSerializer Error

Admittedly there is very little excuse for hand-crafting Json output these days, with libraries like Json.NET available, and WCF supporting Json natively. However, I was tweaking an existing ASPX page (that was actually rendering Xaml for a Silverlight 1.0 application) to turn it into a 'service' for Silverlight 2.0... and it seemed like the fastest way to do that was simply replace a whole pile of <s and >s with {}

I got some hints/reminders on how to Consume a JSON object in Silverlight, and found Jsonlint really helpful in tuning the output until it was valid Json.


Then I created a 'matching' object model in Silverlight C#


and wired up the OpenReadCompleted event using DataContractJsonSerializer


Everything LOOKED like it should work, so at first I was confused by this error message:
Unable to cast object of type 'System.Collections.Generic.List`1[System.Object]' to type 'System.Collections.Generic.Dictionary`2[System.String,System.Object]'

After staring blankly at the code for a while, I finally realised where my Json had gone wrong -- at the very root of my Json response (see Jsonlint image above) I was "accidentally" wrapping the entire Json output with an unnecessary [] pair. I guess this meant the Deserializer was expecting to cast into a collection (albeit with a single element), but I was intending the root Json element to be a single object (to match my C# JsonCourse).

The simple fix was removing the enclosing [] so that my Json started off like this instead:
{
"width": 800 ,
"height": 600 ,
"runners": [
{
"name": "mapcanberramarathon",
"points": [
Lesson for tonight is to better understand the underlying Json representation before consuming it (or else use a library rather than hand-craft the output). Anyway now it is fixed RaceReplay.net 2.0 is that much closer to fruition...

Thursday, 16 April 2009

Re-blog: C# 4.0 feature focus (recommended)

Of all the blogs I follow, the one I most often 'bookmark and re-read' is probably Bart De Smet's (ok, Scottgu too).

I think I first started following Bart in 2006 when I was learning about LINQ - A custom implementation of the .NET Standard Query Operators.

However it was this recent post on C# 4.0: Co- and Contra-Variance for Generic Delegate and Interface Types that prompted this 'recommendation'. It's a perfect example of what I like about many of his posts - I learned a lot! Some of the '3.0 examples' he mentions are just the sort of thing I've noticed, wondered about and worked around before: but Bart provides the "language" and "reasoning" that explains why... sure I've probably heard the terms co- and contra-variance before, and I can understand at some 'high level' why
IEnumerable<Person> people = from Student s in db.Students
where s.Age <= 25
select s;

doesn't (currently) work, but Bart's discussion and examples feel just like the 'developer watercooler' type conversation that might take place amongst developers at work - and suddenly I understand the concepts a lot better.

I enjoy reading (and re-reading, just to fully understand them!) Bart's posts, and highly recommend them.

Friday, 27 February 2009

System.Shell.CommandLine (storm in a teacup)

Miguel's post caught my eye because it seemed to contain thoughtful analysis of a .NET 4.0 'feature' that had appeared in the latest beta (Miguel later added the pre-script to his post - which sorta spoils the end of the story). I like Miguel's perspective on the framework (particularly the core) given his work on Mono and Moonlight.

I was a big fan of Mono.GetOptions when I first saw it, having included in in Searcharoo.net back in 2007. Now I discover that Miguel is
not a fan of using attributes for command line options. Attributes are just a poor language for expressing complicated semantics, they can only do well for setting simple flags, but once that is over, they become just as bad as the System.Shell.CommandLine interface...We tried that before, that was called "Mono.GetOptions" and we obsoleted it a few years ago, it was a cute idea, but did not map well for real world use.
I guess I better check out Mono.Options. Here's my GetOptions implementation for posterity (I thought the attributes were kinda cool, and more readable/maintainable than any other alternative at the time...)


Elsewhere on my reading list, Bart simultaneously posted an alternative opinion - some detailed examples of the .NET 4.0 code to help people understand it (not sure if he was aware it might not make RTM).

I enjoyed both perspectives, although tended to agree with Miguel's position - that it belonged on CodePlex rather than in the Framework. Was interesting to see his later update indicating that is exactly what would actually happen.

Bit OT for a blog post... but interesting to see the next release growing & changing

Saturday, 27 December 2008

MIX 10k Challenge

I just submitted my MIX 10k Challenge entry - so I can stop worrying about tweaking and move on to the next little project. I'll post a link if/when it's approved, but for now here is the single line of c#

and the single line of XAML

and the 'proof' that it's only 10k


I had planned to write a whole post about 'how to fit more code into 10k', but Bill's Thoughts on the MIX 10k Challenge and Adam's What can you make with 10k of Silverlight or WPF? have covered most of the tricks already... I'll just mention a handful of comments/additions below:

Bill's second suggestions is to use short class/method/property/field/variable names - single characters if possible. In the interests of 'maintainability' I did not quite go that far (using two- and three-letter acronyms) but in general the code looks like it's been obfuscated (as you can see on the class diagram, which also shows I 'missed' a couple of opportunities for refactoring)

I'd originally used human-readable names (the code was well over 20k originally, with comments) and used the Visual Studio Refactor->Rename feature to arrive at the 'compressed' version.

Bill also said "Make sure to use using statements to avoid having to specify the namespace". Good idea - but using can be even more helpful if you alias classes that you plan to use a lot. For example,
using v=System.Windows.Media.Animation.Storyboard;
requires 50 chars for the using declaration, but nine instances of 10 chars became 1 char (Storyboard --> v) which is 81 chars less, an improvement of 31 chars :) You only need to use PropertyChangedEventHandler three times before it's more efficient to
using p=System.ComponentModel.PropertyChangedEventHandler;

Another one I'd expand on is "Reduce whitespace. C# can be written as a single line for the entire file". It's not just line-breaks that you can save - C# in particular can be much shorter if you remove unnecessary spaces (that the IDE inserts) around parens, braces, commas and semi-colons, and omit braces where not required (single-statement-if s). Not sure how space-efficient you can be with Visual Basic...
Another area where the IDE will frustrate you is by offering to put each of your classes, code-behinds, etc into a individual files. DON'T LET IT! there's nothing special about a code-behind file, so put ALL your code into one .cs (I put the entire codebase into Page.xaml.cs). This saves you mulitple using clauses and namespace declarations.

It might seem obvious, but using var instead of the Type name will always be shorter (how many Types other than int are three chars or less?).

Also obvious: declare variables of the same Type together ('globally' if required) and initialize them in the declaration if they need initializing. eg int i,j,k=256; rather than int i;int j; int k; k = 256;
Re-use variables across methods. Don't use a property where a field will do.

Use defaults to your advantage - for example don't specify public protected private if you don't need to. Think about the defaults that apply in Xaml, Databinding, etc.

Unfortunately I don't have any hints on making 'small' applications "look good". I definitely did not inherit the Designer-gene ... for that I'd check out Jose's blog.

Good luck with your MIX 10k entry!

Monday, 2 April 2007

Searcharoo.net gets a facelift...

The Searcharoo.net website has a new skin... much less 'fluro' than the old one (below :)



and a new, improved RecipeNow.net is coming soon...

Saturday, 17 March 2007

Searcharoo C# search engine indexes Word, PDF and more...

The latest version of Searcharoo (4) is now available for download (and read about, of course) --
C# search engine: refactored to search Word, PDF and more.

The coolest feature - searching Microsoft Office docs (Word, Excel, PowerPoint) and Acrobat/PDF files - is based on someone else's article Using IFilter in C#.

Also put a bit of work into refactoring the code structure, layout, naming, etc. At least now it might pass a "code review" (if anyone cared to conduct one:)

The project now also has it's own website www.searcharoo.net - the latest article isn't quite there yet (requires some formatting) but over time I'll post more frequently to that site; it takes forever to write, format and upload decent, CodeProject-quality work...