I know, I know: apples, oranges etc. It's not really, though - this is actually quite straight-forward. But first, some background.

I was recently involved in building another iPhone application for an enterprise customer. We had previously dealt with that customer and had a good working relationship and level of trust with them, but a huge part of that trust was the visibility that we provided as to what was going on. It's mostly off-topic for this post, but the way we did it involved using Team Foundation Server, giving the client's people accounts and making sure that the client's product owner could log in at any time and see how the project was going.

With the iPhone application, we wanted to do exactly the same. The problem was that we hadn't had that much experience using TFS to build iPhone apps. Most of our collective efforts had either been in Objective C using git (the stock-standard approach that Xcode pushes) or C#/MonoTouch using Mercurial (my experience). While both of those approaches are fine for personal and small projects, we really wanted all the other bonus points that TFS provides (continuous integration, work item tracking, reporting, web-based project portal etc.)

So how'd we do it? Well, the first thing to note is that we're not actually building the application bundle using TFS - yet. That still requires a MacBook, MonoDevelop and a bunch of other stuff. We'll probably get there soon using custom build tasks, rsync, ssh and a few other things, but we're not quite there yet.

What we do have is a working continuous integration and nightly build, plus running tests using MSTest. The nice thing is that it actually wasn't that hard.

  1. Open the project in Visual Studio (not MonoDevelop). You'll probably need something like Chris Small's MonoTouch Project Converter to make this work happily.
  2. Include monotouch.dll in your /lib directory and reference it from there rather than from the GAC. (It won't be in the GAC on your build server, and nor should it be.)
  3. If you have other dependencies (e.g. System.Data), copy those from your MacBook into /lib as well and reference those from there.
  4. Done :)

The key point to note when you're building your app is that you're not going to be able to easily test your ViewController classes using MSTest, so make them dumb. If there's business logic in there, extract it out into your domain model. If there's data access logic in there... well... you're doing it wrong anyway and you should definitely extract that out :)

You'll end up with an app that has a dumb(ish) UI shell wrapped around a bunch of well-tested business logic classes. The added bonus of doing it this way is that you can then re-use a lot of that code when you write your WP7 or Android version.

The outcome? The visibility we wanted from the reporting and work item tracking side of things, plus a CI build that didn't require witchcraft to configure, plus automated unit tests.

The only real down-side of this approach is that the build we're unit-testing isn't the build we're shipping - we still have to build that manually on a MacBook somewhere. It does, however, give us a good indication of our overall code quality and a reliable safety net for refactoring.