Category Archives: Dev Stuff

The easy way to push packages into HockeyApp from CI

As I mentioned in my last post, we use HockeyApp to distribute one of our internal mobile applications. As a platform, it provides many wonderful features including distribution, crash reports, feedback forms, analytics and team management. It’s also been acquired by Microsoft recently, which I’m sure will mean a lot of interesting things coming in the Windows 10 timeframe…

Our build and release pipeline is completely automated – just like they all should be! We have a very simple branching strategy (as we only ever have a couple of people working on it). We use pull requests to manage feature branches and anything that hits the master branch goes live. HockeyApp has a REST API that allows you to upload new versions quite easily.

We went through a couple of iterations before this process became easy though. At first we tried pushing packages using PowerShell. This didn’t work too well… unfortunately the API uses multiple form fields to accept the parameters and PowerShell’s built-in web commands aren’t the easiest to accomplish this with. So we wrote a C# console app to do it. This worked, but was a little nasty because we actually needed to compile and include it as an artefact for a later build to use.

We revisited this later and thought that if we are going to push all the packages into artefacts then we could just use curl from a Mac (or Linux) build agent. This made it really easy! We could now use three simple shell commands to push the packages to hockey app –

1. iOS


curl \

-F "status=2" \

-F "notify=1" \

-F "ipa=@dist/MyApp-%CiBuildNumber%.ipa" \

-H "X-HockeyAppToken: %HockeyAppToken%" \

https://rink.hockeyapp.net/api/2/apps/%iOSAppId%/app_versions/upload

2. Android


curl \

-F "status=2" \

-F "notify=1" \

-F "ipa=@dist/com.mycompany.myapp-signed.apk" \

-H "X-HockeyAppToken: %HockeyAppToken%" \

https://rink.hockeyapp.net/api/2/apps/%AndroidAppId%/app_versions/upload

3. Windows Phone


curl \

-F "status=2" \

-F "notify=1" \

-F "ipa=@dist/MyApp.WP_Release_ARM.xap" \

-H "X-HockeyAppToken: %HockeyAppToken%" \

https://rink.hockeyapp.net/api/2/apps/%WPAppId%/app_versions/upload

They’re all the same right…? Yep – that’s the point. The only thing you need to remember is that the application IDs need to be the right one for your platform and you need to ensure you have the right package names.

The commands above have been parameterised so that you can store the application IDs and HockeyApp token as TeamCity parameters. This would be really handy if you wanted to say have multiple release channels (alpha/beta/RTW) by using a build template and setting the parameters on each configuration.

You may also notice the CiBuildNumber parameter in the iOS command. As this is a chained build we can pick up the Continuous Integration build’s number (which we’ve set up to be a version number – e.g. 1.3.7.0) and add it as a configuration parameter so that we can easily reference it in scripts.

And that’s it!

Advertisements

What to consider when doing Continuous Integration on a Xamarin app

OK, let me set the scene… It was a dark starry night at the local watering hole (READ: pub) and I was discussing one of our internal systems. We quickly identified that:

  • none of us liked using the website on our phone
  • we had an almost equal split between iOS, Android and Windows Phone users
  • and several people wanted to learn Xamarin

So we did the only logical thing… spun up a very small team to build an app. While I could regale you with stories of building APIs and authenticating with Azure AD – I’ll leave that for another day and describe one of our larger challenges: building and packaging the app.

Aaron Powell has actually already written a blog post about how we handled versioning. This post will describe some of the challenges and considerations for process of building the app from source control and packaging it.

Considerations and Challenges

0 – A Build System

First off, let’s address the elephant in the room… We used TeamCity.

In the beginning, we weren’t really sure how much to structure our builds, whether we’d target Mac or Windows, etc. So we chose the most flexible system that we knew. As a consultant, I spend a lot of time answering questions with “it depends…” and that definitely is the answer when asked what build system to use.

So to those who ask why TeamCity over TFS/Jenkins/Bamboo/etc. My simply answer is –

  • it can target OS X and Windows
  • it’s flexible enough to incorporate more steps if needed and split out load over multiple chained configurations if needed; and
  • I’ve used it a LOT so I’m comfortable with it.

1 – Target OS for Build Agents

Those who have used Xamarin will admit that it’s an easier setup when you can do everything on a Mac. However, we didn’t quite have that luxury. Our deciding factor here was simple – we need to compile a Windows Phone app. While we *could* (and actually did…) build for iOS on Mac and WP on Windows (either for Android), this would lead to several chained build configurations and as anyone in a couple-week skunkworks project would tell you –

So instead we chose to run our builds on a Windows VM that would utilise the Xamarin Build Host on its Mac host to build the iOS app.

2 – Distributing your app

Choosing decision is based on what channels you’ll use to distribute your app. In our case, we:

  • Needed to push it out to all employees
  • Didn’t want it on any stores
  • Didn’t want to have to manage users’ device IDs

So we chose Enterprise provisioning for iOS and Windows Phone and a generated keystore for Android. This means we can use something like HockeyApp for distribution. This method of provisioning would also work well for distributing apps to testers before they are published to a store.

If you’re pushing your apps straight into a store then you’ll need to follow the guidelines for each platform’s provisioning.

iOS Provisioning

Using an Enterprise Provisioning Profile means we can freely distribute our app through the organisation without the need for maintaining UUIDs (as with Ad-Hoc). When packaging your iOS app you can either –

  1. Sign into XCode as a user with access to the provisioning profile
  2. Install the certificate and provisioning profile manually

Check out Xamarin’s docs for a step-by-step guide on doing this.

Android Provisioning

If you don’t want to go via Google Play or Amazon Stores to get a signing key, you can generate your own keystore using –

keytool -genkey -v -keystore release.keystore

Store this in a safe place.

Windows Phone Provisioning

If you’re building a marketplace app, you can get your certificate by associating the project with the store app. This makes things very easy…

Using an Enterprise distribution profile meant we needed to distribute an AETX to our users in order to trust applications signed for our enterprise. We then need to sign the XAP (or APPX) file that is produced as a post build step. The trick here is to ensure that the manifests have the right author and publisher details in them.

XAP projects have a WMAppManifest.xml file in the Properties folder. In this file the App element should have the Author and Publisher attributes set to the CN of your certificate.

APPX projects have a Package.appxmanifest in their root. The Identity element’s Publisher attribute needs to match the certificate’s distinguished name and the Properties/PublisherDisplayName element should have the certificate’s CN as its value.

3 – Solution Build Configurations

They’re important. Follow the guides on the Xamarin docs for preparing iOS and Android for distribution carefully. They’re full of golden nuggets and are sure to get you through some tough times.

4 – Restoring Xamarin Components

Xamarin Components are fantastic! They’re just like commercialised NuGet packages.

However, getting them to work just like NuGet packages is a little bit tricky. There’s a command line tool available on https://components.xamarin.com/submit (direct download) that is used to package or restore components in a solution. To use this tool you need to authenticate with it. When you do, it drops a cookie called .xamarin-credentials in your home directory (~). It then uses that cookie for subsequent operations.

There are a couple of ways around this:

  1. Store your cookie in source control and –
    1. Copy the cookie to the home directory before restoring components
    2. Add an environment variable pointing the COOKIE_JAR_PATH to your build process
  2. Run the command manually on the machine

We chose the source control + environment variable option because it seemed a little neater than littering my home directory. Looking back, there’s so much manual set up in any agent that I’m going to suggest running it manually on the agent.

Hindsight

We actually had a couple of iterations of build configuration. We started with a highly parallelised set of builds chained up the wazoo… and simplified it down to 2. The first compiled and packaged the applications and published them as artefacts on all branches. The second, which only ran for changesets on the master branch, grabbed the artefacts from the previous build and published them to HockeyApp.

Windows Phone signing also proved to be very difficult with the Enterprise profile. We originally used this because we didn’t want to be spinning up shadow accounts for each platform to get certificates, but signing and distributing a XAP/APPX manually is quite convoluted and tricky.

We learnt a lot from this project about Xamarin. By the end we had spent more time on infrastructure pieces (i.e. exposing APIs, build configurations, integrating third party components, etc) than actually putting the core functionality in. It was great fun and I wouldn’t change it for the world!

A Raspberry Pi Build Light for TeamCity in Mono

Some Background…

After I bought my Raspberry Pi I went through a little period of uncertainty that most other owners have, asking myself “how do I make this thing useful?” It didn’t take long before one of my colleagues created a GitHub repository for a little project that had been sitting around for a while – TeamFlash.

TeamFlash is a build light client app for TeamCity. It monitors the build server to find the status of the latest build and flashes a light in a different colour depending on that status. Having a little bit of electronics know-how, I wanted to see if I could make my Raspberry Pi into a standalone build light.

First up, I knew there had to be someone that’s interfaced with the GPIO ports. Within a few seconds of deciding to search I stumbled upon the RPi Low-level peripherals page. This has a sample of driving the GPIO using C# linking to the RaspberryPi.Net GitHub repository. It has one moving part – Mike McCauley’s BCM2835 library. Brilliant! Now to get it working…

Getting Started

The RapsberryPi.Net README has instructions on how to compile the BCM2835 library into a statically linked library named libbcm2835.so. To use GPIO from Mono I simply:

  1. Compiled the BCM2835 library
  2. Compiled the RaspberryPi.Net project
  3. Took the output of both and added them to my Dependencies folder
  4. Added RaspberryPi.Net.dll to my project as a reference
  5. Added libbcm2835.so to my project as a linked file and changed its build action to Copy if newer

Now I can write code like:

var led = new GPIOMem(GPIO.GPIOPins.GPIO01);
led.Write(true);
Thread.Sleep(1000);
led.Write(false);

Running TeamFlash on YOUR Pi

Here’s how you can get TeamFlash working on your Raspberry Pi.

Step 1 – Wiring it up

I’ve got some standard LEDs from an old Arduino kit, so I’ve hooked each of those in serial with a 330 Ohm resistor to ensure the forward current is within the correct range.

Raspberry Pi TeamFlash Fritzing VisualRaspberry Pi TeamFlash Fritzing Schematic

Step 2 – Boot Raspbian

Go to http://www.raspberrypi.org/downloads and get the latest release of Raspbian. Follow the instructions and get it up and running.

Step 3 – Install the Required Components

To get it all going you’ll need to install mono. If you want to build it on the Pi, install git and mono-complete:

sudo apt-get install git
sudo apt-get install mono-complete

Otherwise the mono-runtime will suffice:

sudo apt-get install mono-runtime

Step 4 – Get TeamFlash

Again, if you want to compile the app on the Pi:

git clone https://github.com/ducas/TeamFlash.git
cd TeamFlash
xbuild

Otherwise you can clone the repository and compile it on your desktop/laptop and just move the output (contents of TeamFlash/bin/Debug) around on a USB key.

Step 6 – Configure TeamFlash

Edit the serverUrl, username and password values in TeamFlash.exe.config (in the output directory of the build – TeamFlash/bin/Debug). If you’re looking for an easy way to do this on the Pi, try using nano:

nano TeamFlash.exe.config

When using nano, simply change the appropriate values and hit Ctrl + X to exit – you will be prompted to save.

Step 7 – Run TeamFlash

sudo mono TeamFlash.exe

Step 8 – Profit!

And that’s it! When your build is:

  1. Good – the green light will stay lit
  2. Broken – the red light will flash on and off
  3. Investigating – the red and yellow lights will flash sequentially

Unit Testing and the .NET Micro Framework

Recently I decided to take up a new hobby… writing code for small things. So, being a .NET developer, I decided the easiest way to crack this nut is to use .NET Micro Framework (or NETMF).

Getting started with NETMF is fairly easy for a .NET developer. You go to www.netmf.com, download and install the SDK and you create new projects using the new templates.

One thing that’s missing from NETMF is the ability to unit test your projects. Because the core is not the same as the desktop core there are no unit testing tools. Searching a bit I found a few people using some patchy solutions involving referencing code files in regular unit testing projects. While this may work for some, I don’t believe it’s the most accurate test as the framework implementations are rather different. This drew me to creating my own solution…

Introducing MFUnit!

MFUnit is a simple .NET Micro Framework Unit Testing Library. It uses convention based discovery to find and run your tests.

Open Source

MFUnit is open source – https://github.com/ducas/MFUnit. Check it out! You’ll find a readme page with more detailed information and source code, including a test project that uses MFUnit to test the Assert class.

Installation

1. Create a NETMF Console Project

2. …

Install-Package MFUnit

3. Profit!

Installing this package will create a class named TestProgram. Simply delete Program.cs, write your tests and you’re on your way.

Conventions

  1. Test fixtures/classes must end with Tests
  2. Test methods (or facts) must be public void methods

Asserting

MFUnit comes with an Assert library that supports the following methods:

  • Fail
  • AreEqual
  • IsTrue
  • IsFalse
  • IsNull
  • IsNotNull
  • Throws

Example

The following class will be discovered by the test runner and the methods will be executed.

public class AssertTests
{
    public void AssertIsNull_ShouldPass_WhenActualIsNull()
    {
        Assert.IsNull(null);
    }

    public void AssertIsNull_ShouldFail_WhenActualIsNotNull()
    {
        Assert.IsNull(1);
    }
}

The first method will pass, but the second will fail. The Output Debug window will contain the following text:

PASS AssertTests.AssertIsNull_ShouldFail_WhenActualIsNull
    #### Exception MFUnit.AssertException - 0x00000000 (3) ####
    #### Message: Expected: "null", Actual: "not null".
    #### MFUnit.Assert::Fail [IP: 0005] ####
    #### MFUnit.Assert::IsNull [IP: 000f] ####
    #### MFUnit.Tests.AssertTests::AssertIsNull_ShouldPass_WhenActualIsNull [IP: 0008] ####
    #### System.Reflection.MethodBase::Invoke [IP: 0000] ####
    #### MFUnit.TestRun::Execute [IP: 00b6] ####
A first chance exception of type 'MFUnit.AssertException' occurred in MFUnit.dll
FAIL AssertTests.AssertIsNull_ShouldPass_WhenActualIsNotNull: Expected: "null", Actual: "not null".

GUI

Currently the only GUI is a simple Pass/Fail count.

MFUnit GUI

Depending on whether this picks up and how many people request it, I may put some effort into listing the appropriate tests and making the information navigable.

Also, once VS11 is a bit closer to release, I may add a plugin for the test runner.

Enjoy!

Hopefully this is useful to others out there. If you have any suggestions feel free to leave comments. If you want to contribute I’ll happily accept GOOD pull requests.

Using Web API Validation with jQuery Validate

Building on my last post about validating your model with Web API, if you’re calling a Web API controller from JavaScript you may need to parse the validation result and display it on the screen.

Most people using MVC would be using the jQuery Validate plugin that’s been included with the default template for quite a while now. While most validations are performed using JavaScript adapters, some are only performed server side. As a result, the regular unobtrusive JavaScript adapters will not catch this before the post occurs. This means that you if you are using JavaScript requests with Web API to handle data manipulation you will need to somehow manually handle the validation errors that will be returned.

Plugging into jQuery Validation is actually quite easy… To validate a form, simply select the form using jQuery and call .validate() on it – e.g.

var validator = $('.main-content form').validate();

This will return a validator object will a few handy methods on it. Two of which are valid() and showErrors(). The valid method will return a Boolean value indicating whether the form is valid or not and the showErrors method will show any validation errors on the current form. The showErrors method also accepts an object that defines any additional error messages you wish to display – e.g. to display the message “The title is incorrect” for a property named Title:

validator.showErrors({ Title: 'The title is incorrect.' });

Now, assuming I a view with the following mark-up inside the form, I should see a validation error:

<div class="editor-label">@Html.LabelFor(model => model.Title)</div>
<div class="editor-field">
    @Html.TextBoxFor(model => model.Title)
    @Html.ValidationMessageFor(model => model.Title)
</div>


But how do we connect this to Web API…? Well, if you’ve read my previous post you’ll recall that calling a Web API controller’s PUT action that’s decorated with the ValidateFilter attribute I created will return a collection of validation errors if the model is not valid. To test this, I’ll modify my TodoApiController from the previous post as follows:

[ValidateFilter]
public void Put(int id, TodoItem value)
{
    if (value.Title == "hi there")
        ModelState.AddModelError("Title", "The title is incorrect.");

    if (!ModelState.IsValid) return;

    db.Entry(value).State = EntityState.Modified;
    db.SaveChanges();
}

I should now receive a validation error whenever I try to update an item with the title “hi there”. Let’s write some jQuery to submit my form:

function updateItem(form, url) {
    var validator = form.validate(),
        serialized = form.serializeArray()
        data = { };

    if (!validator.valid()) { return; }

    // turn the array of form properties into a regular JavaScript object
    for (var i = 0; i < serialized.length; i++) {
        data[serialized[i].name] = serialized[i].value;
    }

    $.ajax({
        type: 'PUT', // Update Action
        url: url, // API Url e.g. http://localhost:9999/api/TodoApi/1
        data: data, // e.g. { TodoItemId: 1, Title: 'hi there', IsDone: false }
        dataType: 'JSON',
        success: function () { alert('success'); },
        error: function (jqXhr) { extractErrors(jqXhr, validator); }
    });

}

Now let’s look at extractErrors:

function extractErrors(jqXhr, validator) {

    var data = JSON.parse(jqXhr.responseText), // parse the response into a JavaScript object
        errors = { };

    for (var i = 0; i < data.length; i++) { // add each error to the errors object
        errors[data[i].key] = data[i].value;
    }

    validator.showErrors(errors); // show the errors using the validator object
}

Lastly, attaching to the form’s submit event will call this whenever the Enter key is hit or the Submit button is clicked:

$('.main-content form').submit(function () {
    updateItem($(this), '/api/TodoApi/' + $('#TodoItemId').val());
});

Validating your model with Web API

One of the great things about ASP.NET 4.5’s Web API is that it’s built on the same (or similar) principles as MVC. This means that you get a lot of cool things out of your API controllers from MVC – like Action Filters.

While building my first Web API controller, I wanted to ensure that a creation or an update of an item was only done if that item was valid. I also wanted to pass any validation errors back to the client. This looks quite difficult at first because the Put and Post functions on an ApiController can’t return a result. Action Filters to the rescue!

With a simple action filter attribute, you can ensure that your models are validated and the errors are returned in a simple format to the client by decorating the appropriate methods.

Note: Code also available on Gist – https://gist.github.com/1920999

public class ValidateFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        var modelState = actionExecutedContext.ActionContext.ModelState;
        if (!modelState.IsValid)
        {
            var errors = modelState
                .Where(s => s.Value.Errors.Count > 0)
                .Select(s => new KeyValuePair<string, string>(s.Key, s.Value.Errors.First().ErrorMessage))
                .ToArray();

            actionExecutedContext.Result = new HttpResponseMessage<KeyValuePair<string, string>[]>(
                errors,
                HttpStatusCode.BadRequest
            );
        }
    base.OnActionExecuted(actionExecutedContext);
}

Now, for the controller implementation…

public class TodoApiController : ApiController
{
    private BetterMobileSpaContext db = new BetterMobileSpaContext();

    // GET /api/todoapi
    public IEnumerable<TodoItem> Get()
    {
        return db.TodoItems.ToList();
    }

    // GET /api/todoapi/5
    public TodoItem Get(int id)
    {
        return db.TodoItems.Find(id);
    }

    // POST /api/todoapi
    [ValidateFilter]
    public void Post(TodoItem value)
    {
        if (!ModelState.IsValid) return;

        db.TodoItems.Add(value);
        db.SaveChanges();
    }

    // PUT /api/todoapi/5
    [ValidateFilter]
    public void Put(int id, TodoItem value)
    {
        if (!ModelState.IsValid) return;

        db.Entry(value).State = EntityState.Modified;
        db.SaveChanges();
    }

    // DELETE /api/todoapi/5
    public void Delete(int id)
    {
        TodoItem todoitem = db.TodoItems.Find(id);
        db.TodoItems.Remove(todoitem);
        db.SaveChanges();
    }

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
}

Now a Create (PUT) and an Update (POST) request will validate the model before invoking the appropriate action. Calling Create from using a JSON request with an invalid model would look something like this:

Request

Method: PUT

Body: { Title: ”, IsDone: false }

Response

Status Code: 400 (Bad Request)

Body: [{ “key”: “Title”, “value”: “The Title field is required” }]

Using a valid model will simply result in a 200 (OK) response with no body.

NOTE: while writing this post I stumbled on these two blog posts that did the same thing and adapted my code:

EDIT 2012-02-27: Updated code to replace ValidationError type with KeyValuePair and changed filter to be after action execution so controller can perform any extra validation.

Back button broken for URL hashes in IE on WP7 Mango

I’ve been on a little twitter rant recently about this, and thought I’d use my blog as another distribution medium.

Feel free to leave comments here, here (connect forum) or here (jQuery Mobile forum).

Here’s the content of my connect forum post on the matter:

Mango seems to have brought along with it a broken browser…

When you link to an anchor on your page using a hash, the back button doesn’t seem to work too well.

For example, go to http://jquerymobile.com/demos/1.0b1/. Those in the know will realise why… To put it simply, jQuery Mobile uses hash changes to navigate between “pages” in a single or multiple page web application. It uses the hash changed events to determine when to use javascript to request the next page, load it into the DOM and perform an animation to display the next page.

Navigating forward on a jQuery Mobile site works perfectly as expected. It’s when you hit the back button that things go wrong. On a desktop browser, going from the landing page to “Into to jQuery Mobile” swipes in the intro page and hitting back swipes it out again to show the landing page. However, on Mango, hitting the back button when on the intro page does nothing. If you were on another site before you went the the demo landing page, then hitting the back button again will take you back to the previous site.

I have been working on an embedded web application recently and this has been driving me crazy! I’m seeing a Navigated event being raised by the WebBrowser control when it goes to the second page in the application, but back buttons (and manually invoking history.back() and history.go(-1) in javascript) just don’t do anything.

This seems to be a breaking change to me and would potentially mean I push to have Windows Phone 7 dropped as a targetted platform for the (relatively high traffic) website I am working on. An embarrassing idea seeing as I pushed for it to be targetted… 😦

UPDATE 14 July 2011 – It’s been noted that the jQuery Mobile docs site mentioned does work in previous builds of WP7, running IE 7 Mobile. This verifies that this issue has been introduced with IE9 Mobile on WP7 Mango.

UPDATE II 14 July 2011 – The issue is a little deeper than I initially realized. If you define a simple page with an anchor at the top of the page referencing a div at the bottom of the page then we see some more exotic behaviour. Clicking the link and hitting back will work the first time. However, you’ll notice that in the address bar the hash is not removed from the URL. This means that clicking the link and hitting back again will get the browser into a tangle. Here is the HTML snippet I used:

<a href="#bottom">bottom</a>
<div style="height: 1000px"></div>
<a id="bottom" href="#" onclick="javascript:history.back();return false;">back</a>

UPDATE III October 2011 – The issue appears to have been fixed in Mango RTM.

Making MVC 3 a little more… dynamic

The other day I ran into a little problem. I wanted to be able to post some JSON to a web service and have it accept a whole bunch of data (not exactly the technical term) that was not predefined.

Given this very vague problem, I decided I wanted to use ASP .NET MVC 3. So starting with File\New Project, I added a method to my HomeController as follows:

[HttpPost]
public ActionResult DoSomething(string a, string b, dynamic c)
{
    return new EmptyResult();
}

The thought behind this was that I wanted to supply two required properties (a and b) and basically have a dynamic bag for everything else. Also, I didn’t want to be restricted to a single layer of properties. Instead, I wanted to be able to pass a deep tree and have it totally accessible.

Then I pulled up fiddler and set up a request with the following details:

Method POST
URL http://localhost:2643/Home/DoSomething
Request Headers User-Agent: Fiddler

Content-Type: application/json

Request Body { a: “Hello”, b: “World”, c: { d: “this”, e: “is”, f: “dynamic” } }

image

When I hit execute and set a breakpoint on the action, I received the following values in my locals:

clip_image002

As expected, the JsonValueProviderFactory has kicked in and populated the values of a and b using the JSON provided. However, it cannot match c properly so simply throws an object at it that is not dynamic and does not have values on it, hence useless.

After some searching, I found a few valid solutions. The most useful one was this blog post by Anoop Madhusundanan. In this post, he describe the process of using a model binder and a custom model binder attribute to identify parameters of an action method that are to use the model binder. This seemed like a great solution for me, but had a couple of little problems… Most importantly, Anoop’s solution does not allow for multiple parameters on an action method or mapping the parameter to the property in the JSON object.

I came up with a very similar way of solving this problem. I created a class named DynamicJsonBinder which implements IModelBinder, just like Anoop. The main difference with my solution is that I provided a way to ensure that the parameter being populated with the dynamic object is mapped based on the name of the parameter. This is done by using a switch on the attribute called MatchName, which will cause the binder to look at the parameter name and find the matching property in the JSON object to return.

Here’s the attribute:

using System.Web.Mvc;

public class DynamicJsonAttribute : CustomModelBinderAttribute
{
    public override IModelBinder GetBinder()
    {
        return new DynamicJsonBinder(MatchName);
    }

    public bool MatchName { get; set; }
}

And here’s the model binder:

using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Web.Helpers;
using System.Web.Mvc;

public class DynamicJsonBinder : IModelBinder
{
    private readonly bool matchName;

    public DynamicJsonBinder(bool matchName)
    {
        this.matchName = matchName;
    }

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var contentType = controllerContext.HttpContext.Request.ContentType;
        if (!contentType.StartsWith("application/json", StringComparison.OrdinalIgnoreCase))
            return null;

        string bodyText;
        using (var stream = controllerContext.HttpContext.Request.InputStream)
        {
            stream.Seek(0, SeekOrigin.Begin);
            using (var reader = new StreamReader(stream))
                bodyText = reader.ReadToEnd();
        }

        if (string.IsNullOrEmpty(bodyText)) return null;

        var desiralized = Json.Decode(bodyText);

        if (!matchName) return desiralized;

        var members = desiralized.GetDynamicMemberNames() as IEnumerable&lt;string&gt;;
        return members == null
            || members.Contains(bindingContext.ModelName)
            ? desiralized[bindingContext.ModelName] : null;
    }
}

So if by adding a DynamicJson attribute to the c parameter on the action method, we’ll get the following:

public ActionResult DoSomething(string a, string b, [DynamicJson] dynamic c)
{
    return new EmptyResult();
}

Debugging into this with the same post request as earlier:

clip_image004

We see that c has been populated with a DynamicJsonObject (the result of the call to Json.Decode) and that it has recursively mapped the properties under the JSON object to dynamic object. However, the value is the entire JSON object, that is it’s not mapped to the c property.

To enable name matching we get the following code:

[HttpPost]
public ActionResult DoSomething(string a, string b, [DynamicJson(MatchName = true)] dynamic c)
{
    return new EmptyResult();
}

Debugging we get:

clip_image006

And we see that the property has been matched correctly and the correct value is passed through to the parameter.

Using these two simple classes and a bit of dynamic know-how, I’ve managed to post anything to my web service and have it dynamically accessible. This basically means we have the equivalent of a property bag on steroids that can be queried and manipulated in the same way as any dynamic object. We can even re-serialize the dynamic object to JSON and store it as is if required.

A Day in the Life of a Metro-veloper

This is a follow-up post to my Windows Phone 7 presentation last week at the SDDN.

I have uploaded my powerpoint deck to SlideShare.

Some of the sample apps I used are available on MSDN.

And there was a recent video on youtube of an awesome golfing app that really shows the power of the WP7 UX.

push notifications in windows phone 7

Basics

There’s lots of information hiding within about 10 links on MSDN about how push notifications for WP7. The short of it is the phone opens a HttpNotificationChannel which gives it a unique URI for your applications to send notifications to it.

There are three types of notifications – tile, toast and raw. Tile notifications are used for changing the background, count and title of the application when it is pinned to the Start screen. Toast notifications provide unobtrusive notifications to users when they are outside the application, allowing them to step in easily to perform an action. Raw notifications are messages of any format that can be sent to the phone application and received while it is active.

The problem with the information out there is that it is slightly conflicting because of the recent changes to the APIs with different versions of the toolkit. As a result, I spent many hours trying to get this relatively simple piece of functionality working. At one point I decided that whenever I got something working I’d try shrink wrap it and publish it, so here we go…

DISCLAIMER: WOMM…

WARNING: There’s a lot of code here… Please read the Client Side and Server Side sections before believing you are qualified to download the sample. The sample has a thin WPF client implementation over the server side code to make it a little easier to get started. Remember to check the Debug output window for the phone application when you need a Uri to send a notification.

Client Side

My aim was to get the code required for subscribing to notifications to be as simple as possible. I got it down to the following few lines of code:

// in App.ctor
NotificationService notificationService = new NotificationService("some funky channel name");
notificationService.RawNotificationReceived += RawNotificationReceived;
notificationService.ToastNotificationReceived += ToastNotificationReceived;
notificationService.ChannelUriUpdated += ChannelUriUpdated;

// in Application_Launching & Application_Activated
notificationService.Subscribe();

// event handlers
void ToastNotificationReceived(object sender, ToastNotificationReceivedEventArgs e)
{    // example of handling a toast notification within the application
    MessageBox.Show(e.Message, e.Title, MessageBoxButton.OK);
}

void RawNotificationReceived(object sender, RawNotificationRecievedEventArgs e)
{
    MessageBox.Show(e.Message);
}

void ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
    // call a webservice to report e.ChannelUri
}

There are a couple of things to note. Firstly, the Subscribe() method is called in the Application_Launching and Application_Activated events. This is due to the tombstoning nature of WP7.

Next, there are separate event handlers for Toast and Raw notifications. They are handled differently because toast notifications have a definite payload format that specifies the ability to send to pieces of text (a title and a message). A raw notification can be anything, which is why I have decided to simply surface the string representation of the payload. A toast must also be handled if the application is executing – i.e. it will not be displayed by the OS.

Also, there is no tile notification handler. This is because the OS handles this directly.

Lastly, the ChannelUriUpdated event handler. This is necessary for your phone application to let the server application know that there is a phone waiting for notifications. When this occurs, the phone application should call a web service to register the URI. The NotificationService class will write this information to the Debug console whenever the application is started, so it is not required for debugging purposes.

Server Side

While I was busy working on the client side, I realised that obviously I’d eventually need to have some server side code to send the notifications. Again, I wanted to simplify it as much as possible:

NotificationService service = new NotificationService();

// send a raw notification
service.SendRaw(clientUri, messageText);

// send a toast notification
service.SendToast(clientUri, titleText, messageText);

// send a tile notification
service.SendTile(clientUri, backgroundImageUri, countValue, titleText);

See… simple. Smile

The Real Code

As I mentioned earlier, you can download all this here. Otherwise, feel free to read through this (or just copy/paste it) to get a better understanding of how the HttpNotificationChannel works.

Client side NotificationService implementation:

public class NotificationService
{
    string channelName;
    HttpNotificationChannel channel;

    public NotificationService(string channelName)
    {
        this.channelName = channelName;
    }

    /// <summary>
    /// Subscribes to the notification events on the channel.
    /// If the channel doesn't already exist, it will be created
    /// and bound to shell tile and toasts.
    /// </summary>
    public void Subscribe()
    {
        if (channel == null) BindChannel();
    }

    /// <summary>
    /// Unubscribes from the notification events on the channel.
    /// </summary>
    public void Unsubscribe()
    {
        if (channel != null) UnsubscribeFromChannelEvents();
    }

    /// <summary>
    /// Finds or creates the notification channel and binds the shell tile
    /// and toast notifications as well as events.
    /// </summary>
    private void BindChannel()
    {
        channel = HttpNotificationChannel.Find(channelName);

        if (channel == null || channel.ChannelUri == null)
        {
            if (channel != null) DisposeChannel();

            channel = new HttpNotificationChannel(channelName);
            channel.ChannelUriUpdated += channel_ChannelUriUpdated;
            channel.Open();
        }
        else System.Diagnostics.Debug.WriteLine(channel.ChannelUri.AbsoluteUri);

        SubscribeToChannelEvents();

        if (!channel.IsShellTileBound) channel.BindToShellTile();
        if (!channel.IsShellToastBound) channel.BindToShellToast();
    }

    /// <summary>
    /// Subscribes to the channel's events.
    /// </summary>
    private void SubscribeToChannelEvents()
    {
        channel.ShellToastNotificationReceived += channel_ShellToastNotificationReceived;
        channel.HttpNotificationReceived += channel_HttpNotificationReceived;
        channel.ErrorOccurred += channel_ErrorOccurred;
    }

    /// <summary>
    /// Unsubscribes from the channel's events
    /// </summary>
    private void UnsubscribeFromChannelEvents()
    {
        channel.ShellToastNotificationReceived -= channel_ShellToastNotificationReceived;
        channel.HttpNotificationReceived -= channel_HttpNotificationReceived;
        channel.ErrorOccurred -= channel_ErrorOccurred;
    }

    /// <summary>
    /// Closes the channel and disposes it.
    /// </summary>
    private void DisposeChannel()
    {
        channel.Close();
        channel.Dispose();
        channel = null;
    }

    /// <summary>
    /// Event handler for the ChannelUriUpdate event.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void channel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
    {
        channel.ChannelUriUpdated -= channel_ChannelUriUpdated;
        System.Diagnostics.Debug.WriteLine(e.ChannelUri.AbsoluteUri);
        OnChannelUriUpdated(e);
    }

    /// <summary>
    /// Raised when the notification channel is given a URI.
    /// </summary>
    /// <remarks>
    /// This is when you would call a web service to tell it that a client is
    /// registered and what the notification URI is.
    /// </remarks>
    public event EventHandler<NotificationChannelUriEventArgs> ChannelUriUpdated;

    /// <summary>
    /// Raises the ChannelUriUpdated event.
    /// </summary>
    /// <param name="e"></param>
    protected virtual void OnChannelUriUpdated(NotificationChannelUriEventArgs e)
    {
        if (ChannelUriUpdated != null) ChannelUriUpdated(this, e);
    }

    /// <summary>
    /// Event handler for the HtppNotificationReceived event.
    /// This is called when a raw notification is received.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void channel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
    {
        byte[] bytes;
        using (var stream = e.Notification.Body)
        {
            bytes = new byte[stream.Length];
            stream.Read(bytes, 0, (int)stream.Length);
        }
        var message = Encoding.UTF8.GetString(bytes, 0, bytes.Length);

        OnRawNotificationReceived(message);
    }

    /// <summary>
    /// Occurs when a raw notification is received.
    /// </summary>
    public event EventHandler<RawNotificationRecievedEventArgs> RawNotificationReceived;

    /// <summary>
    /// Raises the RawNotificationReceived event on the UI thread.
    /// </summary>
    /// <param name="message"></param>
    protected virtual void OnRawNotificationReceived(string message)
    {
        Deployment.Current.Dispatcher.BeginInvoke(() =>
        {
            if (RawNotificationReceived != null) 
                RawNotificationReceived(
                    this, 
                    new RawNotificationRecievedEventArgs(message)
                    );
        });
    }

    /// <summary>
    /// Event handler for the ShellToastNotificationReceived event.
    /// This occurs when a toast notification is received on the channel.
    /// </summary>
    /// <remarks>
    /// This must be handled by the application if it is running when a toast is received.
    /// </remarks>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void channel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
    {
        var title = e.Collection.Values.First();
        var message = e.Collection.Values.Skip(1).FirstOrDefault() ?? string.Empty;
        OnToastNotificationReceived(title, message);
    }

    /// <summary>
    /// Occurs when a toast notification is received.
    /// </summary>
    /// <remarks>
    /// This must be handled by the application if it is running when a toast is received.
    /// </remarks>
    public event EventHandler<ToastNotificationReceivedEventArgs> ToastNotificationReceived;

    /// <summary>
    /// Raises the ToastNotificationReceived event on the UI thread.
    /// </summary>
    /// <param name="title"></param>
    /// <param name="message"></param>
    protected virtual void OnToastNotificationReceived(string title, string message)
    {
        Deployment.Current.Dispatcher.BeginInvoke(() =>
        {
            if (ToastNotificationReceived != null) 
                ToastNotificationReceived(
                    this, 
                    new ToastNotificationReceivedEventArgs(title, message)
                    );
        });
    }

    /// <summary>
    /// Event handler for the ErrorOccurred event.
    /// Handles different events according to ErrorType.
    /// </summary>
    /// <remarks>
    /// Needs more work... ;-(
    /// </remarks>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    void channel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e)
    {
        switch (e.ErrorType)
        {
            // something went severely wrong. lets wait a while before trying again.
            case ChannelErrorType.ChannelOpenFailed:
                DisposeChannel();
                System.Threading.Thread.Sleep(60000);
                BindChannel();
                break;
            // an image uri has been referenced in a notification that was
            // not bound to the shell tile.
            case ChannelErrorType.MessageBadContent:
                break;
            // too many notifications have been received in too short a time span.
            case ChannelErrorType.NotificationRateTooHigh:
                break;
            // a bad payload was received. re-establish the connection to overcome this.
            case ChannelErrorType.PayloadFormatError:
                DisposeChannel();
                BindChannel();
                break;
            // the type notifications we're receiving is going to change.
            case ChannelErrorType.PowerLevelChanged:
                break;
            default:
                break;
        }
    }
}

public class RawNotificationRecievedEventArgs : EventArgs
{
    public string Message { get; private set; }
    public RawNotificationRecievedEventArgs(string message)
    {
        Message = message;
    }
}

public class ToastNotificationReceivedEventArgs : EventArgs
{
    public string Title { get; private set; }
    public string Message { get; private set; }
    public ToastNotificationReceivedEventArgs(string title, string message)
    {
        Title = title;
        Message = message;
    }
}

Server side code:

public interface INotificationService
{
    NotificationResponse SendTile(string uri, string backgroundImageUri, int count, 
                                  string title, [Optional] Guid messageId);
    NotificationResponse SendToast(string uri, string text1, [Optional] string text2, 
                                   [Optional] Guid messageId);
    NotificationResponse SendRaw(string uri, string message, [Optional] Guid messageId);
}

public class NotificationService : INotificationService
{
    const int maxPayloadLength = 1024;

    const string targetHeader = "X-WindowsPhone-Target";
    const string notificationClassHeader = "X-NotificationClass";
    const string messageIdHeader = "X-MessageID";
    const string notificationStatusHeader = "X-NotificationStatus";
    const string subscriptionStatusHeader = "X-SubscriptionStatus";
    const string deviceConnectionStatusHeader = "X-DeviceConnectionStatus";

    const string tileMessageFormat =
        "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
        "<wp:Notification xmlns:wp=\"WPNotification\">" +
            "<wp:Tile>" +
                "<wp:BackgroundImage>{0}</wp:BackgroundImage>" +
                "<wp:Count>{1}</wp:Count>" +
                "<wp:Title>{2}</wp:Title>" +
            "</wp:Tile>" +
        "</wp:Notification>";
    /// <summary>
    /// X-WindowsPhone-Target: token
    /// </summary>
    const string tileTarget = "token";
    /// <summary>
    /// X-NotificationClass: 1
    /// </summary>
    const string tileNotificationClass = "1";

    const string toastMessageFormat =
        "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
        "<wp:Notification xmlns:wp=\"WPNotification\">" +
            "<wp:Toast>" +
                "<wp:Text1>{0}</wp:Text1>" +
                "<wp:Text2>{1}</wp:Text2>" +
            "</wp:Toast>" +
        "</wp:Notification>";
    /// <summary>
    /// X-WindowsPhone-Target: toast
    /// </summary>
    const string toastTarget = "toast";
    /// <summary>
    /// X-NotificationClass: 2
    /// </summary>
    const string toastNotificationClass = "2";

    /// <summary>
    /// X-NotificationClass: 3
    /// </summary>
    const string rawNotificationClass = "3";

    public NotificationResponse SendTile(string uri, string backgroundImageUri, int count, 
                                         string title, [Optional] Guid messageId)
    {
        var str = string.Format(tileMessageFormat, backgroundImageUri, count, title);
        return SendNotification(uri, str, tileNotificationClass, tileTarget, messageId);
    }

    public NotificationResponse SendToast(string uri, string text1, [Optional] string text2, 
                                          [Optional] Guid messageId)
    {
        var str = string.Format(toastMessageFormat, text1, text2);
        return SendNotification(uri, str, toastNotificationClass, toastTarget, messageId);
    }

    public NotificationResponse SendRaw(string uri, string message, 
                                        [Optional] Guid messageId)
    {
        return SendNotification(uri, message, rawNotificationClass, messageId: messageId);
    }

    private NotificationResponse SendNotification(string uri, string message, 
                                                  string notificationClass, 
                                                  [Optional] string target, 
                                                  [Optional] Guid messageId)
    {
        var payload = Encoding.UTF8.GetBytes(message);
        if (payload.Length > maxPayloadLength) 
            throw new ArgumentException(
                "The message provided is longer than the maximum payload length (1024B).",
                message
                );

        var sendNotificationRequest = WebRequest.Create(uri) as HttpWebRequest;
            
        sendNotificationRequest.Method = WebRequestMethods.Http.Post;
        sendNotificationRequest.ContentLength = payload.Length;
        sendNotificationRequest.ContentType = "text/xml";

        // X-NotificationClass: 1, 2, 3
        sendNotificationRequest.Headers.Add(notificationClassHeader, notificationClass);
        // X-WindowsPhone-Target: token, toast
        if (!string.IsNullOrWhiteSpace(target)) 
            sendNotificationRequest.Headers.Add(targetHeader, target);
        // X-MessageId: 00000000-0000-0000-0000-000000000000
        if (messageId != null && messageId != Guid.Empty) 
            sendNotificationRequest.Headers.Add(messageIdHeader, messageId.ToString());

        using (var requestStream = sendNotificationRequest.GetRequestStream())
            requestStream.Write(payload, 0, payload.Length);

        HttpWebResponse response;
        string errorMessage = null;
        try
        {
            response = sendNotificationRequest.GetResponse() as HttpWebResponse;
        }
        catch (WebException ex)
        {
            response = ex.Response as HttpWebResponse;
            errorMessage = ex.Message;
        }
        return new NotificationResponse
        {
            MessageId = response.Headers[messageIdHeader],
            ErrorMessage = errorMessage,
            NotificationStatus = response.Headers[notificationStatusHeader],
            SubscriptionStatus = response.Headers[subscriptionStatusHeader],
            DeviceConnectionStatus = response.Headers[deviceConnectionStatusHeader],
            StatusCode = response.StatusCode
        };
    }
}

public class NotificationResponse
{
    public string ErrorMessage { get; set; }
    public DateTimeOffset Timestamp { get; set; }
    public string MessageId { get; set; }
    public HttpStatusCode StatusCode { get; set; }
    public string NotificationStatus { get; set; }
    public string DeviceConnectionStatus { get; set; }
    public string SubscriptionStatus { get; set; }
}

DOWNLOAD IT NOW!

Remember, to get the Uri just watch the Debug output window when your WP7 application starts up.

Good luck Smile

[Update 2010-08-27] Added extra check to Client.NotificationService.BindChannel() so that a channel that already exists but does not have a URI is disposed and a new one is created.