CI covers the Go half only. The iOS suite -- 112 SpamFilterKit tests, 16 app tests, 6 XCUITests -- runs nowhere but a maintainer laptop, so an iOS PR cannot be validated before a human builds it.
This was deliberately left out of the CI work so it could have its own debugging budget, because it is genuinely fiddly rather than merely unwritten.
The specific hazard
The local make test-ios broke once already in a way worth understanding before you start. Xcode updated to 26.6, which ships SDK 26.5 — but the matching iOS 26.5 simulator runtime was not installed. Installed runtimes were 18.4, 26.0, and 26.2. Result:
xcodebuild: error: Unable to find a destination matching the provided destination specifier
... error:iOS 26.5 is not installed.
xcrun simctl list devices available happily listed iPhones, so the failure looked like a simulator-selection bug. It was not. The fix was xcodebuild -downloadPlatform iOS, an 8.5 GB download.
On a hosted macOS runner you inherit whatever Xcode/runtime pairing the image ships. So the job needs to either pin an Xcode version whose runtime is present, or download the runtime (slow, and cache it), or resolve a destination from the runtimes actually installed rather than assuming.
What to do
runs-on: macos-14 (or newer), xcodegen generate, then xcodebuild test
- Resolve the simulator from installed runtimes rather than hardcoding a device name.
Makefile's test-ios target already does this and explains why in comments — reuse that logic rather than reinventing it.
CODE_SIGNING_ALLOWED=NO — the suite does not need signing
- Expect a slow job. Consider running it only on PRs touching
ios/, via paths:
- macOS runners cost roughly 10x Linux minutes; worth stating the tradeoff in the PR
SpamFilterKitTests/IntegrationTests self-skip via XCTSkip unless SPAMFILTER_INTEGRATION_BASE_URL is set, so they will not fail a CI run with no backend.
CI covers the Go half only. The iOS suite -- 112 SpamFilterKit tests, 16 app tests, 6 XCUITests -- runs nowhere but a maintainer laptop, so an iOS PR cannot be validated before a human builds it.
This was deliberately left out of the CI work so it could have its own debugging budget, because it is genuinely fiddly rather than merely unwritten.
The specific hazard
The local
make test-iosbroke once already in a way worth understanding before you start. Xcode updated to 26.6, which ships SDK 26.5 — but the matching iOS 26.5 simulator runtime was not installed. Installed runtimes were 18.4, 26.0, and 26.2. Result:xcrun simctl list devices availablehappily listed iPhones, so the failure looked like a simulator-selection bug. It was not. The fix wasxcodebuild -downloadPlatform iOS, an 8.5 GB download.On a hosted macOS runner you inherit whatever Xcode/runtime pairing the image ships. So the job needs to either pin an Xcode version whose runtime is present, or download the runtime (slow, and cache it), or resolve a destination from the runtimes actually installed rather than assuming.
What to do
runs-on: macos-14(or newer),xcodegen generate, thenxcodebuild testMakefile'stest-iostarget already does this and explains why in comments — reuse that logic rather than reinventing it.CODE_SIGNING_ALLOWED=NO— the suite does not need signingios/, viapaths:SpamFilterKitTests/IntegrationTestsself-skip viaXCTSkipunlessSPAMFILTER_INTEGRATION_BASE_URLis set, so they will not fail a CI run with no backend.