iOS Continuous Deployment with Fastlane 🚀iOS Continuous Deployment with Fastlane - Coletiv Blog
If you want to add Continuous Deployment (CD) to your iOS applications and you are having a hard time doing so, you have come to the right place.
We were missing an article describing the complete process of using Fastlane to perform CD safely and with a working example using a Continuous Integration (CI) service (in this case Travis CI). With this in mind, this guide will provide you a step-by-step guide for the entire process.
We have now deployed multiple applications at our company and we found the process of manually delivering the applications to the App Store is a very repeated, frustrating and time consuming process. Using Fastlane we went from a few hours of testing and deployment to just a few minutes.
This is the second article from a two part series that includes:
- Automated Testing (Introduces Fastlane)
- iOS Continuous Deployment with Fastlane
Notes: Sample Project 📋
Notes is an iOS application that allows the user to add, remove and change notes. This project is used to illustrate how to do continuous deployment using Fastlane together with Travis CI.
The entire project is available on GitHub, for you to consult and use as you please.
- Create a new Apple ID without 2FA. Avoid using your own account, its safer for you and easier to deliver the project along to your client or someone else. Fastlane supports 2FA but you will run into issues when managing the session token (you should test it though!);
- Add the account to the project development team in Apple Developer and iTunesConnect. This will be mandatory to automatically create the necessary certificates and provisioning profiles, and also to upload the applications to TestFlight or the App Store;
- An empty GitHub private repository to store the encrypted certificates and provisioning profiles used in your project. Don’t use or change this repository for anything else, the script will manage the repository files on its own.
By the end of the article 🎓
You will have multiple commands available, all under a single file, that will allow you to:
- Test your application, with a pretty printing at the end;
- Manually deploy your application from your machine, with the build version incrementing by fetching the current version from TestFlight;
- Automatically deploy your application from your CI service.
A. Xcode Project Setup 🛠
We need to setup the Xcode project for a harmoniously interaction with the Fastlane and Travis CI scripts.
At the time of writing and after trying to use Xcode with the **Automatically manage signing **selected, we concluded the only viable way to properly implement the continuous deployment was to go with a manual certificate management.
These are the steps:
- Create the certificates;
- Configure Xcode signing to the new certificates;
- (Optional) Register more devices.
A.1. Certificate Management using Match (from Fastlane)
You will need the Apple account and the GitHub repository mentioned in the Requirements 🎒 section. Just use the init command and follow the steps shown (you will be prompted the repository URL).
fastlane match init
By the end of the script you will have created a Matchfile inside the fastlane folder. Open it and change the
app_identifier and the
username to your application bundle identifier and the Apple ID of the account created, respectively.
Now we need to run the command to create the certificates to the desired profile type. Since we will want to configure the Xcode project correctly, we are creating the
development and the
Run the development script, a passphrase will be prompted to encrypt and decrypt the certificates, save it since we will later need it.
fastlane match development
Proceed with the appstore script:
fastlane match appstore
Every certificate needed will be created, remember that this will create the certificates for all the devices listed in the developer account certificate manager, if you need to add more check step 3. Register Devices ahead.
A.2. Xcode Configuration
After you run the scripts above, every certificate will be installed in your computer. This way you can open your
.xcodeproj and configure your targets signing accordingly.
A.2.1. Disable Automatically Manage Signing
Remember to associate a specific provisioning profile with a configuration, an example using the default Xcode configurations:
- match Development for Debug
- match AppStore for Release
As you can see, we disabled Automatically manage signing and set the match created provisioning profiles to their respective configuration.
NOTE: Leave the test targets (In the picture above: NotesTests and NotesUITests) with the Automatically manage signing setting turned ON.
A.2.2. Enable Apple Generic Versioning System
This is only if you want Fastlane to manage the versioning of the application, it needs to use the Apple Generic system in order to know how it should increment the build version automatically.
To do this you can either configure the project (like the example below) or the target depending on what suits you best:
A.3. Register Devices 📱
This step isn’t mandatory but if you are testing with multiple devices, you will want to register them and create the appropriate provisioning profiles to be able to run the application on those devices.
In order to register devices you will need a name and a UUID. If a device is connected to your computer, you can use Instruments to list all the connected devices and find these two parameters using the command:
instruments -s devices
After retrieving the name and UUID of the devices you can do two things:
- Register a single device via command line using the tool register_device:
fastlane run register_device name:"iPhone 8" udid:"d629fef002af1..."
- Register multiple devices by using the Fastlane tool register_devices. Unfortunately there is no command line support at the time of writing and I can’t provide an example, if this changes please let me know.
NOTE: You will need to repeat the step 1. Certificate Management using Match with Forcing to update the Development provisioning profiles with the new devices.
fastlane match development --force
The AppStore provisioning profiles are not specific to the list of devices you have registered therefore they don’t need to be updated.
B. Fastlane Setup 🚀
After configuring Xcode, we will need to write the Fastlane scripts to perform the manual and automatic deployment.
NOTE: If you don’t have any builds uploaded to Testflight or the App Store, you need to perform a manual deployment before you can automate the process.
itc_team_id and the
team_id are only needed if your Apple ID is integrated into more than one team on the Apple Developers Portal and iTunesConnect.
NOTE: If your Apple ID is in multiple teams, remove the settings and run the B.1. Manual Deployment script, extract the iTunesConnect ID and the Team ID from the script log (you will be prompted to select a team) and set them in the
# Application Configuration app_identifier "com.coletiv.medium-notes" # Bundle Identifier # Apple Configuration apple_id "firstname.lastname@example.org" # Apple ID itc_team_id "119013826" # iTunes Connect Team ID team_id "RGK82JLN9X" # Developer Portal Team ID # For more information about the Appfile, see: # https://docs.fastlane.tools/advanced/#appfile
B.1. Manual Deployment
We now need to create the Fastlane lane responsible for the manual deployment, we called it
manual_testflight, check the
# 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 # # Uncomment the line if you want fastlane to automatically update itself # update_fastlane default_platform(:ios) platform :ios do desc "Push Notes to TestFlight Manually" lane :manual_testflight do # Fetch the necessary certificates and # provisioning profiles into default keychain. match( readonly: true ) # Increment the build number using the # latest Testflight build number. increment_build_number( build_number: latest_testflight_build_number() + 1, xcodeproj: "./Notes.xcodeproj" ) # Build the application using the # specified scheme. build_app(scheme: "Notes") # Upload the application to Testflight upload_to_testflight( skip_waiting_for_build_processing: true ) end end
The script will fetch all the necessary certificates, increment the build number by checking the latest build uploaded to iTunesConnect, create an .ipa file and upload it to TestFlight.
Before running the script we will need to set some environmental variables: the Apple ID password and the passphrase I said you would need later during step A.1. Certificate Management using Match.
export FASTLANE_PASSWORD=”YOUR_APPLE_ID_PASSWORD” export MATCH_PASSWORD=”YOUR_CERTIFICATES_PASSPHRASE”
If this is the first build you are uploading, you might get a version prompted, just press Enter and it will properly get the initial version (1.0).
NOTE: If you didn’t follow step A.1. Certificates Management using Match, you will not have the necessary certificates on your computer to perform the manual deployment.
Now we only need to run the manual_testflight lane:
B.2. Automatic Deployment
For the automatic deployment we need to add some changes to the manual lane and we created the lane
travis_testflight, you can check in the
# 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 # # Uncomment the line if you want fastlane to automatically update itself # update_fastlane default_platform(:ios) platform :ios do desc "Push Notes to TestFlight with Travis CI" lane :travis_testflight do # Fetch the keychain env variables # securely stored in the travis.yml. keychain_name = ENV["MATCH_KEYCHAIN_NAME"] keychain_password = ENV["MATCH_KEYCHAIN_PASSWORD"] # Create a temporary keychain to # store the certificates. create_keychain( name: keychain_name, password: keychain_password, default_keychain: true, unlock: true, timeout: 3600, add_to_search_list: true ) # Fetch the necessary certificates and # provisioning profiles. match( keychain_name: keychain_name, keychain_password: keychain_password, readonly: true ) # Increment the build number using the # latest Testflight build number. increment_build_number( build_number: latest_testflight_build_number() + 1, xcodeproj: "./Notes.xcodeproj" ) # Build the application using the # specified scheme. build_app(scheme: "Notes") # Upload the application to Testflight upload_to_testflight( skip_waiting_for_build_processing: true ) # Remove the temporary keychain leaving # no trace. delete_keychain( name: keychain_name ) end end
This script will do the same thing the manual does, but will take into account an extra measure of security. It will create a locked keychain and save the certificates there and, by the end of the script, it will delete the keychain along with all the downloaded certificates.
NOTE: Do not run this lane on your personal computer, it will mess with your keychain and you might end up losing your keychain data. Use it only on the CI machines.
C. Travis CI Setup
As you were able to see in section B.2. Automatic Deployment, the script contains environmental variables. We will need to encrypt those into the Travis CI script and some other variables.
For this step you will need five environmental variables, some are the same as before:
- MATCH_PASSWORD = The certificates passphrase;
- MATCH_KEYCHAIN_NAME = A keychain name (choose);
- MATCH_KEYCHAIN_PASSWORD = A keychain password (choose);
- FASTLANE_PASSWORD = Apple account password;
- CI_USER_TOKEN = GitHub Personal Access Token with repo permissions, you can get it here (it will be necessary to access the private repository with the certificates).
After you get all those, you need to encrypt them onto your
.travis.yml file, to do so just insert the following five commands, one by one:
travis encrypt 'MATCH_PASSWORD=YOUR_CERTIFICATES_PASSPHRASE' --add env.global travis encrypt 'MATCH_KEYCHAIN_NAME=KEYCHAIN_NAME' --add env.global travis encrypt 'MATCH_KEYCHAIN_PASSWORD=KEYCHAIN_PASSWORD' --add env.global travis encrypt 'FASTLANE_PASSWORD=YOUR_APPLE_ID_PASSWORD' --add env.global travis encrypt 'CI_USER_TOKEN=YOUR_PERSONAL_ACCESS_TOKEN' --add env.global
This will add the environmental variables directly to your travis script. Now we just need to add a command to provide GitHub access to the Travis machine during
before_install and execute the Fastlane lanes during
script phase, you can check the final script below:
# Configurations language: objective-c osx_image: xcode9.2 xcode_sdk: iphonesimulator11.0 # Before Install before_install: # This command will provide travis machines with github access - echo -e "machine github.com\n login $CI_USER_TOKEN" >> ~/.netrc # Script (Fastlane) script: - fastlane scan - fastlane travis_testflight # Environmental Variables env: global: - secure: VrIEyJrkZo+IkOXHJEIir+sYIt9YsuYDcCCdtGtY1ILT5MbENSeC3rjhsi8jmg4L9mUna2mayWvZsHfo8WqLg5Iyq9TshvewhnsMI/7eEqis7afnw9kY8qJA23lsMjj57SPn0Y/C8HU2/jzlfrg0qFdCgRzOZW1BaxBq8hM1pWv8+jisbTG2M+2DgFjKUj2mstFRIkIRHsI6+NnHvHdymIVEZbneBFL0iJ/8gWqi2uq1iNobDql4jQUY8z8n2LbAwtxPIRvp/6PAKBhRzaa/jDFbayBH/W6ecLc/i1YzNJZ3N5YuWynMO30DUVQuCjTzmpSM9jmqh7czLaPpco40W0SnEFCxFJ9tlnmSyHoD6/TDalfpML30JKnA5ReuYHdy0Xa4knrV7YT28Yv4IV1cAU2byJlyaunMxt5biz1xO3nSciYqrXVKOBCHQlLBNsChNkRFn+ueKrNfGZX4gvOyfaJIOY1gAcWyZ7dJtDtgrK30W3iol6On4lVhb+sCoCRej8K5DmYepAFnjzvvq6iNaBosp7WpBzOTk3AuaKEU3yLDBJva+Ebh7NEvvKPhBzpdcX0uQAE3yRodnxfOhiyvXXraHdEYEcYbAIMQXISJOx+pWAC/irWlf6Co2i3T6Hw3oCZ1TYSp1h8ZbsZdw9KJ8QpTXasjv3lXYPKIndGTdlk= - secure: HWVcjB5df5Cmd/oz+NDoV4nijS6zRD+rEMfiFzWlP8a7jRWe2kJeOeCdVU737qW5bNDcc0Nt4gi9LQQFzUqKQPV3ZMLn2USeUl1DvaKZ4Pl+QErJsVXLzY5HTijA6611OQLUfg7qfscx0fnMW2uiCRoZ2dwqoYQ3fI8BCGUEEgOfKJ96B1en3SwSN0H/u/Yx9tW+dtPljcNQKi96M5AFw9Af/yU7S7eAcumi4Tii2YkAA8l7aB5V8VcPy8MQIqcnrJ0SkVgIsNaE+r9tVN6++G37f2RakyW47m4fURWTSb89LLg6ByNpq8+CIt/8qb9ZJVJrwMlii9zp4L9H0PZ8Y36wSrX2sdwu/YEZGYyjrRhIU1HB0olgKR+/j/gJfRJ11P9yeYscwMgvlQO2vuJbW/ZwrkUW4XY+qlc21MfyF4Cm5hDcqWfCqgImNOP84VoAouART6Xv03VNwpTCW3EHg2+YUUGMzIyEiX76PpmEQFxLyLWLioKgOm6hTCNMCAG5qQlw6bQlWq9LYpkRD8Pq8tU9JkDjEmOrclPByzcTVFs40C82WbsXEpwDsV37L3dmR83Zish7GA/iTo7T12e1QkrsY0KGbBQ9jr7DyaWIGwnrw+w2wZ7r9ie51PP/pGS5eO5Q2K22atVQ0TO8qY7nl9lRWr73AzsBmnYsjIHdcQA= - secure: n27Wz2chS8jAqOGbfiCkFj3mF1uf4E0HjVi/isG+5hMFzehGylHHwq9pCNxPDPx/VV1qHtBjW8k81Kx3iC1iHcE2jUI+XEFHgN9LaOVS1SELUkugRm8oWBDSRAqOG5O0IpmcGZKnNb4j5O1xEeKySvhYO/+AFvmN9URb02xBtoMGGia6SmhDJK38FQfw9/F1O1YJ7iByefI3y8yDvw5FsZGGeDsw8tKtAW9xOkWjzCOne71G7TzTkdKECfDgbUDAPIH34DugvV2MQeC5WVWmLX/R9I2LuTFLdr5jH+txUdc58OFu0kw9s+P1kKHgnvtWtrBCqOtKujPb7FYvPIZt0DeMgkBbF6NP5gKOYiPsATyuKvpAKlLJhWlwSjJiRCdwojHK5VOxGKZD6oQeNJVeXus7/wbygE1j6APhwQsWnu19+uVJqXqxGo0wF8j0skt8kysI13qJoulQ8UgrDmg5UY1tvA8D9HHQLFZTYmNOodXg8sOAXUZz4sBuDrDHoVIaFDwg6WOJ5buRttqvGmsvBI5ShfW7Zk6WwY3BvROSWXviEMCLbMMHcwWTzf5lBKP8pq7ePZgyDTMH+7A5bTlHEmnSFIgzXmvEanjUWEW70LgApd2gmyW2Hmd4C6W0fVmLHMcnJoA6v61XI67LPTq1NLFEy2fPDWsJzGraRNj+z2M= - secure: RnfgsbNlOpN/G1oS0JYapxsxCiarZiDWx/ODRteFc+nnONLDvKTSuzypocB2gLqScgw2KiDRQQUVpctdBI/rk3vQAkJ31NXJ1mLXUo0Zyt8NiTEUn2dCCFc/y07Kf1dDM7bGzvU61+kDn2HKgmFGyfHolh2t3DzVuS2Y3F1xMb+CueBhi+XwrSj8OHjKHSqo9bg0BrMWMTEcNhzpAdrnbJWxbkgyK9bpq44jC1Bq6OQkkbg6Ti4qrqnvURzUMvX386QW94T1PYgRNcYxfMeoB2QyYlTaoMFJO2ALemF+VeZ4rT0vEE8mqaAlAmxkUS5Ge3HNhjFoDNlHrJI5/lnjuulv5gcLGLhGtPVqfc1TUIe327TipaNXYUphQdX7S4VGphF9TRNLhydFIUNei+FZUklIoQiAVUohsYAdkstljDYUckzn2DoJzpgHNvw6kOy+SFYBdlTrzBhNta5P3dSKs3f18SwPVCmqE/bq2TKWhhbjjCt4NLQ77Wi+qfFRvXKxniqiLkaKZUiew2SX/9cEKN8fDvWXAX2sgTTJSiFWY/sawkwoolbrN+dfuSdwNPzebRSDNyovtpbr5RexDJfrJDjpA6tP++7jGyyIELzCzVfYiXsEaOnasw5jWM9yE8So0wGVGEngoViQ3/5b6DlZRBAW08h98Yttsc+LvEW11fc= - secure: pMOnc9ZtNeeKwv30q6XL2vn2iW489ikDxmTff76VW2KIiLAg2gtcvUi/t3yeYP/5rimgJP9foQZRrUZgS89LDEJs7rjQEZLt8fEhIW0dO4R8vkOytqspoyZeeUpPBJYMYNFrzXU3tkvq2L9tcIOohOlRJ64h7QUSvQQ6DG7btW3rW4GAyRwN6Nzxl2fjANH0zMUjIouYi2mXj0sC/5HSD6GE6vCLZvLeW496bDF2CIj3O7c6FLfY6JnHCPWMlsH114533UzG02l9iANs3BNcTFRmlkI6ft0ElB4hqoAMMTI9N4bSnrxn1vydr+gZy3UGeumKG2XONirCzwfuFxI31zeiOy+vDy1H1Dyh/w+8h6gleRuiWCiCTcvI0Xb9zVKttd5Cc4vS40umHycMnChLWfOTtvGEPTF6PfnwcnjTEKJgISshsg6391Qyam20Xhgbby7zS12ZKCxnZlCzlDFQGYWzsEul8E3S8fM6rp/MgO9Ri6xnqgR9K4BpA/mzpwv9+298Q4nPsilmgN51ZMhi/JKMKXRUUGhvu1c0t1OFqADAtnoBPDbnuWGV7nP8trbVMe3AYC7yoRw1CYHMNx2WOhMNc2+EI/X9WTvP3+rm4PL0k1kb64d1TTx/mJvmIu+onJEP0DPWqnpKCWyEqA52/std9tlt9+fAwhuqIavHqrw=
Time Saving Tip 🕐
If you are tired of getting the
Missing Compliance warning on iTunesConnect, you can add a key to your
Info.plist file that automatically complies with the export policies.
Set the key
ITSAppUsesNonExemptEncryption boolean to
NO, remember you should only use this if it applies to your project (more info here).
Thank you for reading! 😊
I hope this guide helps you and your team saving some time with the deployments and remember to continuously check Fastlane advancements and updates. More and more tools will come that may expedite this enduring process of deployment!
In case you don’t know, Coletiv is a software development studio from Porto specialised in Elixir, iOS, and Android app development. But we do all kinds of stuff. We take care of UX/UI design, web development, and even security for you.