Automating the tedious parts of your life as an iOS developer by mixing GitLab CI and fastlane like a boss
That may lead to the increase of one of the following: productivity, caffeine, free time, friends at work.
This post serves as a continuation of How to set up GitLab CI for iOS in three relatively simple steps and assumes that you have GitLab CI already running for a a project of your choosing.
Introduction
In my previous post, I showed how some people use GitLab CI for iOS and I showed how I implemented it myself. But guess what? You can take things a bit further and improve your nice set-up with the goal of automating all the tedious tasks connected with building and releasing your app. That is right, it is fastlane that I have in mind(you should know that if you read the title 😃). So, keep reading till the end and I promise that running unit tests and uploading a new version to TestFlight for your project will feel like a breeze 🌬.
How did we start using fastlane at work?
Ok, look, I work at a small start-up company with a workforce of a bit over 10 people and I am part of the iOS team. I will not go into details of how we organize the workflow and etc., I will just say that we are in charge of all of it. So, yeah, when we start out building a new app, we do it all — planning, development, betas, production versions, releasing, all the buzzwords you can think of 🙏. Do not understand me wrong — I like things this way, it keeps my developer’s live way more interesting.
But what is it that is not so interesting about my way more interesting developer’s life? 🐼🔫 There might be other things that come to mind, buuuut I am talking about deploying an app to the appstore— dealing with provisioning profiles(sometimes on different machines), uploading different versions(forgetting to increment the build number), receiving random weird errors. Is any of this actual programming? Doubt it. And, hey, we are not even considering the time wasted(time is money they say 💸).
And voilà 🏄, what a satisfying feeling when fastlane is all set and working:
So, how to set up fastlane to work with GitLab CI for iOS deployment and feel that satisfying feeling?
First of all, you need to install the required tools for using fastlane — Xcode command line tools and fastlane itself obviously. You can do that following these instructions where providing your Apple ID will be the most important thing to start with, I am sure you know that one 😃. fastlane then automatically does its magic and in your project’s directory you should see a fastlane folder containing all your metadata from iTunes Connect. I recommend starting with paying special attention to:
- the Appfile(your app and your account info is here).
- the Fastfile(your instructive lanes are here, a.k.a. the key to lazy productivity).
- the rest of the metadata varies depending on whether you had your app on iTunes Connect before or after you started using fastlane.
If you are now wondering what exactly a Fastfile consists of? Plainly said, it is Ruby code. But if you are the more curious, this and this should clarify things further.
And that is it, fastlane is ready to be used 🎊. Only locally though, meaning the command line 😢. That may be enough if you are a freelancer and you work on your own, or you are the one-person iOS team in your company. But most of the time, that is not the case. You want to have things ready once and you want the configuration to be used by everyone in your iOS team. Luckily, you read my previous post and you have GitLab CI waiting to be mixed with fastlane and thus creating a beautiful cocktail 🍹.
Ideally, you would want your CI set-up to run on a separate server that is available 24/7.
If you read the fastlane installation instructions closely, at the bottom it is mentioned that you should use a Gemfile to easily define your dependency on the latest stable version of fastlane. So, yeah, keep reading and make sure you have bundler installed, and that pretty Gemfile positioned in the root directory of your project containing this:
source "https://rubygems.org"gem "fastlane"
If you use Cocoapods in your project, well, add a line for them to the file too(last time we put the Cocoapods installation instructions in the GitLab YAML file, now we put them in the Gemfile):
gem "cocoapods"
This should be enough of configuration for your Gemfile at this point.
Next comes the actual setting up of the collaboration between GitLab CI(the .gitlab-ci.yml file) to work with bundler(the Gemfile) and fastlane and this is the result we are looking for:
- GitLab CI starts out by first calling bundler which installs fastlane and the needed Cocoapods.
- GitLab CI then starts executing its pre-set stages as usual. This time however, these stages contain calls to the respectively configured lanes in the Fastfile. In other words, fastlane will be the tool to direct the CI execution.
- fastlane does its thing according to the instructions provided — it is the same as using fastlane through the command line but automated with the help of GitLab CI.
Alright then, we have the Gemfile ready, we need the other two. Let me show you what I have for a YAML file:
stages:
- test
- deploy
before_script:
- gem install bundler
- bundle install
unit_tests:
stage: test
script:
- fastlane test
tags:
- ios
deploy_beta:
stage: deploy
script:
- fastlane beta
tags:
- ios
only:
- beta
Starting with stages, there are obviously two that I have in mind — one for testing and one for deploying(a new version). Before the actual script runs, I call the installation of bundler itself with gem install bundler and with bundle install I call the instructions in the Gemfile. GitLab CI should now be ready to run the unit_tests job for every branch in the project(we would generally want to run the unit tests with every important commit, right?) and the deploy_beta job for the beta branch only(we merge with beta branch every time we want a new beta version to be uploaded to TestFlight; so yeah, it is assumed you have a beta branch too, and if you don't, make one 😃).
Lets go back to the Fastfile and set up the test and beta lanes. The test lane is quite straightforward:
lane :test do
scan(scheme: "YourAppSchemeName")
end
The beta lane is a bit more sophisticated but nothing too crazy:
lane :beta do
increment_build_number ({
build_number: latest_testflight_build_number + 1
}) match(app_identifier: "YourAppIdentifier",
type: "appstore",
force: true
) gym(scheme: "YourAppSchemeName",
silent: true,
clean: true)
pilot
end
So, one by one:
- increment_build_number and the corresponding in-bracket calculation always make sure that you properly implement the build number of the project that you upload to TestFlight.
- match is one of my favorites cause it makes sure that you always have all required certificates and provisioning profiles shared in a git repo for all team members to use. However, it needs special preparation so you need to read a bit about it here. If you are still wondering about the configuration I have — type is for type of codesigning identity you are using and force: true makes sure that your app’s provisioning profile is always up-to-date.
- gym makes sure that we get a nice, clean(fresh installation) and silent(only readable info is showed to us on the command line) project build before we try uploading a new version.
- pilot is for uploading to TestFlight.
And that should do it provided that you followed all the instructions closely. Go ahead, make a commit to any branch to invoke some unit testing and when you are ready, merge or commit to the beta branch to start an upload of a new beta to TestFlight.
Well, thank you for reading, I hope you enjoyed it. If you have some advice, positive or negative, please do comment. And some 👏 👏 👏 would be nice if you feel like it 😉. Till next time!