Using Github Actions for Continuous Integration

Wikipedia: CI is intended to be used in combination with automated unit tests written through the practices of test-driven development

Some CI tools I’ve used in production apps in the past are BuddyBuild, Travis, and Bitrise.

Today we will be taking a look and using Github Actions as our CI. I knew Github Actions existed but never got to using it until starting the iOS Lead Essentials Program.

The main benefit we’re after here is being able to push our code to Github and having our automated tests run on the server and send us an email or showing a failing badge if any of our tests fail. But our goal is to always strive for green βœ…

Create an Xcode Framework

Create an Xcode Framework and name it DemoApp.

Ensure test and documentation are unchecked

Keep the test unchecked as we will create this explicitly next.

New Test Target

Create a new test target.

Add a Unit test class DemoAppTests to the Target.

Edit the DemoApp Scheme to add the newly added Test

Adding a System to be tested

In this demo we will create a file called ArrayProblems.swift and add a method called mergeArrays(). This will be the system under test, or the SUT.

public final class ArrayProblems {
    public init() {}
    
    public func mergeArrays(_ arr1: [Int], arr2: [Int]) -> [Int] {
        if arr1.isEmpty && arr2.isEmpty { return [] }

        var result = [Int]()
        var leftIndex = 0
        var rightIndex = 0

        while leftIndex < arr1.count && rightIndex < arr2.count {
            let leftElement = arr1[leftIndex]
            let rightElement = arr2[rightIndex]
            if leftElement < rightElement {
                result.append(leftElement)
                leftIndex += 1
            } else if leftElement > rightElement {
                result.append(rightElement)
                rightIndex += 1
            } else {
                result.append(leftElement)
                leftIndex += 1
                result.append(rightElement)
                rightIndex += 1
            }
        }

        if leftIndex < arr1.count {
            result.append(contentsOf: arr1[leftIndex...])
        }

        if rightIndex < arr2.count {
            result.append(contentsOf: arr2[rightIndex...])
        }
        
        return result
    }
}

Unit Tests

From the target, we created earlier create the following tests in the DemoAppTests.swift class:

import XCTest
import DemoApp

class DemoAppTests: XCTestCase {

    func test_mergeArrays_bothArraysAreEmpty() {
        let array1 = [Int]()
        let array2 = [Int]()

        let sut = ArrayProblems()

        let resultsArray = sut.mergeArrays(array1, arr2: array2)

        XCTAssertEqual(resultsArray, [])
    }

    func test_mergeArrays_bothArraysHaveValues() {
        let array1 = [1, 3, 4, 5]
        let array2 = [2, 4, 6, 8]

        let mergedArray = [1, 2, 3, 4, 4, 5, 6, 8]

        let sut = ArrayProblems()

        let receivedArray = sut.mergeArrays(array1, arr2: array2)

        XCTAssertEqual(mergedArray, receivedArray)
    }
}

Run all tests (command + U), and ensure all tests are passing βœ…. At this point push all your code to Github and we will move on to the heart of this article – adding Github Actions for continuous integration.

Adding Github Actions as our CI

Navigate to your Github repository and select Actions in the top menu as seen below. You should now be seeing a screen similar to screenshot. Actions does a great job of recognizing your file types and provides workflow suggestions. Here we see, Swift, Xcode…iOS.

Select the Configure option under the Swift workflow. You should now be viewing a YAML file similar to below:

name: Swift

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: macos-latest

    steps:
    - uses: actions/checkout@v2
    - name: Build
      run: swift build -v
    - name: Run tests
      run: swift test -v

We will be editing this file as seen in this iOS Essentials video to suit our particular Xcode build and testing needs.

Verify the following build and test command in a Terminal window prior to adding it to the YAML file below. Navigate to your project in Terminal and run this command, ensure your Project name is edited as needed if it’s different from the naming below:

xcodebuild clean build test -project DemoApp/DemoApp.xcodeproj -scheme "DemoApp" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO

If all tests succeeded from the command above, then we are good to continue with editing and committing the YAML file below on Github.

View and edit the YAML file below as per your project.

name: Github Action Demo CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: macos-latest

    steps:
    - uses: actions/checkout@v2
    
    - name: Select Xcode
      run: sudo xcode-select -switch /Applications/Xcode_13.2.1.app
    
    - name: Build and Test
      run: xcodebuild clean build test -project DemoApp/DemoApp.xcodeproj -scheme "DemoApp" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO

Once you are done editing the YAML file above commit. At this time Github will run the newly configured workflow. Click on Actions in the top menu to observe the workflow running all of the configured steps from the YAML file.

A final touch is adding a status badge to the repo’s README.

Navigate to Actions, select the workflow e.g Github Actions Demo CI, and the “…” menu button as seen below.

Select Create status badge and copy the status badge Markdown. Paste this Markdown to your README and you should now have a badge present on your page.

You can now enjoy pushing and testing more Data Structures and Algorithms to your repo if you so choose πŸ™ŒπŸΎ. Hope you enjoyed this tutorial on using Github Actions. Feel free to leave any comments or feedback. Happy coding!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s