CI/CD for Swift Projects: GitHub Actions Advanced Level

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Advanced CI/CD for Swift Projects: Optimizing Pipelines with GitHub Actions



Automation of building, testing, and deployment is an integral part of modern iOS and macOS development. Basic CI/CD pipelines solve simple tasks, but real-world projects with Swift, Swift Package Manager (SPM), multiple targets, and complex dependencies require an advanced approach. In this article, we will explore how to take your CI/CD process to the next level using GitHub Actions, leveraging matrix builds, caching optimization, parallel testing, and automatic deployment to TestFlight / App Store.



1. Matrix Builds for Multi-Platform Support



One of the key features of GitHub Actions is the matrix strategy. It allows you to run the same workflow with different parameters (Xcode version, build scheme, platform). This is especially useful for Swift projects that support iOS, macOS, tvOS, and watchOS.



Example matrix configuration for a Swift project:



jobs:  build:    strategy:      matrix:        platform: [iOS, macOS, tvOS]        xcode-version: ['15.0', '15.2']        exclude:          - platform: tvOS            xcode-version: '15.0'  # Exclude incompatible combination    runs-on: macos-14    steps:      - uses: actions/checkout@v4      - name: Select Xcode ${{ matrix.xcode-version }}        run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode-version }}.app      - name: Build for ${{ matrix.platform }}        run: |          xcodebuild clean build \            -scheme MyApp \            -destination "platform=${{ matrix.platform }}" \            CODE_SIGN_IDENTITY="" \            CODE_SIGNING_REQUIRED=NO


This approach allows you to verify the build on all supported platforms and Xcode versions with a single push, saving developer time.



2. Advanced Caching: Speed Up Builds by 2-3 Times



Building Swift projects is a resource-intensive process. Caching SPM dependencies and DerivedData can reduce pipeline time by 50-70%. Instead of a basic cache, use custom keys that account for dependency versions.



Optimized cache for SPM and Xcode:



- name: Cache SPM  uses: actions/cache@v3  with:    path: |      .build      SourcePackages    key: ${{ runner.os }}-spm-${{ hashFiles('**/Package.resolved') }}    restore-keys: |      ${{ runner.os }}-spm-

- name: Cache DerivedData uses: actions/cache@v3 with: path: ~/Library/Developer/Xcode/DerivedData key: ${{ runner.os }}-derived-${{ hashFiles('**/*.swift') }} restore-keys: | ${{ runner.os }}-derived-


Important: Use hashFiles('**/Package.resolved') for precise cache invalidation when dependencies change. For DerivedData, the key can be the hash of all Swift files — this ensures the cache is updated only when there are actual code changes.



3. Parallel Testing and Reporting



For large Swift projects with hundreds of unit tests, sequential execution can take 20-30 minutes. The solution is splitting tests into groups and running them in parallel on multiple runners.



Implementing parallel testing:



test:  strategy:    matrix:      test-group: [1, 2, 3, 4]  runs-on: macos-14  steps:    - uses: actions/checkout@v4    - name: Run tests (Group ${{ matrix.test-group }})      run: |        xcodebuild test \          -scheme MyApp \          -destination "platform=iOS Simulator,name=iPhone 15 Pro" \          -only-testing:MyAppTests/TestGroup${{ matrix.test-group }} \          CODE_SIGN_IDENTITY="" \          CODE_SIGNING_REQUIRED=NO    - name: Upload test results      if: always()      uses: actions/upload-artifact@v4      with:                

Blogs

Book Recommendations