DEV Community

Amarildo Lucas
Amarildo Lucas

Posted on

Continuous Integration and Continuous Delivery with SemaphoreCI and Fastlane for iOS apps

When you develop mobile apps, you manage the process of the app publication manually, using Xcode Organizer, after following some steps like signing, test, build, archive and deploy your build, then change versions and send new builds again and again to the AppStore.

If you generate your builds daily, this process is tedious and tiring. So, soon or later, you will ask yourself: — How could I automate all this proccess? The answer to this question becomes essential to your development and deploy flow, it's essential to have a quick feedback loop to iterate when deploying your builds.

Using SemaphoreCI and Fastlane it's a great choice to build products at high velocity. Whenever you push your code to your remote repository, SemaphoreCI will automatically run your custom build, test and deploy pipeline.

So, let’s start playing with some configuration and set up our first continuous integration flow.

Introducing the project

The steps that we will follow in this tutorial to use our Continuous Integration pipeline is going to look like this:

1. Clone SemaphoreCI Swift demo project from Github
2. Configuring SemaphoreCI pipeline
3. Understanding the configuration of the files in our project
4. Push our first build with Continuous Integration

Clone SemaphoreCI Swift demo project from Github

In order to continue with this tutorial, you should grab a copy of the SemaphoreCI demo project from GitHub and clone it to your computer.

We will use along the tutorial the following tools:
1. Git
2. Xcode
3. Fastlane
4. SemaphoreCI

git clone git@github.com:semaphoreci-demos/semaphore-demo-ios-swift-xcode.git
Enter fullscreen mode Exit fullscreen mode

Note: I recommend to you, to fork the project and clone from your forked repo.

Configuring SemaphoreCI pipeline

Go to your SemaphoreCI dashboard and create a new project. Select a repository semaphore-demo-ios-swift-xcodeand connect to SemaphoreCI.

At this moment, you will see an empty connected project. This is because we didn’t push anything to the project yet.

On your cloned project, open .semaphore/semaphore.yml file. This is the configuration file to run your pipeline every time that you push your code. The file is well commented and you need to change it according to your own necessities.

.semaphore/semaphore.yml

# Use the latest stable version of Semaphore 2.0 YML syntax:
version: v1.0

# Name your pipeline. In case you connect multiple pipelines with promotions,
# the name will help you differentiate between, for example, a CI build phase
# and delivery phases.
name: Semaphore iOS Swift example with Fastlane

# An agent defines the environment in which your code runs.
# It is a combination of one of available machine types and operating
# system images.
# See https://docs.semaphoreci.com/article/20-machine-types
# and https://docs.semaphoreci.com/article/120-macos-mojave-image
agent:
  machine:
    type: a1-standard-4
    os_image: macos-mojave

# Blocks are the heart of a pipeline and are executed sequentially.
# Each block has a task that defines one or more jobs. Jobs define the
# commands to execute.
# See https://docs.semaphoreci.com/article/62-concepts
blocks:
  - name: Run tests
    task:
      # Set environment variables that your project requires.
      # See https://docs.semaphoreci.com/article/66-environment-variables-and-secrets
      env_vars:
        - name: LANG
          value: en_US.UTF-8
      prologue:
        commands:
          # Download source code from GitHub:
          - checkout

          # Restore dependencies from cache. This command will not fail in
          # case of a cache miss. In case of a cache hit, bundle  install will
          # complete in about a second.
          # For more info on caching, see https://docs.semaphoreci.com/article/68-caching-dependencies
          - cache restore gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock),gems-$SEMAPHORE_GIT_BRANCH-,gems-master-
          - bundle install --path vendor/bundle
          - cache store gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock) vendor/bundle
      jobs:
        - name: Fastlane test
          commands:
            # Select an Xcode version, for available versions
            # See https://docs.semaphoreci.com/article/120-macos-mojave-image
            - bundle exec xcversion select 10.3
            # Run tests of iOS and Mac app on a simulator or connected device
            # See https://docs.fastlane.tools/actions/scan/
            - bundle exec fastlane test

  - name: Build app
    task:
      env_vars:
        - name: LANG
          value: en_US.UTF-8
      prologue:
        commands:
          - checkout
          - cache restore gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock),gems-$SEMAPHORE_GIT_BRANCH-,gems-master-
          - bundle install --path vendor/bundle
          - cache store gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock) vendor/bundle
      jobs:
        - name: Fastlane build
          commands:
            - bundle exec xcversion select 10.3
            # Gym builds and packages iOS apps.
            # See https://docs.fastlane.tools/actions/build_app/
            - bundle exec fastlane release

Enter fullscreen mode Exit fullscreen mode

If you try to push your code at this moment, your pipeline wouldn’t work yet and will fail. You need to configure your keys and secrets to allow SemaphoreCI to integrate with your project.

To add a deploy key to your Semaphore project you will need to create secrets to add to your pipeline configuration file. So, let's store the deploy key as a secret file in the SemaphoreCI environment.

If you have not installed the sem command-line tool that we are using in this tutorial, this is a good time to install it, see the sem reference and follow the instructions.

$ sem create secret your-fastlane-ios-certificates-repo -f id_rsa_semaphoreci:/Users/semaphore/.keys/your-fastlane-ios-certificates-repo
Enter fullscreen mode Exit fullscreen mode

You will see the following message on the terminal, Secret ‘fastlane-ios-certificates-repo’ created.. This means that everything worked as expected.

id_rsa_semaphoreci it's your ssh file for SemaphoreCI. Upload it to GitHub so you can deploy your code without further problems.

In our case:

ssh-keygen -t rsa -f id_rsa_semaphoreci
cd ~/.ssh/id_rsa_semaphoreci.pub
Enter fullscreen mode Exit fullscreen mode

Copy and paste to Github SSH keys configuration.

Next, add the URL for the certificates repository and the encryption password as environment variables that will be accessible in SemaphoreCI. I recommend also adding the App Store developer account’s credentials to the same secret.

$ sem create secret fastlane-env \
  -e MATCH_GIT_URL=“<your ssh git url>” \
  -e MATCH_PASSWORD=“<password for decryption>” \
  -e FASTLANE_USER=“<App Store developer’s Apple ID>” \
  -e FASTLANE_PASSWORD=“<App Store developer’s password>”
Enter fullscreen mode Exit fullscreen mode

After run this command, you will find your keys in configuration secrets inside SemaphoreCI dashboard.

Updates your .semaphore/semaphore.yml file with your keys and secrets.

jobs:
        - name: Fastlane build
        commands:
            - chmod 0600 ~/.keys/*
            - ssh-add ~/.keys/*
            - bundle exec fastlane release
secrets:
        - name: fastlane-env
        - name: fastlane-ios-certificates-repo
Enter fullscreen mode Exit fullscreen mode

Understanding the Fastlane Configuration

When you open the demo project folder, you will see that you have a fastlane folder with some files inside. This folder was added after we installed and run fastlane for the first time. To understand more about the process of fastlane installation and configuration you should read this introductory article.

Let's go with the steps that usually you will take.

1. Install fastlane
2. Configure fastlane files
3. Test if everything is working

As fastlane describes in their own website:

Fastlane is the easiest way to automate beta deployments and releases for your iOS apps. It handles all tedious tasks, like generating screenshots, dealing with code signing, and releasing your application.

It also supports other platforms.

Install the latest Xcode command line tools if you haven’t installed yet.

xcode-select —install
Enter fullscreen mode Exit fullscreen mode

Install fastlane.

# Using RubyGems
sudo gem install fastlane -NV

# Alternatively using Homebrew
brew cask install fastlane
Enter fullscreen mode Exit fullscreen mode

After Fastlane installation, run the following command.

fastlane init
Enter fullscreen mode Exit fullscreen mode

Fastlane will automatically detect your project and ask for any missing information. In our case, these folders and files installed by fastlane exists as we downloaded from the demo project in Github. Depending on what kind of setup you choose, different files will be set up for you out of the box.

The most interesting file is fastlane/Fastfile, which contains all the information that is needed to distribute your app.

Our fastlane current folder. Again, the files are well commented, so I will not give further explanations about each of them.

fastlane/Appfile

# For more information about the Appfile, see:
#     https://docs.fastlane.tools/advanced/#appfile

app_identifier "***" # The bundle identifier of your app
apple_id "***" # Your Apple email address
team_id "***" # Developer Portal Team ID
Enter fullscreen mode Exit fullscreen mode

fastlane/Fastfile

# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
#     https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
#     https://docs.fastlane.tools/plugins/available-plugins
#

# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane

default_platform(:ios)

platform :ios do

  before_all do
    # installed via the semaphore plugin with `fastlane add_plugin semaphore`
    setup_semaphore
  end

  desc "Run Tests"
  lane :test do
    scan
  end

  desc "Deploy a new version to the App Store"
  lane :release do
    match(type: "appstore")
    gym(export_method: "app-store") # Build your app - more options available
    deliver(force: true)
  end
end

Enter fullscreen mode Exit fullscreen mode

fastlane/Gymfile

# For more information about this configuration visit
# https://github.com/fastlane/fastlane/tree/master/gym#gymfile
#
# In general, you can use the options available
# fastlane gym --help

# Remove the # in front of the line to enable the option

scheme "HelloWorld"

sdk "iphoneos12.4"

output_directory "./"
Enter fullscreen mode Exit fullscreen mode

fastlane/Matchfile

git_url("https://github.com/amarildolucas/fastlane_certficates")

storage_mode("git")

type("appstore") # The default type, can be: appstore, adhoc, enterprise or development

# app_identifier(["com.amarildolucas.com"])
# username("***") # Your Apple Developer Portal username

# For all available options run `fastlane match --help`
# Remove the # in the beginning of the line to enable the other options

# The docs are available on https://docs.fastlane.tools/actions/match
Enter fullscreen mode Exit fullscreen mode

fastlane/Pluginfile

# Autogenerated by fastlane
#
# Ensure this file is checked in to source control!

gem 'fastlane-plugin-semaphore'

Enter fullscreen mode Exit fullscreen mode

This is everything we need to set up and deploy our first build with continuous integration. The code is very specific and self-explanatory. You just defined 2 different lanes, one for your test deployment, one for App Store release. To release your app in the App Store, all you have to do now is push your code.

Note: Ensure that Xcode "automatically manage signing" is unchecked in Xcode. And that your Signing (Release) contains the match AppStore provisioning profile selected.

Some considerations

Gemfile

It is recommended that you use a Gemfile to define your dependency on fastlane. This will clearly define the used fastlane version, and its dependencies, and will also speed up using fastlane.

Our current Gemfile:

source 'https://rubygems.org'

gem "fastlane"
gem "xcode-install"
gem "cocoapods"

plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
Enter fullscreen mode Exit fullscreen mode

Setting up Fastlane Match

You can use fastlane match to manage code signing. We use it in our fastlane setup.

To set up the match, you’ll need a private Git repository. Follow the documentation and run the init command from the root directory of our project and follow the instructions shown in the terminal.

bundle exec fastlane match init
Enter fullscreen mode Exit fullscreen mode

In our case, our Matchfile exists from the cloned demo project in Github. So, run fastlane match without init to get all required keys, certificates and provisioning profiles installed to your project. 🙌

Note:

Private information like API keys or deploy credentials shouldn’t be written in the pipeline definition file or elsewhere committed to source control. On Semaphore you define these values as secrets using either the sem CLI or through the web interface (Configuration part of the sidebar -> Secrets). Secrets are shared by all projects in the organization. — Semaphore Team

Build your project and run fastlane to check if everything that we did it until now is working as expected.

If you see the message [10:28:38]: fastlane.tools finished successfully 🎉, celebrate 🎉.

Now, we just need to push our code to GitHub, and SemaphoreCI will run the CI/CD pipeline for us.

git add -A
git commit -m "setup semaphore"
git push origin master
Enter fullscreen mode Exit fullscreen mode

After you push your code to master branch in GitHub, your semaphore pipeline will run for the first time. If you click in the commit link, you can see all the processes (steps) in a beautiful layout components separated the steps of your pipeline configuration.

Voilá! Now you just need to continuously push code to your branches and App Store Connect processes the build, and it becomes available in TestFlight and you will be able to select it for your new iOS app version.

Conclusion

There is much more that you can do with automated deployments. CI/CD pipeline has a lot of advantages, more than from the steps we follow through this tutorial. You can add more steps to your pipeline configuration, like get your CHANGELOG file, push messages to Slack, automate build number incrementation, dSYM uploads to Crashlytics, etc.

After you take some time configuring, all this stuff will help you stay focused on writing code rather to manually deliver your app every time. So, your build updates are not stressful and tedious. You can inspect your code by adding tools in the process, for example to run static code reviews, linting, tests or any other important step that you could automate.

However, start with simple things first. If you've made it this far, congrats! 😃 I hope you found this post helpful.

Want to deliver continuously your iOS applications? Check outBuild, test and deliver your iOS apps with Semaphore CI/CD.

Top comments (0)