Code Signing for iOS projects

This guide describes how to set up code signing in CI/CD for your iOS project on Semaphore using Fastlane Match.

If you'd like Semaphore to push new builds for distribution to HockeyApp, Fabric Beta or TestFlight, then CI must have access to the code signing certificate and provisioning profiles that are required to build the app for such distribution.

Below we show you how to do that.

Basic configuration of iOS projects

We assume you have a working iOS project configured to run on Semaphore. If you're new to Semaphore or not sure how to configure your iOS project, see the open source demo project and associated tutorial for an example.

In addition, we assume that you installed and configured Fastlane. If not, please visit the Fastlane setup guide to learn how to configure Fastlane for your project.

If you are new to code signing, we recommend you to visit the Code Signing Guide and read it.

The Match tool has extensive documentation on how to configure and use it, which you can find at Fastlane Docs.

In a nutshell, what we are aiming at is:

  • Your iOS project configured with Fastlane and Match.
  • You have a separate private Git repository that stores code signing certificates and provisioning profiles.
  • Your CI/CD project on Semaphore works with these two repositories.

Setting up Fastlane Match

Before setting up provisioning profiles and distributing builds, you need an App ID for your app and App Store Connect application. If you don't have them yet, you can either create them manually in the Apple developer account and App Store Connect website, or you can use Fastlane's produce action to create both from command line.

Once you have your App ID and App Store Connect Application, you can proceed to configuring code signing for your project.

To set up the match, you'll need a private Git repository. Follow the documentation: run the init command from the root directory of your iOS project and follow the prompts:

$ bundle exec fastlane match init

This will configure match with your private Git URL and password locally on your computer. With this, you are now ready to create provisioning profiles for your app.

The following will generate a 'development' provisioning profile so that you can run your app on the device connected to Xcode:

$ bundle exec fastlane match development

This will generate an 'adhoc' provisioning profile so that other people can run your app on their devices through non-App Store distribution like HockeyApp or Fabric Beta:

$ bundle exec fastlane match adhoc

This will generate an 'appstore' provisioning profile so that your app can be distributed through TestFlight and App Store:

$ bundle exec fastlane match appstore

Preparing your Xcode project for use with Fastlane Match

By default, the Xcode uses "automatic" code signing that uses Xcode's preferences to manage the signing certificates and provisioning profiles. While this works on your local computer, it won't work in the CI environment. That's why the project should switch to the "manual" code signing.

To do this, in the "General" tab of your app target in Xcode, uncheck the "Automatically manage signing" and then select the provisioning profiles generated by match for each "Provisioning Profile" dropdowns for each configuration (Sections "Signing (Debug)" and "Signing (Release)").

Adding Semaphore Plugin to Fastlane

In order to configure Fastlane for the CI/CD environment, please install the Semaphore plugin:

$ bundle exec fastlane add_plugin semaphore

The plugin provides setup_semaphore action that configures temporary Keychain and switches match to 'readonly' mode.

Adding Match to the Fastlane Lane

As mentioned earlier, CI doesn't have access to your developer account unless you provide the credentials in the configuration for the match. To make this work, we need to add a match action to the lanes that will publish your app for distribution.

# fastlane/Fastfile 
default_platform(:ios)

platform :ios do
  before_all do
    setup_semaphore
  end

  desc "Build and run tests"
  lane :test do
    scan
  end

  desc "Ad-hoc build"
  lane :adhoc do
    match(type: "adhoc")
    gym(export_method: "ad-hoc")
  end

  desc "TestFlight build"
  lane :build do
    match(type: "appstore")
    gym(export_method: "app-store")
  end
end

Adding a deploy key to the Semaphore project

Now that we have configured our tools to use appropriate configuration, we must also provide a way for Semaphore to access the Git certificates repo, and the Apple Developer portal.

To allow Semaphore to download certificates from your private certificates repository, you need to create a deploy key and add it to Semaphore secrets. Adding a deploy key is described in Using private dependencies.

If you have not installed the sem command line tool, this is a good time to install it - see the sem reference.

To store the deploy key as a secret file in the Semaphore environment:

$ sem create secret ios-cert-repo -f id_rsa_semaphore:/Users/semaphore/.keys/ios-cert-repo

Mounting the ios-cert-repo secret in your pipeline configuration will create the deploy key file in the ~/.keys directory when your CI/CD jobs run on Semaphore.

For an introduction to secrets on Semaphore, see the guided tour on secrets.

Adding the Match passphrase to a secret

Next, add the URL for the certificates repository and the encryption password as environment variables that will be accessible in CI. We 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>"

⚠️ As a security note, it is highly advisable to create a separate App Store "developer" account that will be used only in the CI/CD environment. The same approach is advisable for accessing the private Git certificates repository.

In a similar fashion, you can also add API keys for another distribution platform that you use, such as HockeyApp or Fabric Beta. Consult with the respective platform's documentation to see which environment variables and secrets you need to include.

With these secrets and configuration in place, Semaphore will be able to access the code signing certificates and provisioning profiles in order to build and distribute your app in your CI/CD workflow.

Sample configuration files

After you have configured Match, Fastlane and environment variables, you are ready to build your app on Semaphore. The following are the examples of 'Fastfile' and semaphore.yml configuration files that will run test and build on every code push.

For Fastlane:

# fastlane/Fastfile
default_platform(:ios)

platform :ios do

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

  desc "Run Tests"
  lane :test do  
    scan
  end

  desc "Build"
  lane :build do
    match(type: "appstore")
    gym(export_method: "app-store")
  end

  desc "Ad-hoc build"
  lane :adhoc do
    match(type: "adhoc")
    gym(export_method: "ad-hoc")
  end

end

For Semaphore:

 # .semaphore/semaphore.yml
version: v1.0
name: Semaphore iOS example
agent:
machine:
    type: a1-standard-4
    os_image: macos-mojave
blocks:
- name: Run tests
    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 test
        commands:
            - bundle exec fastlane ios test
    secrets:
        - name: fastlane-env

- 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
    jobs:
        - name: Fastlane build
        commands:
            - chmod 0600 ~/.keys/*
            - ssh-add ~/.keys/*
            - bundle exec fastlane build
    secrets:
        - name: fastlane-env
        - name: ios-cert-repo

Example application on GitHub

To see how an iOS project can be configured on Semaphore, see the semaphore-demo-ios-swift-xcode GitHub repository.

Still need help? Contact Us Contact Us