Deploying your iOS ad-hoc builds with the BetaBuilder gem and TestFlight

21 January 2011 Deploying your iOS ad-hoc builds with the BetaBuilder gem and TestFlight

Until recently, deploying ad-hoc builds of iOS apps to beta testers was a pain. You needed to maintain a list of testers, obtain their UUIDs, manage provisioning profiles and compile and distribute the builds (and provisioning profiles) yourself.

As of iOS 4.0, it has been possible to package up builds as IPA files and distribute them via the web, making it possible for your testers to install your builds with a single tap.

Back in August last year, iOS developer Hunter Hillegas released iOS BetaBuilder, a small OSX utility that allowed you to take your built app and create a distributable package, including an HTML index page and manifest file that you could upload to your website and send the link to your testers.

Whilst this was a great first step, I felt it suffered from shortcomings, namely that it was GUI app that I was required to run manually. In addition, I still had to launch Xcode and run a Build and Archive myself. Ideally, I wanted the release process to be as automated as possible.

Introducing the BetaBuilder gem

As a Ruby developer, I am used to using Rake to run automated tasks so I took the various snippets of code that I had been using to automate the various parts of the Xcode build process and packaged them up as a Rake task library that I released, with Hunter's permission, as the BetaBuilder Ruby gem.

Using the gem is straightforward, and you can use it like any other Rake task library. If you're not familiar with Rake, there is a nice article by Martin Fowler that covers a lot of the basics.

Here's a simple BetaBuilder configuration:

BetaBuilder::Tasks.new do |config|
  # your Xcode target name
  config.target = "MyGreatApp"

  # the Xcode configuration profile
  config.configuration = "Adhoc" 
end

Adding this to your project's Rakefile will generate two tasks in the beta namespace (you can override this). You can view the available tasks by running rake -T from the command line. The following tasks are available:

rake beta:build     # Build the beta release of the app
rake beta:package   # Package the beta release as an IPA file

So, you now have a way of quickly building and packaging your app as an IPA. You don't have to run them both; Rake has task dependencies and running rake beta:package will automatically run the build task first.

So what about deploying your app?

Deploying your app to TestFlight

In the first release of BetaBuilder, it was possible to distribute your built package using the same web-based method as the iOS BetaBuilder OSX app. Since then, BetaBuilder has introduced the concept of deployment strategies.

The gem currently bundles two strategies (and it's possible to create your own too). The first is the original web-based deployment strategy: your app will be packaged up with an HTML index file and manifest file and will be copied over to a remote server using SCP. For more information on this, check out the README on Github.

What I wanted to focus on in this post is distributing your app via the excellent TestFlight service, which has just launched publicly at the time of writing, having been in private beta for several months.

TestFlight is designed to be an end-to-end solution for ad-hoc app distribution, including managing of all of your apps, releases and testers. It also lets you create distribution lists to group your testers (e.g. Internal, Beta Testers). Better yet, it's free for developers.

Having used TestFlight during it's beta period, I'm convinced that this is the best way of distributing your ad-hoc builds. It makes everything simple for both you as a developer, and your testers. Better yet, they provide a simple API for publishing builds so it made perfect sense to create a TestFlight deployment strategy for the BetaBuilder gem!

Once again, configuration is straightforward; all you need are your TestFlight API token and team token:

BetaBuilder::Tasks.new do |config|
  config.target = "MyGreatApp"
  config.configuration = "Adhoc"   

  # configure deployment via TestFlight
  config.deploy_using(:testflight) do |tf|
    tf.api_token  = "YOUR_API_TOKEN"
    tf.team_token = "YOUR_TEAM_TOKEN"
  end
end

Now, in addition to the previous tasks, a new task called beta:deploy will be available. When you run this task, you will be prompted for the release notes for your build, then your package will be build and uploaded directly to TestFlight.

It is also possible to configure the task to fetch your release notes programatically (e.g. from a CHANGELOG file) and you can also specify which distribution lists the release should be made available to. Again, see the README for more details.

Finally, if you would rather not fire up Terminal.app to deploy your builds and would rather do everything from Xcode, that is easy too. Simply create a new Shell Script target in your project that contains only a "Run Script" build phase containing:

rake beta:deploy

If you have problems, you my need to make sure that your PATH and other environment variables are set up correctly. I usually find adding a source ~/.profile to the beginning of my Xcode script build phases ensures everything works as expected - there may be a better way of doing this however.

Now you simply need to select the new target in Xcode and hit build. Thanks to Colin Humber of TestFlight for this suggestion (he tells me that they are using the gem for some internal projects at TestFlight - cool!).

The gem is licensed under the MIT license and the code is available on Github. If you have any ideas for improvements or alternative deployment strategies, please get in touch, or fork the project on Github and send me a pull request.

Addendum: archiving your builds

One thing I didn't originally cover was archiving your builds. @simonj asked me on Twitter:

Hi Luke - cheers for update of betabuilder. Was wondering though, what do you do about archiving dsyms?

It's a good question; it's usually a good idea to archive your builds (including their dSYM files) so you can symbolicate any crash logs that users send you. Xcode can do this for you with it's "Build and Archive" option. It packages up the build and the dSYM in an "apparchive" file (basically a zip file) in ~/Library/MobileDevice/Archived Applications/.

The good news is, BetaBuilder can do this for you too. It generates a beta:archive task that will do exactly the same thing as "Build and Archive". It even saves the archive to the same location. You can also configure BetaBuilder to automatically archive each build by setting the auto_archive option to true (it's false by default). You can then use something like Dropbox to back-up this archive directory.