iOS Continuous Integration with Xcode
This is a guide to continuous integration for iOS applications created with Xcode. Semaphore supports building, testing and deploying Swift, Objective-C and React Native projects via macOS Mojave VM image and a1-standard machine types.
Table of contents
- Demo project
- Overview of the CI pipeline
- Sample configuration
- Configuration walkthrough
- Run the demo project yourself
Semaphore maintains an example iOS Swift project:
In the repository you will find an annotated Semaphore configuration file
The application uses Swift, Xcode and Fastlane with the Semaphore plugin.
Overview of the CI pipeline
The Semaphore pipeline is configured to:
- Run application tests;
- Build the app using gym, which would generate an
ipafile signed by your developer certificate.
You can extend the example pipeline to run additional tasks and configuring beta and release deployment.
The following configuration is used in the provided demo project:
# .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 - bundle install --path vendor/bundle - cache store 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.2 # 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 - bundle install --path vendor/bundle - cache store jobs: - name: Fastlane build commands: - bundle exec xcversion select 10.2 # Gym builds and packages iOS apps. # See https://docs.fastlane.tools/actions/build_app/ - bundle exec fastlane build
Naming 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.
version: v1.0 name: Semaphore iOS example
Defining the agent
An agent defines the environment in which your code runs. It is a combination of one of available machine types and operating system images.
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 many parallel jobs. Jobs define the commands to execute. Blocks, tasks and jobs are Semaphore's core concepts.
blocks: - name: Run tests task: env_vars: # This environment variable is exported in every job within this block: - name: LANG value: en_US.UTF-8 prologue: commands: - # this runs before every parallel job jobs: - name: Fastlane test commands: - # ... - name: 2nd parallel job commands: - # ... - name: 3rd parallel job commands: - # ... - name: Build app task: jobs: - name: Fastlane app commands: - # ...
By default this performs a shallow git clone from remote origin. If you want a full clone which will be cached by Semaphore, use
commands: - checkout
Semaphore can install dependencies from any supported language. Using the versatile cache tool, you can save time by reusing them across blocks and workflows.
commands: - cache restore - bundle install --path vendor/bundle - cache store
In example configuration, the first command restores dependencies from cache using partial cache key matching:
- Ideal scenario is to match
gems-$SEMAPHORE_GIT_BRANCH-$(checksum Gemfile.lock). This means that we've found dependencies from the current Git branch, and current content of dependencies' lock file.
$SEMAPHORE_GIT_BRANCHis one of the environment variables provided by Semaphore in every job.
checksumis a script that returns MD5 sum of provided file.
- The second best option is to find any previous version of dependencies for the current Git branch, using
- Last option is to reuse the last available cache from the master branch.
cache restore command will not fail in case of a cache miss. In case of a cache hit,
bundle install will complete in about a second.
After installing the dependencies, we store them in cache for reuse in upcoming blocks and future workflows.
Selecting Xcode version
You can find the list of currently available versions of Xcode in the macOS Mojave image reference.
commands: - bundle exec xcversion select 10.2
Semaphore can run tests of iOS and Mac apps on a simulator or connected device. If you're using Fastlane, see documentation regarding fastlane scan.
commands: - bundle exec fastlane test
Building your app
We build our app by running
fastlane build. The Semaphore Fastlane plugin ensures that by default the process runs smoothly with a temporary keychain.
commands: - bundle exec fastlane build
The example is using the Semaphore Fastlane plugin:
# fastlane/Fastfile 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 "Build" desc "Build without code sign. Just to see if the build is working" lane :build do |options| gym( scheme: "HelloWorld", skip_package_ipa: true, skip_archive: true, silent: true, clean: true ) end end
Releasing your app
To manage your developer credentials on Semaphore, use encrypted secrets.
To manage releases, set up promotions to trigger additional pipelines.
Run the demo project yourself
A good way to start using Semaphore is to take a demo project and run it yourself. Here’s how to build the demo project with your own account:
- Fork the project on GitHub to your own account.
- Clone the repository on your local machine.
- In Semaphore, follow the link in the sidebar to create a new project.
- Create your secret as per instructions above.
- Edit any file and push GitHub, and Semaphore will run the CI/CD pipeline.