The XCode IDE can be very flaky when trying to produce consistent build results. Sometimes you end up having to deal with flotsam from earlier builds and running a clean or clearing out the project's "derived data" folder. Beyond that, to produce a build you need to run three separate processes: an archive build, an export of that build to an IPA file, and an upload of that IPA file to iTunes Connect. For my team, it became a 30-40 minute process that we potentially needed to do several times in a row if we were rapidly patching bugs for a release. Automation was an important step towards gaining time back and making release day a sane experience.

When I began looking into automated build solutions, I started with CircleCI since it was the service my company was already using for CI. Circle offers a Mac VM service to run iOS and Mac builds starting at around $39/month, but if your situation becomes mobile-focused, it can balloon to around $450/month. I decided to take advantage of the 14 day trial to get something running.

As I explored the settings for Circle's iOS builds I discovered Fastlane, the utility they were using to manage the build process on their servers. The more I dug into Fastlane, the more I understood how little we were getting from Circle. I took a couple of days getting our build and testing processes working directly through Fastlane.

Fastlane is a suite of tools which can be run from the command line to automate many parts of the XCode toolchain. The real power, however, comes from using a Fastfile to create rich build scripts from these tools.

before_all do
  # increment_build_number
  cocoapods
end

lane :test do
  scan
end

lane :beta do
  match
  gym
  pilot
end

after_all do |lane|
  # This block is called, only if the executed lane was successful
end

error do |lane, exception|
  # Something bad happened
end

The first "lanes" I setup were for scan and gym, which run unit-testing and build operations, respectively. With scan in our build environment (a Mac Mini slaved to our internal Jenkins server) the only issue I ran into was with the iOS Simulator. Periodically when scan would try to run UI Tests, it wouldn't be able to connect to the simulator and the tests would fail. After a couple of Google searches, I found an environment variable that could be added to the Fastfile, ENV["FASTLANE_EXPLICIT_OPEN_SIMULATOR"] = "1" which basically warms up the simulator before the tests. Once this was added to the testing lane, I stopped having problems with the automated UI Tests.

gym was profoundly easy to setup. I created a shared Scheme from XCode and used that to select build configurations. The only impediment came from that perennial iOS villain, provisioning profiles. Thankfully the solution to that came with match.

match, it turns out, is more that just a way to handle certificates and provisioning for builds. Its methodology goes hand-in-hand with the ideas in the Code Signing Guide for Teams. For an established team with a lot of people, the sort of paradigm shift that match requires can be a bit drastic, but for my team it was a good solution.

Once I had established the shared team certificate and provisioning profile through match, the build process through gym was complete. The last piece was to upload the build to iTunes Connect. This is accomplished through pilot. The setup for pilot is nearly as simple as gym, but it requires using the account of a development team member. It would be impractical and insecure for an actual team member to use their personal account, so I created a new Apple ID for the build. With match the new account worked immediately and the build was uploading to iTunes Connect.

Over the last two months, the build process has required very little tweaking. I've been updating Fastlane regularly to get around issues usually caused by minor changes to the Apple services. Even though we are only performing nightly builds currently, I am confident that the automation I've already achieved could be expanded for true CI down the road.