Last Updated: July 26, 2019
· modocache

CI for Open Source iOS Applications, Part 1: Setup


This guide will walk you through setting up a Jenkins continuous integration server for your open source iOS project. By the end of the guide, you will be able to add an image to your README indicating the build status of your repository's master branch.

Step 1: Provisioning a CI server

Jenkins on the Cloud

In order to provide developers with feedback on the state of the build, you will need a publicly available Jenkins instance. Normally, this would mean hosting an instance on EC2, setting up permissions to prevent anonymous users from messing with configurations, and other hassles. You can avoid these headaches by using a PaaS such as CloudBees (note that I'm not affiliated with the service in any way, other than being a extremely satisfied user), which gets you up and running with Jenkins after a simple registration process. This guide will cover setup using CloudBees.

Free OSS Account

CloudBees Jenkins instances are private by default. In order to get a publicly available one, you'll need to register for something called a FOSS account.

After registering, you can check out your Jenkins instance at {{ your_domain }}

Set up a Slave

The Jenkins instance hosted on CloudBees will show other developers the state of the build, but the actual execution has to be on an OS X machine with Xcode installed. In other words, we'll need a slave to run our test suite. You can set up a slave by following CloudBees' instructions on customer provided slaves.

In summary, you'll have to add your RSA key to your CloudBees settings, then download the jenkins-cli.jar file and use it to connect to your Jenkins instance, like so:

java -jar jenkins-cli.jar -s -i ~/.ssh/id_rsa customer-managed-slave -fsroot ~/.jenkins/cloudbees -labels xcode_4_4 -executors 1 -name modocache-macbookpro-2009

The labels parameter is a tag associated with your machine (more on that in the next section).

Once you have your slave connected, you should be able to confirm it is connected at . After making sure it is connected, continue to the next step.

Get a Job!

The next set of instructions will cover the necessary steps in setting up a job on Jenkins. Readers familiar with Jenkins might find this part a bit boring.

  1. Navigate to your Jenkins dashboard and click New Job from the menu on the right

  2. Enter your job name (preferably the name of the repo) and select the Build a free-style software project option. This option is the most open-ended, and best suits our purposes, as all our job will do is execute a shell script.

  3. After being redirected to your job page, click Configure from the menu on the left.

  4. Enable the Restrict where this project can be run option and set Label Expression to xcode_4_4 (we want to make sure we are testing on the right environment, in this case an environment with Xcode 4.4 installed, which we will label xcode_4_4)

  5. Set Source Code Management to git, and specify the repository URL as the git read-only URL for your repository. Make sure to set Branches to build to the branch you will be adding your test script to. This guide will assume the branch name to be feature/jenkins-ci.

  6. Add a build step of the Execute shell variety, with the value ./Scripts/ $BUILD_NUMBER (we will write this script in the next step)

  7. Enable Archive the artifacts as a post-build action, setting Files to archive to dist/* (our Jenkins script will package app binaries at this location)

Basically, all we need Jenkins to do is run a shell script we will create in our project. The next step is actually writing the script.

Step 2: Automating the Build

The Build Script

Our build script will build and package all schemes defined in the project, then run integration and unit tests. Here is a template you can use as a starting point for creating your own. Make sure you create it at the same path as the one you specified in your Jenkins configuration (in our case, Scripts/ It assumes you are using CocoaPods for dependency management, as well as KIF and Kiwi for your integration and unit tests, so let's set those up.

Integration Tests with KIF

  1. Set up integration tests as a separate application target, exactly as outlined in the documentation.
  2. Make sure the target name specified in the runintegrationtests function of the build script matches the name of your integration test target

Unit Tests with Kiwi

Running iOS unit tests via the command line is made significantly more complicated by the fact that xcodebuild does not take a test argument. Thankfully, Peter Jihoon Kim has written an expertly crafted post on the matter. Follow his instructions and you should be golden. Again, make sure the test target name matches the one being used in rununittests in the build script.

Other Items of Note

xcodebuild can only build schemes that are "shared". You can share schemes via the Manage Schemes tab in Xcode:

The build script creates a build and dist directory in the project root directory. Be sure to add these to your .gitignore.

Step 3: Build, baby, build!

Testing Locally

Test out your automated test suite by entering the root of your project and running ./Scripts/ 0. If everything goes well, you should see a ton of build output, followed by the output from your integration tests, followed by your unit tests.

(Tweet or email me if you run into any issues, preferably with details on how you solved those issues, and I will add them here.)

The script will exit with a status code of 0 if everything goes well, and a non-zero value otherwise. Jenkins will use this value to mark the build as pass/fail.

Executing the Build on a Jenkins Slave

Once everything works locally, push to the branch you specified in your Jenkins configuration. Make the slave you set up is still connected and click Build Now on your project. The build will now execute on your machine.

Jenkins will queue jobs to run on your machine. These will run in order once you or another slave connects. The idea is to have many machines dedicated to this task, in order to make sure tests are run as soon as they are enqueued.

Step 4: Adding the embeddable-build-status Plugin

Jenkins has tons of plugins. Only a subset of these are available on the Free OSS plan on CloudBees, but that subset includes embeddable-build-status. This plugin adds a route to your Jenkins instance, which you can access for an image displaying the current build status.

In order to add this plugin, navigate to Manage Jenkins > Manage Plugins > Available on your Jenkins instance, check the embeddable-build-status plugin, and click Download now and install after restart.

Your Jenkins instance will be unavailable during restart (any connected slaves will be disconnected as well), but after a minute or so you should be able to see a link to Embeddable Build Status in the sidebar on your job page. Add this URL to you project's README on Github, and voila! You now have an image displaying the build status of your project.


You can set up polling or a post-commit hook in order to make sure this status updates every time a change is pushed to your repository, allowing all developers to know immediately when something's funky with the build. Now that's continuous integration!

In Conclusion

I hope you've enjoyed this guide. Let me know if you run into any issues that you cannot solve yourself, or if you'd like to suggest a change to this post.

Also, stay tuned for Part 2: Pimping Out Your CI Server!