Weekly Swift articles, podcasts and tips by John Sundell.

Adding Continuous Integration to a Swift project

Published on 07 May 2017

Continuous Integration (or CI for short) is one of those buzz words that have become really popular lately. It’s the idea that instead of having long-lived feature branches that you use to work on a new feature, you continuously integrate your new code into the project’s main branch (which is usually master or develop if you work with Git). Every time you do so you build the project and run its tests, to make sure everything is in order before merging the change in.

This week, let’s take a look at how we can use CI to establish a smooth and nice workflow for our team working on a Swift project.

CI is done on a server that is separate from any team member’s working machine. This adds a layer of “quality assurance” (no more “It works on my machine!” arguments) and makes sure that the project builds on a “clean” setup — which has the added benefit of making on-boarding new team members easier.

The most common options for CI solutions

These are the most commonly used solutions for running a CI server that’s used for Swift projects today:

Picking the right solution

So, which solution is the best one? Like most tech decisions, it comes down to applying a healthy mix between requirements and personal taste.

The good news is that almost all of the above mentioned solutions have a free tier, so before fully investing in any given solution — you can always try a few of them out to see which one fits your project best.

But, I do want to provide a quick guide to setting some of these solutions up - so for the purpose of this post I’m going to focus on Travis and Bitrise. They are interesting to compare, because they take very different approaches, and are easy enough to setup that I can recommend them to anyone, irregardless of how much you like to tinker with these type of things.

So, let’s dive in! 🚀

Travis

Like mentioned above, Travis essentially gives you a command line to run your build tools on. Configuration is done through a .travis.yml file that you put in your project’s repository, and once you sign up for Travis and activate it for your project, it will automatically pick up any such .yml file and run your CI based on it.

Here’s an example of a simple .travis.yml file that uses xcodebuild (which is the command line interface to Xcode’s build system) to build and test an iOS project:

language: objective-c
osx_image: xcode9
script:
    - xcodebuild clean test -project MyApp.xcodeproj -scheme MyApp -destination "platform=iOS Simulator,name=iPhone 7,OS=10.3" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ONLY_ACTIVE_ARCH=NO -quiet

The above command is a bit of a mouthful, so let’s break it down:

There are of course alternatives to using xcodebuild directly, like using xctool or fastlane (both of which come pre-installed on any Travis macOS VM). In the case of fastlane you can simply execute a given lane that you have defined in your Fastfile, making your .travis.yml look something like this:

language: objective-c
osx_image: xcode9
script:
    - fastlane MyTestLane

Even though Travis requires you to set up things manually, and it can be a bit unstable and slow sometimes (especially for open source projects, but hey, it’s free 😅), I really like it. It is widely used in the community, which means that there’s tons of help to be found in case you can’t figure something out.

Bitrise

Now, let’s take a look at Bitrise, which I should disclose is a frequent sponsor of Swift by Sundell. However, I’ve personally used Bitrise since long before they became a sponsor, and this article wasn’t paid for by them in any way.

Like mentioned in the overview above, Bitrise can automatically infer recommended build settings when adding a new project, rather than requiring you to set up configuration files in your repo. Most developers (including myself) will probably be very skeptical to this approach at first, we know how hard inferring things like this correctly can be, so you might think that it’ll be extremely flaky and error prone.

But it turns out, Bitrise works amazingly well. From my own personal experience with the platform, Bitrise is the most stable CI platform that I’ve used so far.

For iOS apps, there’s not much of a tutorial needed in order to get started with Bitrise. Their UI will walk you through setting things up and will ask you for any information required. It “just works”. But, sometimes you do need/want to dive in and customize things with your own build scripts. When I set up Splash with Bitrise, I did find myself in need of doing such customization, since it uses the Swift Package Manager as its build system.

So, here’s how to easily set up a Swift Package Manager-based project using Bitrise. Within the workflow editor for the project in question, simply add a Do anything with Script step, with the following content:

#!/usr/bin/env bash
# fail if any commands fails
set -e
# debug log
set -x

swift test

And that’s it! 🎉

Conclusion

As you can probably tell from reading the above, I’ve become quite a big fan of Bitrise. I especially like how easy it is to setup and manage, especially when dealing with many projects like I do — both for commercial projects and when doing open source. Eventually I’ll probably move all of my projects over to it, but for now I’m also using Travis quite a lot and am quite happy with that too.

But I do encourage you to find out for yourself. Try, experiment, and share your findings! 👩‍🔬👨‍🔬 I’m not using CI for all of my projects, but for any that at least has more people than just me working on it, I definitely think it’s worth the effort setting it up. Having that additional check to make sure that your project remains in a buildable and testable state is super valuable, especially when working in a team.

Do you have any favorite way of doing CI that I didn’t mention in this post, or any other questions, comments or feedback? I’d love to hear from you! 👍 Feel free to contact me on Twitter or email.

Thanks for reading! 🚀