WWDC24 SALE: Save 50% on all my Swift books and bundles! >>

How to validate code changes using CircleCI

Run your tests for every commit and pull request

Paul Hudson       @twostraws

Part 6 in a series of tutorials on modern app infrastructure:

  1. How to refactor your code to add tests
  2. How to add CocoaPods to your project
  3. How to clean up your code formatting with SwiftLint
  4. How to streamline your development with Fastlane
  5. How to save and share your work with GitHub
  6. How to validate code changes using CircleCI

Continuous integration (CI for short) is the process of merging your code with a central repository on a regular basis, which might sound good but has pitfalls. For example, if you have multiple team members committing to your GitHub repository several times a day, what happens if someone breaks the master branch? Or what happens if someone writes code that causes a test to fail?

In this final installment of our mini-series on modern app infrastructure, I want to introduce you to automated testing using CircleCI. This is a company that hosts its own macOS servers running various Xcode builds, and can connect directly to your GitHub repository so that every time you make changes they will automatically run your full test suite to double check that all your changes are good.

Even better, as you’ll see you can even have CircleCI run its tests on pull requests to your repository, so that no one – not even you, if you want – can get code into the repository unless your tests pass. The result is that your team can commit code more often and with more confidence.

Now, I know what you’re thinking: CircleCI is a business and they need to make money, so how much does this service cost. Well, it turns out that if your code is open source they’ll waive hosting costs for their beginner plan – you can get all the benefits of CircleCI’s automation without paying a penny.

So, it helps you write safer code, and it doesn’t code anything for open source projects. Best of all, though, is the fact that it takes only a few minutes to set up – seriously, you should give it a try if only to see what real project automation can look like.

  • Note: For the avoidance of doubt, this isn't a sponsored post. I'm writing about CircleCI because that's what I know and because it's free for open-source projects – I'm sure there are lots of other great continuous integration services out there.
Save 50% in my WWDC sale.

SAVE 50% To celebrate WWDC24, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Setting up CircleCI

The first thing to do is head to https://circleci.com and create an account if you don’t have one already.

Note: If you create your account using your GitHub account, it will automatically configure both GitHub and CircleCI so they can communicate. This drastically simplifies the process, so I suggest you create it this way.

When you’ve logged into CircleCI using GitHub, you’ll see a message like this: “No projects building on CircleCI. Skip to set up new projects” – look for the Add Projects button on the right, and click that.

If you’ve connected CircleCI to GitHub correctly, you should see a list of all your GitHub projects. Look for Paraphrase-Improved in the list of projects (or whatever you called your repository), then click Set Up Project.

Now, even though CircleCI has a free tier for open source projects, you need to email their team to ask them to enable this for you. Seriously, just write an email to billing@circleci.com giving them your username on GitHub and your repository URL, and they’ll set you up with free access. Alternatively, try tweeting @circleci with the same information.

In the meantime, I suggest you select one of their paid-for plans – choose the Growth plan for macOS, because it comes with a two-week free trial. Don’t worry: you won’t be asked for credit card information, so there’s no chance of you being charged by accident. Selecting the free trial lets you continue while the CircleCI team validate your email request.

Once you have an active plan, the CircleCI site will give you a set of instructions to follow. Here’s the simplified form:

  1. Make a new directory in your project called .circleci. You can do this by running mkdir .circleci.
  2. Now run touch .circleci/config.yml to create a configuration file in there.
  3. Run open .circleci/config.yml to open config.yml in a text editor – this is the configuration file that CircleCI reads when working with your project.
  4. Click the Copy To Clipboard button in the CircleCI instructions. This will copy their default configuration data to your Mac’s clipboard, and you should paste that into your config.yml file.
  5. Change their Xcode version to be 9.3.0. I had 8.3.3 in my default configuration file, which is old.
  6. Change the SCAN_DEVICE line to “iPhone 8”, then change SCAN_SCHEME to be “Paraphrase”.
  7. Save that file.
  8. Run git add . from your terminal, to add that configuration file to Git’s staging area.
  9. Now run git commit -m “Adding CircleCI” then git push to commit that change and push it up to GitHub.
  10. Click the Start Building button in CircleCI’s user interface.

At this point CircleCI takes over: it will check out your source code, fetch the CocoaPods you need, build the whole thing, then run your tests and report back how things went. Even better, if there are any failures you’ll automatically get an email showing you what went wrong.

Because CircleCI likes to work with a clean environment for every test, that means it needs to fetch the CocoaPods master spec repository every time a test is run. As you saw in part two this can take a long time, so it’s likely to take about 8 minutes for CircleCI to finish testing our app.

Because CircleCI is now watching our project, every new commit that happens in the future will automatically trigger a new build. If you want to test this for yourself, wait until the previous test finished then try pushing up a minor change – maybe just add a line to README.md, for example. As soon as it’s pushed, you’ll see your CircleCI dashboard update to say another test is running. Perfect!

Securing our code

At this point, CircleCI has run an initial test on our app to make sure everything is working, and if you pushed up another small change then that will be have been tested too – CircleCI is automatically checking that all changes don’t break our tests.

Although this is an improvement, it’s not ideal: we’re doing reactive checking of changes, rather than proactive – we’re waiting for things to break, and it’s preferable to avoid that entirely where possible.

To fix this we’re going to lock down the master branch of our repository so that no one can commit directly to it. Instead, we’re going to force everyone, including ourselves, to commit their code to a branch that CircleCI can then check before it gets merged into master.

This is so incredibly easy to do that I think you’re going to want to switch it on immediately and keep it around – it’s the kind of simple change that will really help take your software development process to the next level.

Here’s how to set it up:

  1. On your GitHub repository’s homepage, go to the Settings tab then look for Branches.
  2. You should see the heading Branch Protection Rules, and below that a Choose A Branch button – please click that button now.
  3. You’ll see a list of branches in your repository; please select Master.
  4. Check the box marked “Protect this branch”
  5. Also check “Require status checks to pass before merging”, then check “ci/circleci” in the list of checks.
  6. If you want, you can also check the box “Include administrators” – this will apply the rules to you as well. You can change this later, but for now please check this box.

Click Save Changes, and enter your password to confirm your new settings.

We’ve just told GitHub that no one – including ourselves – should be allowed to push changes directly to the master branch of our project.

Try it now – try changing README.md again, adding it to Git’s staging area, then using git push to send it up to GitHub. You should get errors back saying “remote: error: GH006: Protected branch update failed for refs/heads/master” and “remote: error: Required status check "ci/circleci" is expected” – that’s GitHub enforcing the rules we just set.

Instead we need to push our change to a remote branch, using a slightly different command:

git push origin master:new_readme 

That pushes our local master branch up to the remote “new_readme” branch – creating it as necessary.

Circle will detect the new branch and immediately start checking our change. If you refresh your GitHub repository page you’ll see “Your recently pushed branches” followed by the “new_readme” branch you just pushed.

At the right side of that message you’ll see “Compare & pull request”, which starts the process of merging that branch with master. You should go ahead and add information here telling your team what changed and why, then click Create Pull Request.

Now, normally you’d see a green button saying “Merge Pull Request” that handles the process of merging the branch you created with the master branch, but it’s now going to be disabled – GitHub is refusing to let you merge the pull request because CircleCI’s tests haven’t finished yet!

This is exactly what we want: no one can break the master branch of our repository any more because CircleCI is actively guarding against it. If their build fails, or if any tests fail, CircleCI will refuse to let us merge.

In this case, our change was trivial, so after a few minutes CircleCI will report a green light back to GitHub, and the Merge Pull Request button will automatically become active. Go ahead and click that, then Confirm Merge, to have the whole process take place for us. When it completes, a new Delete Branch button will appear because our temporary branch is no longer needed – click that one too.

At this point CircleCI is automatically running our tests and letting us know if any fail, and it’s also automatically verifying pull requests so no one can break our build by accident – this is a huge step forward for our little project.

However, before we’re done I’d like you to make one small change. If you followed my advice when we were setting up CocoaPods, you checked your Pods directory into source code. Right now CircleCI takes seven or eight minutes to complete its tests because it has to download and install the master spec repository from CocoaPods, but because our project already has its Pods directory in the Git repository we don’t actually need that to happen.

So, open your config.yml file and look for this commands:

# Install CocoaPods
- run:
    name: Install CocoaPods
    command: pod install

Those are the ones that trigger the massive CocoaPods download, so go ahead and add a # symbol before each of them – that will comment them out so that they don’t happen. Once you commit that change to GitHub, your CircleCI test times will go down from about 8 minutes to about 1 minute – a huge improvement!

Where next?

This was the last installment of a mini-series on upgrading your app for modern iOS infrastructure. In this article you learned how a continuous integration service like CircleCI can help you ensure your tests are being run regularly, and also protect your code from accidental problems – this draws up on the testing code we did in part one, the Fastlane work we did in part four, and the GitHub work we did in part five, all at once.

This app is still a long way from perfect, but it’s significantly better now than it was at the start of the series. Hopefully you’ll be able to apply these same techniques to your projects and enjoy the same benefits – good luck!

Save 50% in my WWDC sale.

SAVE 50% To celebrate WWDC24, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 5.0/5

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.