AGENT SKILL

swift-lang

Swift 6 language and tooling - concurrency model, Sendable, actors, replacing DispatchQueue and completion handlers with async/await, XCTest basics, and xcodebuild. Use for Swift code that is not a SwiftUI view. For SwiftUI architecture and view-layer testing, see `swiftui-lang`.

使用场景测试与质量
License 许可
MIT
所属仓库
eyelock/assistants
最近更新
2026-11-14
Stars
★ 1
Forks
0

01 前言 Front Matter

name
swift-lang
description
Swift 6 language and tooling - concurrency model, Sendable, actors, replacing DispatchQueue and completion handlers with async/await, XCTest basics, and xcodebuild. Use for Swift code that is not a SwiftUI view. For SwiftUI architecture and view-layer testing, see `swiftui-lang`.

02 资源文件 2

03 SKILL.md 正文

Swift Development

For SwiftUI-specific architecture (ViewModel extraction, DI, ViewInspector, snapshot testing, coverage targets by layer), load the swiftui-lang skill.

Swift 6 Concurrency

Swift 6 enforces data isolation at compile time. Every type must be either Sendable or confined to an actor.

Actors isolate mutable state:

actor MyService {
    private var cache: [String: Data] = [:]

    func store(_ data: Data, for key: String) {
        cache[key] = data
    }

    func fetch(key: String) -> Data? {
        cache[key]
    }
}

Callers outside the actor await its methods; the hop onto the actor makes them asynchronous without marking them async.

@MainActor for UI-bound types:

@MainActor
final class ViewModel: ObservableObject {
    @Published var items: [Item] = []
}

Sendable for types crossing actor boundaries — must be value types or immutable:

struct Config: Sendable {
    let baseURL: URL
    let timeout: TimeInterval
}

Rules:

  • Prefer value types (struct) over reference types (class) for Sendable conformance
  • Mark @Observable types as @MainActor when they drive UI
  • Use async/await — avoid DispatchQueue and callback chains
  • Never use nonisolated(unsafe) without a clear documented reason

Enforce strict concurrency at compile time

Manual review cannot catch every missed @Sendable on a closure handed to an Apple SDK that dispatches on a background queue. The only durable enforcement is the build flag:

// Package.swift
.target(
    name: "App",
    swiftSettings: [.enableExperimentalFeature("StrictConcurrency=complete")]
)

Until the project compiles cleanly under StrictConcurrency=complete, treat every closure literal written inside a @MainActor scope as a latent EXC_BREAKPOINT — see swiftui-lang for the per-call-site rule.

Pick one async primitive

DispatchQueue.main.asyncAfter, Task.sleep(nanoseconds:), Timer.scheduledTimer, and bare GCD coexisting in the same codebase is a smell. Default to async/await; reach for Timer only for periodic UI ticks. New code should not introduce GCD.

Task.sleep used to wait for "something to be ready" (connect, callback drain, settle) is a race-condition workaround, not coordination. It will go flaky under load and mask the next race. Replace with an explicit signal: a continuation, an AsyncStream, or a state observer.

Error handling — try? is a regression amplifier

try? converts loud failures into silent ones. The user sees "nothing happened" and there is nothing to debug. Reserve it for cases where the failure genuinely has no consequence (e.g. best-effort cache prime). For anything user-visible — git, subprocess, filesystem, network — surface the error:

// Bad — silent
let branches = try? await git.listBranches()

// Good — log and propagate or present
do {
    let branches = try await git.listBranches()
    ...
} catch {
    logger.error("git listBranches failed: \(error)")
    throw error  // or surface to the user
}

Testing with XCTest

final class MyServiceTests: XCTestCase {
    func testFetch_AfterStore_ReturnsStoredData() async {
        let service = MyService()
        let data = Data("value".utf8)
        await service.store(data, for: "key")

        let result = await service.fetch(key: "key")

        XCTAssertEqual(result, data)
    }
}

Rules:

  • Test names describe the scenario: test<Subject>_<Condition>_<ExpectedResult>
  • Async tests are async (add throws when the code under test throws) — no XCTestExpectation for async work
  • Create shared fixtures in setUp() and release them in tearDown(), not in property initializers
  • Test at the unit boundary — mock only at system edges (network, filesystem)

xcodebuild Tooling

Prefer make targets over direct xcodebuild invocations. Projects expose:

make build         # xcodebuild build
make test          # xcodebuild test
make lint          # SwiftLint
make format        # SwiftFormat (fix)
make format-check  # SwiftFormat (check only, for CI)
make check         # build + lint + format-check + test

Running directly when needed:

xcodebuild build -scheme MyApp -destination 'platform=macOS'
xcodebuild test  -scheme MyApp -destination 'platform=macOS'

Code Signing (macOS)

  • Debug builds: automatic signing, any team
  • Release/distribution builds: specific team ID, provisioning profile
  • Notarization required for distribution outside the App Store
  • Entitlements (*.entitlements) must match capabilities declared in the target

Never hardcode signing identity strings — use CODE_SIGN_IDENTITY= with build setting variables.

04 同仓库更多 Skills