GitHub Actions + XCTest + Pull Request Workflow for iOS

GitHub Actions + XCTest + Pull Request Workflow for iOS

Modern iOS development is not just writing Swift code. A production workflow usually includes automated builds, automated testing, Pull Requests, and CI pipelines that validate changes before merging them into the main branch.

This note walks through the mental model behind GitHub Actions, XCTest, Xcode schemes, test plans, and Pull Request workflows for iOS projects.

1. Why CI Exists

Imagine a team where every engineer directly pushes code into main.

Problems appear quickly:

1
2
3
4
One engineer breaks the build.
Another engineer introduces failing tests.
Someone commits code that only works locally.
A deployment accidentally ships broken functionality.

Continuous Integration (CI) exists to reduce these problems automatically.

The core idea:

1
Every code change should be validated automatically.

Usually this means:

1
2
3
Build the project
Run tests
Report pass/fail status

before code is merged into the main branch.

2. The High-Level Workflow

A modern Git workflow usually looks like this:

1
2
3
4
5
main

Pull Request

feature branch

Instead of committing directly to main, developers create feature branches.

For example:

1
git checkout -b feature/profile-screen

The engineer develops on this branch:

1
2
3
write code
commit
push

Then opens a Pull Request:

1
2
3
feature/profile-screen

main

The Pull Request becomes the review and validation layer.

3. What Happens During a Pull Request

When a Pull Request is opened, GitHub Actions can automatically run CI workflows.

Typical iOS CI steps:

1
2
3
4
5
Checkout repository
Select macOS runner
Build project with xcodebuild
Run XCTest unit tests
Report success or failure

If the workflow fails:

1
The Pull Request should not be merged.

This creates a safety gate around the main branch.

4. GitHub Actions Mental Model

GitHub Actions is event-driven automation.

A workflow is usually stored here:

1
.github/workflows/swift.yml

Example:

1
2
3
4
5
6
7
8
9
10
name: Swift CI

on:
push:
branches:
- main

pull_request:
branches:
- main

This means:

1
2
3
Run the workflow when:
- code is pushed to main
- a Pull Request targets main

The workflow defines jobs:

1
2
3
jobs:
build-and-test:
runs-on: macos-15

Important detail:

1
iOS projects require macOS runners because Xcode only runs on macOS.

5. xcodebuild

Most iOS CI systems eventually use:

1
xcodebuild

Example:

1
2
3
xcodebuild build \
-project "MyApp.xcodeproj" \
-scheme "MyApp"

This compiles the application.

Testing uses:

1
2
3
4
xcodebuild test \
-project "MyApp.xcodeproj" \
-scheme "MyApp" \
-destination 'platform=macOS'

Important distinction:

Command Purpose
build Compile the project
test Compile + execute XCTest

6. XCTest

XCTest is Apple’s native testing framework.

Simple example:

1
2
3
4
5
6
7
8
import XCTest

final class MathTests: XCTestCase {

func testAdd() {
XCTAssertEqual(addNumbers(1, 2), 3)
}
}

The key idea:

1
A test defines an expectation about code behavior.

If the expectation fails:

1
CI fails.

This allows automated validation without manual checking.

7. Shared Schemes

One common iOS CI issue:

1
The project builds locally but fails on GitHub Actions.

Often the problem is:

1
The scheme is not shared.

GitHub Actions only sees:

1
xcshareddata/xcschemes

not local user-specific schemes:

1
xcuserdata

The scheme must:

1
2
3
Include the test target
Be marked as Shared
Be committed to git

Otherwise:

1
xcodebuild: error: Scheme is not configured for the test action

appears.

8. Test Plans

Modern Xcode versions increasingly use Test Plans.

A test plan defines:

1
2
3
4
5
Which tests run
Parallel execution behavior
Configurations
Filtering
Environment settings

Instead of directly attaching test targets to schemes, the flow is now often:

1
2
3
4
5
6
7
Scheme

Test Plan

Test Targets

Test Methods

This is why newer Xcode projects may contain:

1
.xctestplan

files.

9. Deployment Target Problems

Another common CI issue:

1
Tests cannot run because deployment target is too high.

Example:

1
2
CI runner macOS version: 15.x
Project deployment target: 26.x

The fix:

1
2
3
Lower the deployment target
or
Use a newer CI runner

This happens because GitHub Actions runners may not match the newest local Xcode/macOS environment.

10. Why Pull Requests Matter

Pull Requests are not just for merging code.

They provide:

1
2
3
4
5
Code review
Automated validation
Team discussion
Change history
Safety around main

Typical workflow:

1
2
3
4
5
6
7
8
9
feature branch

Pull Request

CI runs

Code review

Merge

Most modern engineering teams avoid directly pushing to main.

11. CI Failure as Feedback

One useful mental model:

1
CI is an automated reviewer.

It continuously checks:

1
2
3
Does the project build?
Do tests pass?
Did this change break anything obvious?

A failing CI pipeline is not necessarily bad.

Often it simply means:

1
The system detected a problem early.

which is the entire purpose of CI.

12. Final Mental Model

GitHub Actions is not specifically an iOS technology.

It is:

1
event-driven automation

combined with:

1
2
3
xcodebuild
XCTest
Git workflows

A modern iOS workflow usually becomes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Write code

Commit

Push branch

Open Pull Request

GitHub Actions runs CI

Build + Test

Review

Merge

The goal is not just automation.

The goal is maintaining code quality as teams and projects grow.


GitHub Actions + XCTest + Pull Request Workflow for iOS
http://runningcoconut.com/2026/05/09/GitHub-Actions-XCTest-Pull-Request-Workflow-for-iOS/
Author
Huajing Lu
Posted on
May 9, 2026
Licensed under