Continuous Deployment (CD) is a practice of using automation to release your code in a quick and sustainable way. Here's how you set up CD for iOS apps to perform automated building, code signing and deployment to the App Store.
iOS Continuous Deployment Using Travis CI and Fastlane
Written by
In the last post, we covered how to set up a CI system to perform code validation and run tests on pull requests. This time, we'll go through step-by-step how you set up a CD system for your iOS apps. You're not required to read the first post, but do so if you want to assemble the complete CI/CD setup!
Whenever you're ready, let's launch your iOS apps to the App Store - automated using Travis CI and Fastlane!
1. Initial Travis CI setup 🔧
1.1. Activate repo in Travis CI
So I'm just going to quote the initial setup from the Travis CI docs here:
- Go to Travis-ci.com and Sign up with GitHub.
- Accept the Authorization of Travis CI. You’ll be redirected to GitHub.
- Click the green Activate button, and select the repositories you want to use with Travis CI.
1.2. GitHub access token for Travis CI
We need to provide Travis CI access to our GitHub repositories. Go to GitHub's Personal Access Token in Settings and generate a new access token. You can name it "travis" if you like. Then you need to go to the environment variables section of your repo in Travis CI. It should be under Settings. You then need to create an environment variable called GITHUB_API_TOKEN
and copy/paste the access token into it.
2. Initial Fastlane setup 🔨
2.1. Install RubyGems
If you don't have Bundler installed already on your Mac, do it by running gem install bundler
. If you don't have permissions, you need to have rbenv installed or sudo
it if you're brave.
Navigate to the directory of your project and run bundle init
. That will create a Gemfile for you. Here are the dependencies we need to install, so copy/paste this code into your newly created Gemfile:
source "https://rubygems.org" gem "fastlane" gem "xcode-install"
Now that we've specified the dependencies we need, install them by running bundle install
.
2.2. Create an Apple ID without 2-Factor Authentication (2FA)
To deploy your apps using Fastlane, make sure you have an Apple ID without 2FA enabled. If not, you can create a new Apple ID here. This way, Travis CI won't be required to enter a 2FA code during deployment! Fastlane does provide an alternative where 2FA is supported, but it comes with some limitations. If you're interested, you can learn about it here.
Whenever you've created the Apple ID, add it as a user in your App Store Connect, and you should be ready to continue!
2.3. Fastlane init
To get started with Fastlane, navigate to your project using the terminal and run fastlane init
and select "Manual setup" when prompted. That will create a fastlane folder as well as a Fastfile and an Appfile. Now, just open the Appfile and provide the parameters app_identier
and apple_id
:
app_identifier "com.appid.example" apple_id "appleid@example.com"
You should use your newly created Apple ID without 2FA as apple_id
.
3. Configure Fastlane match 🔏
Here's what will allow us to perform code signing from Travis CI easily! Fastlane match is a tool for generating all necessary certificates and provisioning profiles and storing them in a git repository. This way, anyone with access to the repo, such as Travis CI, will be able to fetch these credentials from the repo and perform code signing of our apps, so they can go on the App Store!
3.1. Create empty GitHub repository
This is required by Fastlane match. You can create a new GitHub repository here. Name it "ios-certificates" and make it private. That's it. Now you just let Fastlane match do the changes to it.
3.2. Fastlane match init
Open your terminal, navigate to your project and run fastlane match init
. Follow the guide by providing the URL to your empty git repo. Afterward, open the newly created Matchfile in the fastlane folder. Provide the URL to the git repo you created:
git_url("https://github.com/andreaslydemann/ios-certificates.git")
This will let Fastlane match know where to fetch the certificates and profiles from next time.
3.3. Generate profiles and certificates
You can now run the following to generate new certificates and provisioning profiles:
- First, run
fastlane match development
. That should generate the certificate and profile used by our debug builds. - Afterward, run
fastlane match appstore
to create the certificate and profile used by our release builds.
3.4. Select provisioning profiles in Xcode
The newly created certificates and profiles should now be possible to select inside our project. Open up Xcode and go to Signing & Capabilities. Here, select the appropriate provisioning profiles like this:
3.5. Fastlane environment variables
You need to create these two additional environment variables in Travis CI:
FASTLANE_PASSWORD
is the password to your newly created Apple ID without 2FA.MATCH_PASSWORD
is the passphrase you created for your certificates/profiles during the Fastlane match setup.
4. Prepare pipeline ⏭
4.1. Travis CI configuration
Create a file in the root of your project and name it ".travis.yml". This is where we will define all the jobs that we want to run. Copy/paste the following code into the .travis.yml file:
# environment setup language: swift osx_image: xcode11.1 # caching of dependencies cache: bundler: true directories: - Carthage # branch safelist branches: only: - master - /^release\/.*$/ # prepare dependency managers before_install: - brew update - brew outdated carthage || brew upgrade carthage - gem install bundler -v "$(grep -A 1 "BUNDLED WITH" Gemfile.lock | tail -n 1)" # install dependencies install: - travis_wait carthage bootstrap --platform iOS --cache-builds - bundle install # jobs and stages to run jobs: include: - stage: Deploy name: Deploy to App Store script: - echo "machine github.com login $GITHUB_API_TOKEN" >> ~/.netrc - bundle exec fastlane deploy_to_app_store if: branch =~ /^release\/.*$/
The deployment stage is what's important here. Here are the key takeaways from it:
- The deployment stage is only triggered on commits to branches that are prefixed with "release/". This means that we perform a deployment to the App Store by creating a remote branch called "release/x.x.x".
- The deployment stage contains a script that will run two commands:
- The command
echo "machine github.com login $GITHUB_API_TOKEN" >> ~/.netrc
gives Travis CI access to the private GitHub repo containing the necessary certificates and profiles. - The command
bundle exec fastlane deploy_to_app_store
triggers the lane in our Fastfile that is responsible for deploying the app to the App Store.
- The command
if you're not familiar with the concepts of pipelines in Travis CI, I recommend that you read about the job lifecycle and build stages from the Travis CI docs.
4.2. Branch protection
We also want our release branches to be protected so future commits into it must be submitted as pull requests. To do this, go to your project settings in GitHub and select "Branches". Here, you create a branch protection rule with the following settings:
5. Prepare deployment 🚀
5.1. Fastfile lanes
In your Fastfile, you'll need a lane for performing the deployment and one for installing certificates. You can copy/paste the following code into your Fastfile, and you should be good:
default_platform(:ios) before_all do xcversion(version: "11.1") end setup_travis def version_number if ENV["TRAVIS_BRANCH"].match?(/^release\/.*$/) return ENV["TRAVIS_BRANCH"].split("/").last else return get_version_number end end platform :ios do desc "Installs the certificates locally" private_lane :install_certificates do tmp_keychain = "temporary_keychain" create_keychain( name: tmp_keychain, password: tmp_keychain, default_keychain: true, timeout: 3600, unlock: true, ) match( type: "appstore", keychain_name: tmp_keychain, keychain_password: tmp_keychain, readonly: true ) end desc 'Deploys QCards to App Store' lane :deploy_to_app_store do install_certificates increment_version_number(version_number: version_number) increment_build_number(build_number: latest_testflight_build_number + 1) gym( scheme: "QCards", clean: true ) deliver( submit_for_review: true, reject_if_possible: true, skip_screenshots: true, skip_metadata: true, force: true ) end end
The lane deploy_to_app_store
is called from our deployment stage in travis.yml. This lane will, in turn, call the install_certificates
lane which will fetch the code signing credentials using Fastlane match and store them in a temporary keychain. Afterward, we're ready to perform the code signing, whenever we build the app as an .ipa file using gym
. Lastly, deliver
will send the .ipa file to App Store Connect and submit it for review.
You need to manually prepare the app version with screenshots, release notes etc. in App Store Connect before you deploy your app with Fastlane - otherwise, Fastlane will report back that the app version wasn't found and the job will fail.
5.2. Enable Apple Generic versioning system
Since Fastlane will increment the build numbers for us, it's required that we change the versioning system to "Apple Generic". Look for versioning in the build settings and change it like this:
Results ✅
Now you should be all set with the setup. If you've come this far, you should be able to create a remote branch prefixed with "release/" and see the pipeline go to work. If everything is successful, you should see a screen similar to this in Travis CI.
There we go! This setup will save you a lot of time in the future. You can find the source code for the complete setup on GitHub.
Thanks for following along!
Share this post
Facebook
Twitter
LinkedIn
Reddit
You may also like
Automation
Automate Screenshot Framing with Text Using Fastlane
Framing your App Store screenshots greatly improves your store listing. But doing it manually takes hours away from perfecting your app. Instead, here’s how you automate the process of adding device frames and text to your screenshots.
Swift
Transforming Operators in RxSwift: map, flatMap & flatMapLatest
Transforming operators in RxSwift serve the purpose of preparing data coming from observables before it reaches the subscribers. Let’s cover this type of operators and look at 3 operators you will be using all the time.
DevOps
3 EAS Pipelines for Deploying React Native Apps in Teams
One of the best ways to increase productivity in a react native project is to automate the process of deploying builds you want to share internally in your team or with actual costumers via the app store. And you might be surprised at how easy it can actually be implemented when you have the right set of tools like GitHub Actions and EAS.