Build, Test, & Deploy an iOS App with Github Actions (Part1)

In his article, you'll learn how to automate the process of deploying your builds to testers using TestFlight.

The Goal

We want to:

  1. automatically run Unit Tests for each pull request
  2. deploy a new build to TestFlight whenever a pull request is merged

Initial setup

Now, let's go to Visual Studio Code and create our first workflow YAML file:

Create .github/workflows/pullRequest.yml file. I will just copy and paste the following to save time, but we'll go over it:

name: Pull Request

on:
  pull_request:
    branches:
      - main
  workflow_dispatch:

jobs:
  test:
    runs-on: macos-11
    steps:
      - uses: actions/checkout@v2

      - name: Cancel Previous Runs
        uses: styfle/cancel-workflow-action@0.9.1
        with:
          access_token: ${{ github.token }}

      - name: Swift Packages Cache
        uses: actions/cache@v2
        id: cache
        with:
          path: |
            Build/SourcePackages
            Build/Build/Products
          key: ${{ runner.os }}-deps-v1-${{ hashFiles('BILDsolid.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}
          restore-keys: ${{ runner.os }}-deps-v1-
 
      - name: Select Xcode
        run: sudo xcode-select -switch /Applications/Xcode_13.0.app && /usr/bin/xcodebuild -version

      - name: Run tests
        run: xcodebuild test -scheme MyProjectName -project MyProjectName.xcodeproj -destination 'platform=iOS Simulator,name=iPhone 12,OS=15.0' | xcpretty && exit ${PIPESTATUS[0]}
        

on: We describe when we want to run the workflow, or what will trigger it. In our case, it's a pull request against the main branch. workflow_dispatch means that we want to be able to trigger the workflow manually on Github.

jobs: Now we actually start to describe what we want to do. We have the first and the only job called test (can be named anything else). runs-on: macos-11 - Self-explanatory.

steps: Each job can have many of them.

actions/checkout@v2 - pull the repository code to the machine that was given to us for this run. We start with a clean state.

cancel-workflow-action - basically if we already running the same workflow from the same branch, we want to first cancel the old one, and only then start the new execution. We discard the result of the previous workflow.

We can actually pass values or variables as a parameter to the step with the keyword.

access_token: ${{ github.token }}

You see, github.token is actually a variable here. How do we know that it's available? - Documentation.

actions/cache@v2 - we want to cache SPM modules to not fetch them again and again.

Then, we select the Xcode version (in our case 13.0) that allows us to test the app with the desired iOS version (in our case 15.0). That's it. Now we can push and see how it works, but the most important part is missing - the unit tests.

You can use action’s matrix feature to run a job with multiple parameters. The workflow below will run two jobs: one with iOS version 15.2 and the second with version 15.0. As before, we also need to specify the appropriate Xcode version.

name: tests

on: [push]

jobs:
  run_tests:
    runs-on: macos-11
    strategy:
      matrix:
        include:
          - xcode: "13.2"
            ios: "15.2"
          - xcode: "13.1"
            ios: "15.0"
    name: test iOS (${{ matrix.ios }})
    steps:
    - uses: actions/checkout@v1
    - name: Select Xcode
      run: sudo xcode-select -switch /Applications/Xcode_$.app && /usr/bin/xcodebuild -version
    - name: Run unit tests
      run: xcodebuild test -scheme BudgetOnTheGo -project BudgetOnTheGo.xcodeproj -destination 'platform=iOS Simulator,name=iPhone 12,OS=$' | xcpretty && exit ${PIPESTATUS[0]}        

Congrats! 🥳

To view or add a comment, sign in

More articles by AbdelAli J.

  • Which property wrapper for what purpose?

    In SwiftUI property wrappers are often overused even though they are powerful tools. A key principle is to use regular…

  • Leave your ego behind

    Ego is our sense of self-esteem that helps shape our identity and life goals. However an unchecked ego can lead to…

  • How I Interview Developers: A Simple Approach

    When I interview a developer I focus on three important questions: Are they smart? Can they get things done? Can We…

    1 Comment
  • The Power of Naming Conventions

    When SwiftUI 2 was released our team eagerly adopted it for our project. We jumped right in and enjoyed the excitement…

  • Understanding SwiftUI’s Observation Framework

    In SwiftUI observation tools like @Published and ObservableObject weren’t perfect. They often made us either watch too…

  • Swift Enums tips and tricks

    Swift enums are perhaps one of the most interesting features of the language. From defining a finite list of cases, to…

    1 Comment
  • Why tests?

    Adding tests to your app provides a way to ensure that our app does what we expect of it. And not only do tests check…

  • Encapsulate new SwiftUI APIs

    Apple’s SwiftUI framework is progressing at a rapid pace and as we support older versions of the operating system the…

  • How to handle errors in Swift

    Good error handling is one of those factors that distinguishes good applications from bad ones, yet we often tend to…

  • Unowned or Weak?

    When dealing with retain cycles we generally choose between unowned or weak attribute, but sometimes we could be still…

Others also viewed

Explore content categories