Build distribution automation with Fastlane and Travis CI — iOS

Neel Bakshi
Practo Engineering
Published in
8 min readOct 10, 2017

Ever felt like you were spending a considerable amount of your office hours giving your tester a build. You are working on building a version rather than putting fixes on the bugs reported by them. Well if you did, you’re in luck. We are going to discuss how to solve that problem using two of the simplest tools available for us iOS developers.

  1. Fastlane
  2. Travis CI

Before we had Swift integrated into our project (which was written in Objective- C), our build times were pretty small. But that changed as we started writing more and more in Swift. Creating an archive took 15–20 mins from a measly 3–5 mins earlier. We tried all the optimisation techniques suggested by Apple, but to our dismay, it helped us just by a few minutes. If you were to include the time to upload the build it would easily take us 30–40 mins. So it was clear that we had to solve this problem since it eats away developer time and their productivity.

So let’s begin :)

If you want to jump right to the tutorial please scroll down. For the curious ones here’s a little insight about why I chose Fastlane and Travis.

Fastlane:

  1. This tool makes it very easy for you to automate your build processes. All your processes go in these pretty little “lanes”, which are readable, so even a newbie knows exactly what is going on by looking at the code. Moreover, the outputs which fastlane provides are very insightful and easy to read as well.
  2. Manages code signing :O !!!
  3. Fastlane provides beautiful integration with third party apps like Fabric, HockeyApp, Testflight etc. We use Fabric to upload builds from where our testers download builds. Since I wanted to avoid writing integration code with Fabric, Fastlane was a pretty obvious choice for me.
  4. It has an amazing developer community and you can find the solutions to the most common problems.

Travis CI:

I had to use a CI tool to distribute my builds, and thus I chose to do a small survey between Jenkins and Travis. Here are some of the advantages Travis has over Jenkins:

  1. Travis has the GitHUB web hook integrated into it! It also provides you with certain nifty features like showing the build status on your pull request, queue management, running the job only on the latest commit and so on. You do have GitHUB plugins on Jenkins, but there is this time which you need to spend on integrating it.
  2. Travis needed no setup. All I had to create was an account and that was it.
  3. Travis is a cloud-based solution, so you don’t need to have a dependency on a system unlike Jenkins.

Anyway, enough of why…. let’s move on to the how!!!

FASTLANE INTEGRATION:

This tool is going to automate our build generation process. We are going to use two of fastlane’s tools:

  1. match — To manage certificates and profiles for different distribution schemes
  2. gym — To create iPAs

Install fastlane on your system

brew cask install fastlane

Go to your project folder and run

fastlane init

This creates a folder called fastlane in your project directory which contains the fastfile where you are going to write your “lanes”.

The lanes follow a pretty simple structure:

lane :build do |options|
scheme = "SchemeName"
if options[:scheme]
scheme = options[:scheme]
end
configuration = options[:configuration]
export_method = options[:export_method]
gym(scheme: scheme, configuration: configuration, export_method:export_method, output_name:"Name.ipa")
end

Something similar to the above one.

You can also have a separate Gymfile and Matchfile for default build generation configurations, but for our purposes, we made the lanes generic enough to get our work done.

Match

Using match is very simple as well. You can use match to do two things:

  1. Create provisioning profiles and certificates for a build option.
  2. Downloading those provisioning profiles and certificates and then signing your build with the downloaded profile and certificate.

You can use match nuke to delete all your existing profiles and certificates, which is what fastlane suggests, <- This is what I had done.

or

you can create a new one using match. Match creates new profiles or repairs existing profiles and commits them to a private GitHUB repository.

So first, create a private repo on GitHUB where you will be storing your profiles and certificates.

Then run

fastlane match init

This will create a matchfile where you can specify the

  1. The URL of the repo created earlier.
  2. The username of the github user, whom match is going to use to download the certificates and profiles.
  3. The bundle identifiers of all the targets in your app in the format given below
["bundle identifier 1", "bundle identifier 2", ...]

Run

fastlane match adhoc

This will generate an adhoc profile and certificate which you will need to sign your application. You can choose from four different options here,appstore, adhoc, enterprise or development

You can also specify a --readonly tag if you want to just download existing certificates and profiles.

If you are running match for the first time depending on the readonly tag, it will either create new ones or download existing ones and commit it to your certificates repo you created earlier. The first time you run it you will have to enter the password associated with the GitHUB username you specified in the matchfile. Now it may strike you that in a CI environment, you won’t be able to enter this password, or even if you could that would mean that someone will have to do it manually… which we hate :).

To overcome this problem we are going to have to add an environment variable with the nameMATCH_PASSWORD.This variable is going to store the password of the username provided in the matchfile. More about this later on in the CI integration.

TRAVIS CI INTEGRATION:

The first step after connecting your github account with travis you need to do in order for travis to run your CI processes is to create a .travis.yml file. This is the file where you are going to specify all the things that need to happen during the continuous integration.

Make sure the travis.yml file starts off like this

language: objective-c
osx_image: xcode8.3

Or whatever image is the one you prefer.

Before we start off I would like to mention, in a cloud-based CI environment you cannot expect to install your certificates on a global keychain which can be used to create the builds. This gives rise to three things:

  1. Manual Code-Signing: Unless it is on your own machine where your keychain is setup, fastlane doesn’t play well with automatic code-signing. So I would suggest having a dummy build configuration which you can create in Xcode. You will be using this configuration to generate your builds.
  2. Setting up Manual Code-Signing Parameters: Now that you’ve created a dummy configuration, make sure that you have setup the development team in all your target’s “build settings”. Set the provisioning profiles and certificates related to this dummy configuration to the ones that match had created. If these steps are not done your build will fail to code-sign in the CI Environment.
  3. Creating a new keychain: Since you cannot use the global keychain, you have to create your own local keychain for the purposes of that build. Otherwise, you may experience issues like
n

So let us get into the nitty-gritty of it.

First things first.

To address the first issue, of automatic code signing, we disable it :) ! We are going to use manual code-signing to build our app. This reason for doing this is practical experience.

With automatic code-signing I had two entities managing the provisioning profiles, one being us (using fastlane) and the other was Xcode. I’ve found it was better if only I had control over things like certificates and provisioning profiles so that I knew exactly when and where the changes were being done to them. Keep in mind that Xcode would regenerate profiles if you toggled between automatic and manual code-signing.

You can always make it work with automatic code-signing, but I felt it was a hassle with automatic code-signing.

Automatic CodeSigning

For the ones who would want to have a go at automatic code-signing, here are the steps:

  1. add this line above your gym call

disable_automatic_code_signing(path: "myproject.xcworkspace")

and this below your gym call

enable_automatic_code_signing(path: "myproject.xcworkspace")

2. Download all the provisioning profiles, from the Xcode->Preferences->Accounts->Download Manual Profiles

3. Go to the Provisioning Profile section in the Build Settings tab in each of your project’s targets, open up the build configuration in which your project is going to get archived(eg: Release or your own custom configuration). Select the provisioning profile you created using match.

Now to address the second issue of creating a keychain, make sure you create a keychain when you’re building on travis before you perform your build operations using

create_keychain(
name: ENV["MATCH_KEYCHAIN_NAME"],
password: ENV["MATCH_KEYCHAIN_PASSWORD"],
default_keychain: true,
unlock: true,
timeout: 3600,
add_to_search_list: true
)

The ENV[] parameters are basically environment variables which can be assigned in the before_all script in the fastfile.

So we are done with the fastlane preparation for travis. Now it is time to learn about some Travis specific things.

Environment Variables

You need these to specify these parameters in here so that your “fastfile” can access these whenever needed. Now, these can be confidential parameters as well (eg. the MATCH_PASSWORD that we talked about earlier). We need to encrypt them before adding them to our .travis.yml file.

Install travis

gem install travis

Then you can run the encrypt command to encrypt your secret environment keys

travis encrypt MY_SECRET_ENV=super_secret --add

PS: Make sure you don’t add quotes before your value until and unless the value actually requires it.

  • Awesome. So now let us add our MATCH_PASSWORD to it using the above command.
travis encrypt MATCH_PASSWORD=password --add

Now another problem that I had to solve is allowing Travis access to our private repositories since our certificates are in a private repo. To solve this, there are four methods suggested by Travis, from which I chose the “API Token” solution.

Generate an access token for your CI GitHUB user. Encrypt it using the

travis encrypt CI_USER_TOKEN=token --add

command and add the following to your before_install part of the travis.yml file.

echo -e "machine github.com\n  login $CI_USER_TOKEN" >> ~/.netrc

Congratulations, you are all done preparing yourself for the journey.

For the next steps, you can write your lanes in the fastfile which creates builds and uploads to whatever hosting service you want to upload to. You can then call these lanes in the script part of your .travis.yml file. eg:

script:
- fastlane create_travis_build
- fastlane upload_to_hosting_service

And there you go! You are done with build automation with Fastlane and Travis CI!

Here are some extra links which you might find helpful

Follow us on twitter for regular updates. If you liked this article, please hit the Hand icon to recommend it. This will help other Medium users find it.

--

--

Neel Bakshi
Practo Engineering

Guy who handles everything mobile @headout among other things! Ex @practo