hello! burin

Automating Your Way out of the Dark Ages: Our Experience with (And Without) PhoneGap Build

I’ve received a few questions about how we’ve used PhoneGap Build in our development workflow and thought it would be good to share some of our experiences with it. This isn’t PhoneGap specific, but I wanted to give a shout out to some tools that helped us move faster. Some of these workflows could help with native app development as well.


It’s helpful to first start with a timeline of how we built our iOS and Android applications and got them in people’s hands for testing:

Dark Ages: Build Manually with Xcode & Eclipse

At first, we were living in a world of enlightenment, knocking out features, closing stories, basking in the fame and fortune. We made one big mistake though:

We were using Chrome to test.

“What’s the big difference anyway? It’s all WebKit.” Well it turns out that there’s more to it than WebKit. Differences between iOS Chrome, Mobile Safari, and a UIWebView. Oh and there’s Chrome Desktop, Chrome for Android, and even Android’s WebView — which could be Chrome-powered (new devices), or crap-powered (old devices). Oh, and there’s that Blink thing.

This period is what I consider to be the First Dark Ages. It was a pain to get a “native” version of the app to your phone.

  • git pull
  • configure the api endpoint and different env options
  • concatenate/minify the javascript
  • copy files over to the Xcode project
  • grab your phone
  • build to the phone
  • grab your boss’ phone
  • build to that phone
  • grab your test phone
  • build to that phone

We’d go days, even weeks without looking at the app in “native” form. We had a ton of bugs because we (devs) were never testing the wrapped applications. I won’t even get into Apple Provisioning Hell, which didn’t help matters at all.

Renaissance: PhoneGap Build

There are two parts to getting the new app on a phone: building the app and distributing the app.

When we heard about and started using PhoneGap Build, it made both of things things seamless. PhoneGap Build is a service that takes your uploaded HTML, CSS, and JavaScript, and builds it for multiple native platforms. From that point forward, you’ll have a build available to install straight from a URL.

We created our www zip file manually and uploaded it via the PhoneGap Build web interface. It. Was. Awesome. But we needed to go faster!

We eventually ended up with a script that would run on our build server that did a few things after every commit:

  • git pull
  • package the app into a zip (configured for env, concat/minify)
  • upload that zip to PhoneGap Build via the API

After that, anyone on the team could go to the PhoneGap Build site from their iOS, Android, or BlackBerry device and install the app straight from the site.

This was awesome, and we were installing new builds multiple times a day. We were able to test, iterate, and see and fix bugs way quicker than during the Dark Ages.

Outgrowth: Manual Builds Again :(

If this were a perfect world, we’d be using PhoneGap Build forever, but it’s not, and we eventually outgrew it.

We built a feature and it needed a PhoneGap plugin that wasn’t supported by PhoneGap Build. They’ve added support for a few more plugins since then, but ours wasn’t supported at the time.

We had to go back to manually building and distributing our ad-hoc distributions again.

Yeah. It sucked. But we had a taste of how things should be done. We needed to move faster!

Holy Grail? Build & Distribution Automation

Building an IPA or APK wasn’t a big deal anymore, since we just had to:

  • git pull
  • run our javascript build script
  • push a button

Distributing and getting it to the phones the way we were doing it before, by just telling someone to go to a URL, was difficult. Not only that, but getting someone’s iOS device provisioned was a pain:

  • Me: “Want to help? Install this app and email me your UDID
  • Teammate: “I emailed you my UDID the other day, when can I install the app?”
  • “Um.. let me check. I need to add it to the provisioning profile.”
  • Add UDID to provisioning profile
  • git pull
  • Run javascript build script
  • Push a button
  • Forgot to get new provisioning profile into Xcode
  • Refresh profiles
  • Push a button
  • Send it out
  • “it wouldn’t install, what’s the deal?”
  • “lemme try again”
  • restart xcode
  • push a button
  • “ok try now”

Yeah, that sucked.

TestFlight to the rescue! (for iOS)

That whole “email me your UDID”, find a way to upload it to a server, and notify-everyone-when-there’s-a-new-build-ready dance is all a piece of cake with TestFlight.

All you have to do is upload an IPA that you create with Xcode, then gathering UDIDs, permissions, and sending notifications of new builds is all done via their web UI.

Hockey App (and then Dropbox)to the rescue! (for Android)

Android doesn’t have that whole device provisioning thing, so it was as simple as just getting the APK somewhere for people to download. Initially we used HockeyApp (paid TestFlight competitor with support for iOS and Android), but eventually we just built to a shared Dropbox folder that all the test Android devices had access to.

Automating the Build AND Distribution

Up to this point, we were still pushing a button in Xcode/Eclipse and clicking around in web UI’s to upload new builds. STILL TOO SLOW. PhoneGap Build spoiled us!


There are a few command line tools available that help you build an IPA that you can then distribute via TestFlight: xcrun, xcodebuild, and agvtool. Luckily, you don’t need to mess with any of them with a gem called shenzhen.

Shenzhen wraps these commands for a nicer interface. Once our project files are all configured properly, all we need to do to build the IPA is:

ipa build -s {{projectName}} -c Release'

After that, we just distribute it to TestFlight:

ipa distribute --api_token {{apiToken}} --team_token {{teamToken}} --notify --replace --lists "Core" --notes \"{{notes}}\"'

This notifies all the people in the “Core” distribution list on TestFlight, and sets the release notes as something that we grab via a prompt when running the script.


For Android, all we do is:

ant release

And do a copy of the file to our shared Dropbox folder:

mv bin/{{AppName}}.apk {{copyTo}}/{{appName}}.apk'

End Of The Line?

Now, we just kick off a single command that:

  • grabs new code
  • pre-builds the app (concat/minify, etc)
  • builds the native apps
  • notifies and makes the app installable to the team

Is that it? Nope, there’s more! From enabling the ability to have multiple versions (git feature branches) installed simultaneously on the devices to having this run automagically every day are things that we can do to take this further.

I’d love to get to the a point where we can use a single tool to do what we are doing with grunt, shenzhen, TestFlight, and Dropbox, like we were with PhoneGap Build. If our application used the supported PhoneGap Build plugins, I wouldn’t even be writing the last half of this post, since we’d just be using them :)


PhoneGap Build


Hockey App



  1. helloburin posted this