Automating iOS CI/CD with GitHub Actions
Manual app building and distribution is error-prone and time-consuming. In this post, I share the GitHub Actions workflows I use for my iOS projects.
Basic Build and Test Workflow
1name: iOS CI
2
3on:
4 push:
5 branches: [main, develop]
6 pull_request:
7 branches: [main]
8
9jobs:
10 build-and-test:
11 runs-on: macos-14
12 steps:
13 - uses: actions/checkout@v4
14
15 - name: Select Xcode
16 run: sudo xcode-select -s /Applications/Xcode_15.2.app
17
18 - name: Cache SPM
19 uses: actions/cache@v4
20 with:
21 path: .build
22 key: spm-${{ hashFiles('Package.resolved') }}
23
24 - name: Build
25 run: |
26 xcodebuild build \
27 -scheme MyApp \
28 -destination 'platform=iOS Simulator,name=iPhone 15' \
29 -skipMacroValidation
30
31 - name: Test
32 run: |
33 xcodebuild test \
34 -scheme MyApp \
35 -destination 'platform=iOS Simulator,name=iPhone 15' \
36 -resultBundlePath TestResults \
37 -skipMacroValidationCaching for Faster Builds
SPM dependency caching can save 2-5 minutes per build:
1- name: Cache DerivedData
2 uses: actions/cache@v4
3 with:
4 path: ~/Library/Developer/Xcode/DerivedData
5 key: derived-data-${{ hashFiles('**/*.swift') }}
6 restore-keys: derived-data-SwiftLint Integration
Enforce code style automatically:
1- name: SwiftLint
2 run: |
3 if which swiftlint > /dev/null; then
4 swiftlint lint --reporter github-actions-logging
5 else
6 brew install swiftlint
7 swiftlint lint --reporter github-actions-logging
8 fiTestFlight Distribution
Automate TestFlight uploads on tagged releases:
1deploy:
2 runs-on: macos-14
3 needs: build-and-test
4 if: startsWith(github.ref, 'refs/tags/v')
5 steps:
6 - uses: actions/checkout@v4
7
8 - name: Install Certificates
9 env:
10 P12_BASE64: ${{ secrets.P12_CERTIFICATE }}
11 P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
12 run: |
13 echo $P12_BASE64 | base64 --decode > certificate.p12
14 security create-keychain -p "" build.keychain
15 security import certificate.p12 -k build.keychain -P "$P12_PASSWORD"
16
17 - name: Archive
18 run: |
19 xcodebuild archive \
20 -scheme MyApp \
21 -archivePath MyApp.xcarchive \
22 -destination generic/platform=iOS
23
24 - name: Export IPA
25 run: |
26 xcodebuild -exportArchive \
27 -archivePath MyApp.xcarchive \
28 -exportPath export \
29 -exportOptionsPlist ExportOptions.plist
30
31 - name: Upload to TestFlight
32 env:
33 APP_STORE_CONNECT_KEY: ${{ secrets.ASC_KEY }}
34 run: |
35 xcrun altool --upload-app \
36 -f export/MyApp.ipa \
37 --type ios \
38 --apiKey $APP_STORE_CONNECT_KEYMy GeminiAction Project
I built GeminiAction↗ to explore integrating GitHub Actions with AI capabilities. The project demonstrates how to use Google Gemini AI within CI/CD workflows for automated code review and documentation generation.
Workflow Tips
- Use
macos-14for Apple Silicon runners (faster) - Cache aggressively — SPM, DerivedData, CocoaPods
- Fail fast — run linting before building
- Use matrix strategy for testing across iOS versions
- Secure secrets — never hardcode certificates or API keys
Conclusion
GitHub Actions has become my go-to for iOS CI/CD. The macOS runners are reliable, the ecosystem of actions is rich, and the integration with GitHub is seamless. Set it up once, and every push is automatically built, tested, and optionally deployed.