How to integrate fastlane into an iOS Project

Jason Chan
The Startup
Published in
11 min readFeb 24, 2020

--

What is fastlane? I think you can find the best solutions here.
However, I would like to tell you why I choose to learn fastlane integration.

In 99.co, we are using Travis CI for in-house QA build deployment. When a new commit for the specific branch is detected, Travis will execute and start build the iOS build and submit the built to Hockey (deprecated).

There are a few processes that happen for the whole deployment activity:
a. Developer pushes the commit to the specific branch.
b. Travis triggers the build.
c. Travis submits the build to Hockey upon success.
d. Developer checks the status of the QA-build deployment, once hockey build is created, manually update the QA tester for the availability for testing.

There are several issues that caused the activity to fail.
1. Whenever the certificates and provisioning profile is expired, you will need to execute a series of actions to fix the Travis deployment issue. This includes downloading the new certs and profiles, encrypt it using Travis, commit it. (NIGHTMARE).
2. You will be suffered a lot in the meanwhile of testing will travis success or failed!
3. Manual process to notify tester upon success…

I believe you are able to find numerous integration tutorials all over the internet (which I did for learning the integration). However there is not a single one that is completely comprehensive enough for the whole integration process, especially the problem you may face along the way of the integration.

In this series of tutorials, I will consolidate everything I learn and I will go through step by step for the integration. Beside of that I will also share the problems I faced during the whole integration process.

Series 1: Fastlane Integration and using match to handle certificates and provisioning profiles.

Series 2: Using Fastlane to automate In-House and App Store distribution process.

Series 3: Integrate Travis CI to handle distribution instead.

Series 4: Advance Fastlane integration.

Series 1: Fastlane Integration and using match to handle certificates and provisioning profiles.

Installation of Fastlane on your machine

Handling of Certificate and Provisioning Profiles
Section 1: Setup Github SSH access
Section 2: Initiate Fastlane
Section 3a: Handling Provisioning Profiles for new project
Section 3b: Handling Provisioning Profiles for existing project
Section 4: Handling Certificates
Section 5: Fastlane match action integration
Section 6: Create a new lanes to install certificates and provisioning profiles into a new machine.

Installation of Fastlane on your machine.

Step 1: Install the latest Xcode command line tools

xcode-select --install

Step 2: Install Homebrew (you can use sudo to install fastlane but Homebrew is highly recommended due to a lot of issue came up by using sudo.)

/usr/bin/ruby -e \   "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Step 3: Install Ruby

brew install ruby

Step 4: Install Fastlane

brew install fastlane

Do not use brew cask install fastlane , it is the legacy way of installation and it will cause a lot of issues happened in the integration.

Now you should have fastlane installed on your machine. To verify the installation, you can run fastlane init in your iOS project, if no error show up, fastlane is ready to hit the rock now.

Handling of Certificate and Provisioning Profiles

Certificates and provisioning profiles is complicated especially you have multiple build targets or using a enterprise account for in house deployment which is very common when you are dealing with medium-large scale project.

There are multiple fastlane actions is designed to handle certificate and provisioning profile, sigh , cert , import_certificate and more. However I recommend to use only match action to handle all the required certificates and profiles.

There are two way of using match action,
a. generate brand new certificate and provisioning profiles from fresh.
b. reusing existing certificate and profiles without revoking it.

It is extremely important to understand the consequences of using the 1st approach.

  1. Revoking a production app certificate and provisioning profile will have no impact to the existing app the already live in App Store.
  2. The submitted app in Testflight which built with the revoked certificate and provisioning profile will no longer testable and submit for review.
  3. All the in house distribution app built under the revoked enterprise certificate and provisioning profile will no longer working.

It is relatively reasonable to use the second approach if the concerns are matter for you.

I will use a new project to demonstrate how to handle the 2nd approach.
I will use GitHub for the integration.

I highly recommend to use the 2nd approach no matter you are integrating into existing project or a brand new project, the reason you can find here.

Section 1: Setup Github SSH access

Step 1: Generate new SSH key
- Open Terminal:

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

Do not enter any passphrase for this key.

Step 2: Add newly generated key to Github
- Copy SSH key:

pbcopy < ~/.ssh/id_rsa.pub

Go to GitHub, paste in the SSH key.

Step 3: Create a new project.
If you want to integrate into existing project, you can jump to Section 2.

Step 4: Create a new repo on GitHub.

Step 5: Setup Project remote git.

Copy the SSH access git url and add it to the project as Existing Remote. Ensure that it is SSH address start with git@github.com.

To verify it is correct, verify it with Push action.

Section 2: Initiate Fastlane

To avoid any unexpected issue, ensure fastlane is updated to the newest version.

brew upgrade fastlane

Important: In order to avoid direct modifying master branch, please ensure that you are branch out new branch ( let’s call feat/fastlane) before you start fastlane integration.

Step 1: Fastlane initialisation

In project root folder,

fastlane init

Choose option 4 to have the full control of the integration. You should see bundle update .

Section 3a: Handling Provisioning Profiles for new project

Step 1: Configure project’s configuration
There will be 3 configurations for
1. Development
2. In House Distribution
3. App Store Distribution

(This step is to demonstrate advance configuration for enterprise project, you can ignore it.)

Update Product Bundle Identifier for different distribution environment.

Disable Automatically manage signing, you should able to see the errors.

Login to Apple Developer:
- Three important attributes you will need to understand
1. App Identifier — a unique identifier for every app, it is the same as the Bundle Identifier above.
2. Certificate — a certificate that generated using Certificate Signing Request(CSR) from your mac which is used to sign the provisioning profile.
3. Provisioning Profile — a profile that contain a set of certificate, unique device identifier and App Identifier and must be installed on device to run the App.

Step 2: Create universal Apple Certificate

Starting from Xcode 11, Apple introduce universal Certificate for both Development and Distribution. You can have one certificate that used across iOS, macOS, tvOS and watchOS.
I highly recommend to create/use this certificate for all the app you created.
However there are limitation on number of certificates you can created for this option. Maximum of 2 certificates can be created per developer.

Step 3: Create new App Identifier

Step 4: Create new Provisioning Profile

Repeat the Step 3 and Step 4 to create a total of
3 Universal Certificates, 3 App Identifiers and 3 Provisioning Profiles for Development, InHouse and AppStore distribution.
Reminder: You most login to enterprise account in order to create InHouse distribution Profile.

Step 5: Create one folder and name itprofiles , create 3 subfolders development , enterprise and appstore in profiles folder.

Step 6: Download the provisioning profile and rename it according like below.

AppStore_{{bundle_id}}.mobileprovision
Development_{{bundle_id}}.mobileprovision
InHouse_{{bundle_id}}.mobileprovision

If you have notification extension, you can create the extension provisioning profile using the same certificate that generated in Step 2.

The naming of the provisioning profile follow the same structure.

AppStore_{{bundle_id_of_the_extension}}.mobileprovision
Development_{{bundle_id_of_the_extension}}.mobileprovision
InHouse_{{bundle_id_of_the_extension}}.mobileprovision

Step 7: Save the renamed provisioning profiles into respective folder accordingly base on the distribution method, eg. AppStore_ -> appstore folder.

Section 3b: Handling Provisioning Profiles for existing project

For existing project, to avoid revoking existing provisioning profiles, you just need to follow Step 5, 6 and 7 above.

Section 4: Handling Certificates

Step 1: Download the certificates

Step 2: Double click the certificate to install it so that it can be appear under you keychain.

Step 3: Create 3 subfolders development ,enterprise and distribution in a certs folder.

Step 4: Export the certificate as .cer .

Step 5: Export the private key as .p12 . Do not use a passphrase for this!

Repeat Step 4 and 5 to ensure that you have 3 .cer and 3 .p12 files are saved into respective folders.

Step 6: You will need to use Spaceship to verify the certificates.

Open Terminal:

irb
irb(main)> require 'Spaceship'

If you are having issue with require 'Spaceship', exit the irb by entering exit and run

sudo gem install bunder
bundle install

Important: Since apple have required 2 factor authentication (2FA) for every Apple ID, we need to setup trusted phone number in order to login to Spaceship.

Open Terminal:

irb
irb(main)> ENV["SPACESHIP_2FA_SMS_DEFAULT_PHONE_NUMBER"] = '+65 xxxx xxxx'
irb(main)> require 'Spaceship'
irb(main)> Spaceship.login
Username: {{apple_id}}
Password: {{password}}

You will able to see

//Two-factor Authentication (6 digits code) is enabled for account '{{apple_id}}' //More information about Two-factor Authentication: https://support.apple.com/en-us/HT204915  //If you're running this in a non-interactive session (e.g. server or CI) check out https://github.com/fastlane/fastlane/tree/master/spaceship#2-step-verification  //Environment variable `SPACESHIP_2FA_SMS_DEFAULT_PHONE_NUMBER` is set, automatically requesting 2FA token via SMS to that number 
//SPACESHIP_2FA_SMS_DEFAULT_PHONE_NUMBER = +65 xxxx xxxx //Successfully requested text message to +65 xxxx xxxx
//Please enter the 6 digit code you received at +65 xxxx xxxx: //Requesting session...

Step 7: Verify Certificate ID

irb(main)> Spaceship.certificate.all
irb(main)> Spaceship.certificate.development.all
irb(main)> Spaceship.certificate.production.all

The list will contains all the properties of every certificate, you will need to find the correct certificate that you want by comparing the name and created/expires.

Rename the certificates that you saved previously with the ID you get.

You are ready to integrate using match action now!

Section 5: Fastlane match action integration

Step 1: Initialise match action

Open Terminal (under root folder of the project):

fastlane match init

In this tutorial we are using Github to host the fastlane certificates and provisioning profiles. You must always ensure that your github repo is private. If your project repo is already a private repo, using the same repo as the storage is always recommended.

You will be prompted to select hosting option, choose option 1: git.

Copy and paste in the Github SSH url of the repo (Section 1 — Step 4) in and Enter.

You will see git_url("git@github.com:{{your_repo}}" is inserted into fastlane/Matchfile.

Important: Do not run fastlane match development , it will always create new certificate and revoke all you existing provisioning profiles.

Step 2: Decrypt Git Storage

Open Terminal (under root folder of the project):

irb
irb(main)> require 'match
// this is the repo url that host your cert and profilesirb(main)> git_url = '{{gitHub_ssh_url}}'irb(main)> shallow_clone = false// this is the passphrase used to encrypted the certs and profiles
// remember this passphrase! Is is very important to decryption next
// this passphrase will be used in
Section 6 -- Step 2
irb(main)> ENV["MATCH_PASSWORD"] = '{{encryption passphrase}}'// Your selected branch that at the beginning of integrationirb(main)> branch = '{{your selected branch}}'// Download and decrypt the repoirb(main)> storage = Match::Storage.for_mode('git', { git_url: git_url, shallow_clone: shallow_clone, git_branch: branch, clone_branch_directly: false})irb(main)> storage.downloadirb(main)> encryption = Match::Encryption.for_storage_mode('git', { git_url: git_url, working_directory: storage.working_directory})irb(main)> encryption.decrypt_files// You should be able to see
**🔓 Successfully decrypted certificates repo** next.
irb(main)> storage.working_directory// A temporary directory will be create and you will be able to see the path of the directory instead.**“/var/folders/ql/4rgq9x7j51n_971xb332w9lc0000gn/T/d20181105–65220–1oalh6v”**

Step 3: Paste in both certs and profiles folder into the temporary decrypted folder.

Step 4: Encrypt the temporary folder and push to git repo.

irb(main)> encryption.encrypt_files
irb(main)> files_to_commit = Dir[File.join(storage.working_directory, "**", "*.{cer,p12,mobileprovision}")]
irb(main)> storage.save_changes!(files_to_commit: files_to_commit)
irb(main)> exit

Now the all the certificates and provisioning profiles are encrypted and stored inside the private git repo.

Step 5: Verify and install the provisioning profiles by using match

In order to install the profiles,

Open Terminal (under root folder of the project):

fastlane match development --readonly
fastlane match enterprise --readonly
fastlane match appstore --readonly
// If you want to specify the app_identifier you can pass infastlane match appstore --readonly -a {{app_identifier}}

You should able to see **All required keys, certificates and provisioning profiles are installed 🙌**.

Section 6: Create a new lanes to install certificates and provisioning profiles into a new machine.

If you are part of the member in a iOS development team, I believe you must had problems with syncing certs and profiles across the team.

Normally what we can do is that always use Export Apple ID and Code Signing Assets... option under Xcode preferences and share it across the team.

With match integrated, you can sync the certs and profiles across the team seamlessly.

Step 1: Create a new lane infastlane/Fastfile as below and save it.

platform :ios do
desc "Install certificates and provisioning profiles".black.bold
lane :install_certs_profiles do
match(
type: "development",
readonly: true,
app_identifier: {{bundle_id}}"
)
match(
type: "enterprise",
readonly: true,
app_identifier: {{bundle_id}}"
)
match(
type: "appstore",
readonly: true,
app_identifier: "{{bundle_id}}"
)
end
end

Note: The app_identifiers should be different for three different deployments. You can refer to Section 3a — Step 1 for the identifiers.

If you have extension widget for the project, use app_identifier: {{bundle_id}}”, {{extension_bundle_id}}”] instead.

Step 2: Install certificate and profile using fastlane

Open Terminal (under root folder of the project):

fastlane install_certs_profiles

You will be asked for passphrase for the decryption, use the passphrase you set in Section 5 — Step 2.

Once you enter the correct passphase the installation will be started and you should be able to see success message at the end of the lanes.

You can now run the lane in a new machine directly to install the certs and profiles!

--

--

Jason Chan
The Startup

Jason Chan - Product Manager who passionate in UX/UI Design.