CI/CD Setup for Mobile Applications via CircleCI
CircleCI is cloud CI with some of the best macOS runners available among SaaS solutions. For iOS development, speed matters: m2pro.medium executor builds average Swift project in 6–8 minutes. Configuration via .circleci/config.yml, resources stored in CircleCI cloud without managing own servers.
iOS Configuration
version: 2.1
orbs:
ruby: circleci/[email protected]
executors:
ios-executor:
macos:
xcode: "16.0.0"
resource_class: m2pro.medium
environment:
FASTLANE_SKIP_UPDATE_CHECK: "true"
BUNDLE_PATH: vendor/bundle
jobs:
ios-test:
executor: ios-executor
steps:
- checkout
- ruby/install-deps:
app-dir: "."
bundler-version: "2.4.22"
- restore_cache:
keys:
- pods-v2-{{ checksum "Podfile.lock" }}
- run: bundle exec pod install
- save_cache:
key: pods-v2-{{ checksum "Podfile.lock" }}
paths: [Pods, vendor/bundle]
- run:
name: Run tests
command: bundle exec fastlane test
- store_test_results:
path: fastlane/test_output
- store_artifacts:
path: fastlane/test_output
destination: test-results
ios-deploy:
executor: ios-executor
steps:
- checkout
- ruby/install-deps
- restore_cache:
keys:
- pods-v2-{{ checksum "Podfile.lock" }}
- run: bundle exec pod install
- run:
name: Deploy to TestFlight
command: bundle exec fastlane release
environment:
MATCH_PASSWORD: ${MATCH_PASSWORD}
ASC_API_KEY_ID: ${ASC_API_KEY_ID}
ASC_API_KEY_ISSUER: ${ASC_API_KEY_ISSUER}
workflows:
ios-workflow:
jobs:
- ios-test:
filters:
branches:
only: [main, develop, /feature\/.*/]
- ios-deploy:
requires: [ios-test]
filters:
branches:
only: main
Code Signing: Two Approaches
fastlane match (recommended): secret MATCH_PASSWORD added in CircleCI Project Settings → Environment Variables. Each run fastlane match adhoc --readonly fetches profile from Git repository.
CircleCI Contexts: export common variables (MATCH_PASSWORD, Firebase Token) in Context—group of variables shared between projects. Useful for teams with multiple iOS applications.
jobs:
ios-deploy:
context:
- mobile-signing-context
Android in Same config.yml
android-test:
docker:
- image: cimg/android:2024.01
resource_class: large
steps:
- checkout
- restore_cache:
keys:
- gradle-v1-{{ checksum "app/build.gradle" }}
- run:
name: Build and test
command: ./gradlew test assembleRelease
- save_cache:
key: gradle-v1-{{ checksum "app/build.gradle" }}
paths:
- ~/.gradle/caches
- ~/.gradle/wrapper
- store_artifacts:
path: app/build/outputs/apk/release
Docker image cimg/android:2024.01—official CircleCI Android image with pre-installed Android SDK, JDK 17, Gradle. resource_class: large (4 CPU, 8 GB RAM) needed for Gradle—with medium (2 CPU), build time doubles.
Test Splitting for Long Test Suites
CircleCI supports parallel test execution with automatic distribution by previous run timings:
ios-test-parallel:
executor: ios-executor
parallelism: 4
steps:
- checkout
- run: bundle exec pod install
- run:
name: Run tests with splitting
command: |
TESTS=$(circleci tests glob "**/*Tests.swift" | circleci tests split --split-by=timings)
bundle exec fastlane scan --only_testing "$TESTS"
4 parallel containers, each runs ¼ of tests by historical execution time. Total time for 400-test suite reduces from 18 to 6 minutes.
CircleCI iOS Limitations
- No real device access out of box (simulators only). For device testing—Firebase Test Lab integration via CLI
- macOS minute pricing significantly higher than Linux: m2pro.medium ~$0.02/min. With active development—important to limit workflows via triggers
Timeline
Basic CircleCI configuration for iOS (test + deploy): 2–3 days. Full setup with Android, test splitting, Contexts, Slack notifications: 1–1.5 weeks. Cost calculated individually.







