Skip to main content
ClaudeWave
Skill932 repo starsupdated 13d ago

swift-testing

Swift Testing is the modern testing framework for Swift (Xcode 16+, Swift 6+) that replaces XCTest for unit tests using `@Test`, `@Suite`, `#expect`, `#require`, and trait modifiers. Use it when writing new unit tests, migrating from XCTest assertions, or deciding between test frameworks; reserve XCTest only for UI tests, performance benchmarks, and snapshot tests.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/dpearson2699/swift-ios-skills /tmp/swift-testing && cp -r /tmp/swift-testing/skills/swift-testing ~/.claude/skills/swift-testing
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Swift Testing

Swift Testing is the modern testing framework for Swift (Xcode 16+, Swift 6+). Prefer it for new unit tests. Keep XCTest where migration is still in progress, and use XCTest for UI automation, performance APIs, Objective-C exception tests, and common snapshot-test tooling.

## Contents

- [Basic Tests](#basic-tests)
- [`@Test Traits`](#test-traits)
- [#expect and #require](#expect-and-require)
- [`@Suite and Test Organization`](#suite-and-test-organization)
- [Execution Model](#execution-model)
- [XCTest Migration Boundaries](#xctest-migration-boundaries)
- [Known Issues](#known-issues)
- [Additional Patterns](#additional-patterns)
- [Common Mistakes](#common-mistakes)
- [Test Attachments](#test-attachments)
- [Exit Testing](#exit-testing)
- [Version-Gated APIs](#version-gated-apis)
- [Advanced API Review Checklist](#advanced-api-review-checklist)
- [Review Checklist](#review-checklist)
- [References](#references)

---

## Basic Tests

```swift
import Testing

@Test("User can update their display name")
func updateDisplayName() {
    var user = User(name: "Alice")
    user.name = "Bob"
    #expect(user.name == "Bob")
}
```

## `@Test` Traits

```swift
@Test("Validates email format")                                    // display name
@Test(.tags(.validation, .email))                                  // tags
@Test(.disabled("Server migration in progress"))                   // disabled
@Test(.enabled(if: ProcessInfo.processInfo.environment["CI"] != nil)) // conditional
@Test(.bug("https://github.com/org/repo/issues/42"))               // bug reference
@Test(.timeLimit(.minutes(1)))                                     // time limit
@Test("Timeout handling", .tags(.networking), .timeLimit(.seconds(30))) // combined
```

## #expect and #require

```swift
// #expect records failure but continues execution
#expect(result == 42)
#expect(name.isEmpty == false)
#expect(items.count > 0, "Items should not be empty")

// #expect with error type checking
#expect(throws: ValidationError.self) {
    try validate(email: "not-an-email")
}

// #expect with specific error value
#expect {
    try validate(email: "")
} throws: { error in
    guard let err = error as? ValidationError else { return false }
    return err == .empty
}

// #require records failure AND stops test (like XCTUnwrap)
let user = try #require(await fetchUser(id: 1))
#expect(user.name == "Alice")

// #require for optionals -- unwraps or fails
let first = try #require(items.first)
#expect(first.isValid)
```

**Rule: Use `#require` when subsequent assertions depend on the value. Use `#expect` for independent checks.**

## `@Suite` and Test Organization

See [references/testing-patterns.md](references/testing-patterns.md) for suite organization, confirmation patterns, known-issue handling, and execution-model details.

## Execution Model

Swift Testing runs tests in parallel by default. Do not assume test order, shared suite instances, or exclusive access to mutable state unless you explicitly design for it.

```swift
@Suite(.serialized)
struct KeychainTests {
    @Test func storesToken() throws { /* ... */ }
    @Test func deletesToken() throws { /* ... */ }
}
```

Use `.serialized` when a test or suite must run one-at-a-time because it touches shared external state. It does not make unrelated tests outside that scope run serially.

**Rules:**
- Each test must set up its own state.
- Shared mutable globals are a bug unless protected or intentionally serialized.
- `@Suite(.serialized)` is for exclusive execution, not for expressing logical ordering between tests.
- If tests depend on sequence, combine them into one test or move the sequence into shared helper code.

## XCTest Migration Boundaries

Swift Testing unit tests do not inherit from `XCTestCase`. Declare `@Test` on free functions, global functions, or methods on suite types such as `struct`, `class`, or `actor`; use `static` or `class` methods when instance fixtures are not needed.

When reviewing migration code or plans, do not collapse every XCTest construct into `#expect`. Include a compact assertion-mapping note or table in the answer so required unwraps and unconditional manual failures are not lost, even when the user only says "replace every XCTAssert with #expect."

State coexistence explicitly: XCTest and Swift Testing can coexist during migration. Keep UI automation, performance benchmarks, and common snapshot-test flows on XCTest/XCUITest or snapshot tooling, and separate files or targets when that makes runner expectations clearer.

For Xcode 27-era migrations, mention test framework interoperability when reviewing mixed helpers. Frame what changed: test plans created before Xcode 27 inherit `limited` mode, where cross-framework XCTest issues are warnings; new Xcode 27 projects use `complete` mode, where those issues remain errors. Xcode and SwiftPM can surface XCTest failures from Swift Testing tests and Swift Testing issues from XCTest tests depending on the configured interop mode (`limited`, `complete`, `strict`, or `none`). Prefer `complete` or `strict` while migrating helpers, use `SWIFT_TESTING_XCTEST_INTEROP_MODE` for SwiftPM when needed, and do not claim cross-framework APIs are categorically forbidden. Still prefer native Swift Testing APIs in new Swift Testing tests and convert helper failures to `Issue.record`, `#expect`, `#require`, or `Test.cancel` over time.

Migration defaults:
- `XCTAssert*` -> `#expect(...)`
- `XCTUnwrap` or any value required by later checks -> `try #require(...)`
- `XCTFail("...")` or manual unconditional issues -> `Issue.record("...")`
- UI tests, performance benchmarks, and common snapshot-test flows stay on XCTest/XCUITest or snapshot tooling.
- Put `@available` on individual `@Test` functions, not on suite types or their containing types.

```swift
let user = try #require(optionalUser)
#expect(user.isActive)

guard featureFlag.isEnabled else {
    Issue.record("Expected feature flag to be enabled")
    return
}
`
accessorysetupkitSkill

Discover and configure Bluetooth and Wi-Fi accessories using AccessorySetupKit. Use when presenting a privacy-preserving accessory picker, defining discovery descriptors for BLE or Wi-Fi devices, handling accessory session events, migrating from CoreBluetooth permission-based scanning, or setting up accessories without requiring broad Bluetooth permissions.

activitykitSkill

Implement, review, or improve Live Activities and Dynamic Island experiences in iOS apps using ActivityKit. Use when building real-time updating widgets for the Lock Screen and Dynamic Island — delivery tracking, sports scores, ride-sharing status, workout timers, media playback, or any time-sensitive information that updates in real time. Also use when working with ActivityKit, ActivityAttributes, Activity lifecycle (request/update/end), Dynamic Island layouts (compact/minimal/expanded), push-to-update Live Activities, or Lock Screen live widgets.

adattributionkitSkill

Measure ad effectiveness with privacy-preserving attribution using AdAttributionKit. Use when registering ad impressions, handling attribution postbacks, updating conversion values, implementing re-engagement attribution, configuring publisher or advertiser apps, or replacing SKAdNetwork with AdAttributionKit for ad measurement.

alarmkitSkill

Implement AlarmKit alarms and countdown timers for iOS and iPadOS with Lock Screen, Dynamic Island, StandBy, and paired Apple Watch system UI. Covers AlarmManager scheduling, AlarmAttributes and AlarmPresentation, AlarmButton stop and snooze actions, authorization, state observation, countdown widget-extension handoff, and Live Activity integration. Use when building wake-up alarms, countdown timers, or alarm-style alerts that need Apple's system alarm experience.

app-clipsSkill

Build iOS App Clips with invocation URLs, App Clip Codes, NFC, QR codes, Safari banners, Maps, Messages, target setup, App Store Connect experiences, size/capability constraints, NSUserActivity routing, SKOverlay promotion, App Group/keychain handoff, ephemeral notifications, location confirmation, and full-app migration. Use when creating App Clips or wiring App Clip invocation, experience configuration, or full-app handoff.

app-intentsSkill

Implement App Intents for Siri, Shortcuts, Spotlight, widgets, Control Center, and Apple Intelligence on iOS. Covers AppIntent actions, AppEntity and EntityQuery models, AppShortcutsProvider phrases, IndexedEntity Spotlight indexing, WidgetConfigurationIntent, SnippetIntent, and assistant schemas. Use when exposing app actions or entities to system surfaces.

app-store-optimizationSkill

Optimize App Store product pages for search visibility and conversion. Use for App Store Optimization (ASO), keyword research, app name/subtitle/keyword-field strategy, conversion-focused descriptions and promotional text, screenshot captions and ordering, Custom Product Pages with assigned search keywords, In-App Events, Product Page Optimization tests, localized metadata, ratings/review strategy, and in-app review prompt timing with RequestReviewAction or AppStore.requestReview. Also use when routing ASO vs App Store review, privacy/ATT, or StoreKit implementation boundaries.

app-store-reviewSkill

Prepare for App Store review and prevent rejections. Covers App Store review guidelines, app rejection reasons, PrivacyInfo.xcprivacy privacy manifest requirements, required API reason codes, in-app purchase IAP and StoreKit rules, App Store Guidelines compliance, ATT App Tracking Transparency, EU DMA Digital Markets Act, HIG compliance checklist, app submission preparation, review preparation, metadata requirements, entitlements, widgets, and Live Activities review rules. Use when preparing for App Store submission, fixing rejection reasons, auditing privacy manifests, implementing ATT consent flow, configuring StoreKit IAP, or checking HIG compliance.