Testing iOS Applications on macOS
This document describes how to set up and customize testing for an iOS application with CircleCI in the following sections:
- Using the macOS executor
- Supported Xcode versions
- Getting started
- Using Fastlane
- Using Ruby
- Using Homebrew
- Configuring deployment
- Reducing job time and best practises
- Supported build and test tools
- Using Multiple Executor Types (macOS + Docker)
- Troubleshooting
Overview
CircleCI offers support for building, testing and deploying iOS projects in macOS virtual machines. Each image provided has a set of common tools installed, such as Ruby and OpenJDK, alongside a version of Xcode. For more information about supplied images, refer to the software manifest for each Xcode image.
There is documentation for an iOS example project and getting started on MacOS.
Using the macOS executor
Each macos
job is run a fresh virtual machine, running a specified version macOS. We build a new image each time a new stable, or beta, version of Xcode is released by Apple and aim to get these deployed as soon as possible. Generally, the contents of a particular build image will remain unchanged, except in very exceptional circumstances we might be forced to re-build a container for a specific reason. Our goal is to keep your build environment stable, and to allow you to opt-in to newer containers by setting the xcode
key in your config.yml
file.
Periodically, we will update the version of macOS each image includes to ensure the build environment is as up to date as possible. When a new major version of macOS is released, we will generally switch to this once the new major version of Xcode reaches the xx.2
release to ensure the build environment is kept stable.
We announce the availability of new macOS containers, including Xcode betas, in the annoucements section of our Discuss site.
Beta image support
We endeavour to make beta Xcode versions available on the macOS executor as soon as we can to allow developers to test their apps ahead of the next stable Xcode release.
Unlike our stable images (which are frozen and will not change), once a new beta image is released it will overwrite the previous beta image until a GM (stable) image is released, at which point the image is frozen and no longer updated. If you are requesting an image using an Xcode version that is currently in beta, please expect it to change when Apple releases a new Xcode beta with minimal notice. This can include breaking changes in Xcode/associated tooling which are beyond our control.
To read about our customer support policy regarding beta images, please check out the following support center article.
Apple silicon support
Please Note: Apple has indicated that Apple Silicon developers should continue to use Xcode 12 beta 6, rather than the GM. We have retained this image and you can access it by requesting the 12.0.0-beta
image.
It is possible to build Apple Silicon/Universal binaries using the Xcode 12.0.0-beta
image as Apple provides both the Intel (x86_64
) and Apple Silicon (arm64
) toolchains in this release. Cross-compiling Apple Silicon binaries on Intel hosts has an additional overhead and as a result compilation times will be longer than native compilation for Intel.
Running or testing Apple Silicon apps natively is currently not possible as CircleCI build hosts are Intel-based Macs. Binaries will need to be exported as artifacts for testing apps locally.
Supported Xcode versions
Config | Xcode Version | macOS Version | macOS UI Testing Supported | Software Manifest | Release Notes |
---|---|---|---|---|---|
12.4.0 |
Xcode 12.4 (12D4e) | 10.15.5 | Yes | Installed software | Release Notes |
12.3.0 |
Xcode 12.3 (12C33) | 10.15.5 | Yes | Installed software | Release Notes |
12.2.0 |
Xcode 12.2 (12B45b) | 10.15.5 | Yes | Installed software | Release Notes |
12.1.1 |
Xcode 12.1.1 RC (12A7605b) | 10.15.5 | Yes | Installed software | Release Notes |
12.1.0 |
Xcode 12.1 (12A7403) | 10.15.5 | Yes | Installed software | Release Notes |
12.0.1 |
Xcode 12.0.1 (12A7300) | 10.15.5 | Yes | Installed software | Release Notes |
11.7.0 |
Xcode 11.7 (11E801a) | 10.15.5 | Yes | Installed software | Release Notes |
11.6.0 |
Xcode 11.6 (11E708) | 10.15.5 | No | Installed software | Release Notes |
11.5.0 |
Xcode 11.5 (11E608c) | 10.15.4 | No | Installed software | Release Notes |
11.4.1 |
Xcode 11.4.1 (11E503a) | 10.15.4 | No | Installed software | Release Notes |
11.3.1 |
Xcode 11.3.1 (11C505) | 10.15.1 | No | Installed software | Release Notes |
11.2.1 |
Xcode 11.2.1 (11B500) | 10.15.0 | No | Installed software | Release Notes |
11.1.0 |
Xcode 11.1 (11A1027) | 10.14.4 | No | Installed software | Release Notes |
11.0.0 |
Xcode 11.0 (11A420a) | 10.14.4 | No | Installed software | Release Notes |
10.3.0 |
Xcode 10.3 (10G8) | 10.14.4 | No | Installed software | Release Notes |
10.2.1 |
Xcode 10.2.1 (10E1001) | 10.14.4 | No | Installed software | Release Notes |
10.1.0 |
Xcode 10.1 (10B61) | 10.13.6 | No | Installed software | Release Notes |
10.0.0 |
Xcode 10.0 (10A255) | 10.13.6 | No | Installed software | Release Notes |
9.4.1 |
Xcode 9.4.1 (9F2000) | 10.13.3 | No | Installed software |
Getting started
Select a macOS project repository you would like to build from the Add Projects page of the CircleCI application. You will need to ensure you have a plan that allows macOS builds, or if your project is open source, you can apply for a special plan with free monthly build credits.
We highly recommend using Fastlane to build and sign your apps in CircleCI. Fastlane requires minimal configuration in most cases and simplifies the build-test-deploy process.
Setting up your Xcode project
After setting up the project on CircleCI, you will need to ensure that the scheme you intend to build with Fastlane is marked as “shared” in your Xcode project. In most new projects created by Xcode, the default scheme will already be marked as “shared”. To verify this, or to share an existing scheme, complete the following steps:
- In Xcode, choose Product -> Scheme -> Manage Schemes
- Select the “Shared” option for the scheme to share, and click Close
- Ensure the
myproject.xcodeproj/xcshareddata/xcschemes
directory is checked into your Git repository and push the changes
Simple projects should run with minimal configuration. You can find an example of a minimal config in the iOS Project Tutorial.
Using Fastlane
Fastlane is a set of tools for automating the build and deploy process of mobile apps. We encourage the use of Fastlane on CircleCI as it simplifies the setup and automation of the build, test and deploy process. Additionally, it allows parity between local and CircleCI builds.
Adding a Gemfile
It is recommended to add a Gemfile
to your repository to make sure that the same version of Fastlane is used both locally and on CircleCI and that all dependencies are installed. Below is a sample of a simple Gemfile
:
# Gemfile
source "https://rubygems.org"
gem 'fastlane'
After you have created a Gemfile
locally, you will need to run bundle install
and check both Gemfile
and Gemfile.lock
into your project repository.
Setting up Fastlane for use on CircleCI
When using Fastlane in your CircleCI project, we recommend adding the following to beginning of your Fastfile
:
# fastlane/Fastfile
...
platform :ios do
before_all do
setup_circle_ci
end
...
end
The setup_circle_ci
Fastlane action must be in the before_all
block to perform the following actions:
- Create a new temporary keychain for use with Fastlane Match (see the code signing section for more details).
- Switch Fastlane Match to
readonly
mode to make sure CI does not create new code signing certificates or provisioning profiles. - Set up log and test result paths to be easily collectible.
Example Configuration for Using Fastlane on CircleCI
A basic Fastlane configuration that can be used on CircleCI is as follows:
# fastlane/Fastfile
default_platform :ios
platform :ios do
before_all do
setup_circle_ci
end
desc "Runs all the tests"
lane :test do
scan
end
desc "Ad-hoc build"
lane :adhoc do
match(type: "adhoc")
gym(export_method: "ad-hoc")
end
end
This configuration can be used with the following CircleCI config file:
# .circleci/config.yml
version: 2.1
jobs:
build-and-test:
macos:
xcode: 11.3.0
environment:
FL_OUTPUT_DIR: output
FASTLANE_LANE: test
steps:
- checkout
- run: bundle install
- run:
name: Fastlane
command: bundle exec fastlane $FASTLANE_LANE
- store_artifacts:
path: output
- store_test_results:
path: output/scan
adhoc:
macos:
xcode: 11.3.0
environment:
FL_OUTPUT_DIR: output
FASTLANE_LANE: adhoc
steps:
- checkout
- run: bundle install
- run:
name: Fastlane
command: bundle exec fastlane $FASTLANE_LANE
- store_artifacts:
path: output
workflows:
build-test-adhoc:
jobs:
- build-and-test
- adhoc:
filters:
branches:
only: development
requires:
- build-and-test
The environment variable FL_OUTPUT_DIR
is the artifact directory where FastLane logs and signed .ipa
file should be stored. Use this to set the path in the store_artifacts
step to automatically save logs and build artifacts from Fastlane.
Code Signing with Fastlane Match
We recommend the use of Fastlane Match for signing your iOS applications as it simplifies and automates the process of code signing both locally and in the CircleCI environment.
For more information on how to get started with Fastlane Match, please see our iOS code signing documentation.
Using Ruby
Our macOS images contain multiple versions of Ruby. The default version in use on all images is the system Ruby. The images also include the latest stable versions of Ruby at the time that the image is built. We determine the stable versions of Ruby using the Ruby-Lang.org downloads page. The versions of Ruby that are installed in each image are listed in the software manifests of each container.
If you want to run steps with a version of Ruby that is listed as “available to chruby” in the manifest, then you can use chruby
to do so.
Note: Installing Gems with the system Ruby is not advised due to the restrictive permissions enforced on the system directories. As a general rule, we advise using one of the alternative Rubies provided by Chruby for all jobs.
Images using Xcode 11.7 and later
As a result of the macOS system Ruby (2.6.3) becoming increasingly incompatible with various gems (especially those which require native extensions), Xcode 11.7 and later images default to Ruby 2.7 via chruby
.
Defaulting to Ruby 2.7 allows for greater compatibility and reliability with gems moving forward. Common gems, such as Fastlane, run without any issues in Ruby 2.7.
To switch to another Ruby version, see our chruby documentation. To revert back to the system Ruby, add the following to the beginning of your job:
# ...
run:
name: Set Ruby Version
command: echo 'chruby system' >> ~/.bash_profile
Images using Xcode 11.2 and later
The chruby
program is installed on the image and can be used to select a version of Ruby. The auto-switching feature is not enabled by default. To select a version of Ruby to use, add the chruby
function to ~/.bash_profile
:
# ...
run:
name: Set Ruby Version
command: echo 'chruby ruby-2.6' >> ~/.bash_profile
Replace 2.6
with the version of Ruby required - you do not need to specify the full Ruby version, 2.6.5
for example, just the major version. This will ensure your config does not break when switching to newer images that might have slightly newer Ruby versions.
Images using Xcode 11.1 and earlier
Images using macOS 10.14 and earlier (Xcode 11.1 and earlier) have both chruby
and the auto-switcher enabled by default.
To specify a version of Ruby to use, there are two options. You can create a file named .ruby-version
and commit it to your repository, as documented by chruby
.
If you do not want to commit a .ruby-version
file to source control, then you can create the file from a job step:
# ...
run:
name: Set Ruby Version
command: echo "ruby-2.4" > ~/.ruby-version
Replace 2.4
with the version of Ruby required - you do not need to specify the full Ruby version, 2.4.9
for example, just the major version. This will ensure your config does not break when switching to newer images that might have slightly newer Ruby versions.
Installing additional Ruby versions
To run a job with a version of Ruby that is not pre-installed, you must install the required version of Ruby. We use the ruby-install tool to install the required version. After the install is complete, you can select it using the appropriate technique above.
Using Custom Versions of CocoaPods and Other Ruby Gems
To make sure the version of CocoaPods that you use locally is also used in your CircleCI builds, we suggest creating a Gemfile in your iOS project and adding the CocoaPods version to it:
source 'https://rubygems.org'
gem 'cocoapods', '= 1.3.0'
Then you can install these using bundler:
steps:
- restore_cache:
key: 1-gems-{{ checksum "Gemfile.lock" }}
- run: bundle check || bundle install --path vendor/bundle --clean
- save_cache:
key: 1-gems-{{ checksum "Gemfile.lock" }}
paths:
- vendor/bundle
You can then ensure you are using those, by prefixing commands with bundle exec
:
# ...
steps:
- run: bundle exec pod install
Using Homebrew
Homebrew is pre-installed on CircleCI, so you can simply use brew install
to add nearly any dependency you require to complete your build. For example:
# ...
steps:
- run:
name: Install cowsay
command: brew install cowsay
- run:
name: cowsay hi
command: cowsay Hi!
It is also possible to use the sudo
command if necessary to perform customizations outside of Homebrew.
Configuring deployment
After the app has been tested and signed, you are ready to configure deployment to your service of choice, such as App Store Connect or TestFlight. For more information on how to deploy to various services, including example Fastlane configurations, check out the deploying iOS apps guide
Reducing job time and best practises
Pre-starting the simulator
Pre-start the iOS simulator before building your application to make sure that the simulator is booted in time. Doing so generally reduces the number of simulator timeouts observed in builds.
To pre-start the simulator, add the following to your
config.yml
file, assuming that you are running your tests on an iPhone 11 Pro
simulator with iOS 13.2:
# ...
steps:
- run:
name: pre-start simulator
command: xcrun instruments -w "iPhone 11 Pro (13.3) [" || true
Note: the [
character is necessary to uniquely identify the iPhone 7
simulator, as the phone + watch simulator is also present in the build
image:
-
iPhone 11 Pro (13.3) [<uuid>]
for the iPhone simulator. -
iPhone 11 Pro (13.3) + Apple Watch Series 5 - 40mm (6.1.1) [<uuid>]
for the phone + watch pair.
Collecting iOS simulator crash reports
Often if your scan
step fails, for example due to a test runner timeout, it is likely that your app has crashed during the test run. In such cases, collecting crash report is useful for diagnosing the exact cause of the crash. Crash reports can be uploaded as artifacts, as follows:
steps:
# ...
- store_artifacts:
path: ~/Library/Logs/DiagnosticReports
Optimizing Fastlane
By default, Fastlane Scan generates test output reports in html
and junit
formats. If your tests are taking a long time and you do not need these reports, consider disabling them by altering the output_type
parameter as described in the fastlane docs.
Optimizing Cocoapods
In addition to the basic setup steps, it is best practice to use Cocoapods 1.8 or newer which allows the use of the CDN, rather than having to clone the entire Specs repo. This will allow you to install pods faster, reducing build times. If you are using Cocoapods 1.7 or older, consider upgrading to 1.8 or newer as this change allows for much faster job execution of the pod install
step.
To enable this, ensure the first line in your Podfile is as follows:
source 'https://cdn.cocoapods.org/'
If upgrading from Cocoapods 1.7 or older, additionally ensure the following line is removed from your Podfile, along with removing the “Fetch CocoaPods Specs” step in your CircleCI Configuration:
source 'https://github.com/CocoaPods/Specs.git'
To update Cocoapods to the latest stable version, simply update the Ruby gem with the following command:
sudo gem install cocoapods
We also recommend that you check your Pods directory into source control. This will ensure that you have a deterministic, reproducible build.
Note: The previous S3 mirror we provided for the Cocoapods Spec repo is no longer being maintained or updated since the release of Cocoapods 1.8. It will remain available to prevent existing jobs breaking, we highly recommend switching to the CDN method described above.
Optimizing Homebrew
Homebrew, by default, will check for updates at the start of any operation. As Homebrew has a fairly frequent release cycle, this means that any step which calls brew
can take some extra time to complete.
If build speed, or bugs introduced by new Homebrew updates are a concern, this automatic update feature can be disabled. On average, this can save up to 2-5 minutes per job.
To disable this feature, define the HOMEBREW_NO_AUTO_UPDATE
environment variable within your job:
version: 2.1
jobs:
build-and-test:
macos:
xcode: 11.3.0
environment:
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- checkout
- run: brew install wget
Supported build and test tools
With the macOS executor on CircleCI, it is possible to customize your build as needed to satisfy almost any iOS build and test strategy.
Common test tools
The following common test tools are known to work well on CircleCI:
React Native projects
React Native projects can be built on CircleCI using macos
and docker
executor types. For an example of configuring a React Native project, please see our demo React Native application
Creating a config.yml
File
The most flexible way to customize your build is to modify the CircleCI configuration for your project in .circleci/config.yml
. This allows you to run arbitrary bash commands as well as utilise built-in features such as workspaces and caching. See the Configuring CircleCI documentation for a detailed description of the structure of the config.yml
file.
Using Multiple Executor Types (macOS + Docker)
It is possible to use multiple executor types in the same workflow. In the following example each push of an iOS project will be built on macOS, and additional iOS tools (SwiftLint and Danger) will be run in Docker.
version: 2.1
jobs:
build-and-test:
macos:
xcode: 11.3.0
environment:
FL_OUTPUT_DIR: output
steps:
- checkout
- run:
name: Install CocoaPods
command: pod install --verbose
- run:
name: Build and run tests
command: fastlane scan
environment:
SCAN_DEVICE: iPhone 8
SCAN_SCHEME: WebTests
- store_test_results:
path: output/scan
- store_artifacts:
path: output
swiftlint:
docker:
- image: bytesguy/swiftlint:latest
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- run: swiftlint lint --reporter junit | tee result.xml
- store_artifacts:
path: result.xml
- store_test_results:
path: result.xml
danger:
docker:
- image: bytesguy/danger:latest
auth:
username: mydockerhub-user
password: $DOCKERHUB_PASSWORD # context / project UI env-var reference
steps:
- checkout
- run: danger
workflows:
build-test-lint:
jobs:
- swiftlint
- danger
- build-and-test
Troubleshooting
If you are facing build failures while executing your jobs, check out our support center knowledge base for answers to common issues.
See also
- See the
circleci-demo-ios
GitHub repository for a full example of how to build, test, sign and deploy an iOS project using Fastlane on CircleCI. - See the iOS Project Tutorial for a config walkthrough.
- See the iOS code signing documentation to learn how to configure Fastlane Match for your project.