---
name: swift-rules
description: "Swift coding rules: style, patterns, security, testing. Triggers: .swift, Package.swift, .xcodeproj, SwiftUI, Combine, async/await, XCTest."
effort: medium
user-invocable: false
allowed-tools: Read
---

# Swift Rules

These rules come from `app/rules/swift/` in ai-toolkit. They cover
the project's standards for coding style, frameworks, patterns,
security, and testing in Swift. Apply them when writing or
reviewing Swift code.

# Swift Coding Style

## Naming
- PascalCase: types, protocols, enums, struct, class.
- camelCase: functions, methods, properties, variables, enum cases.
- No prefixes: Swift has module namespacing (no `NS` or `UI` prefix for your types).
- Use descriptive names: `removeElement(at:)` not `remove(i:)`.
- Boolean properties read as assertions: `isEmpty`, `hasChildren`, `canSubmit`.

## Types
- Prefer `struct` over `class` by default (value semantics, no reference cycles).
- Use `class` only when reference semantics or inheritance is required.
- Use `enum` with associated values for modeling finite states.
- Use `protocol` for defining capabilities. Prefer protocol composition.
- Use `typealias` for complex generic signatures for readability.

## Optionals
- Use `guard let` for early exit on nil. Use `if let` for conditional binding.
- Never force-unwrap (`!`) unless failure is a programming error.
- Use `??` for default values: `let name = user?.name ?? "Unknown"`.
- Use optional chaining: `user?.address?.city`.
- Use `map` / `flatMap` on optionals for transformations.

## Properties
- Use `let` by default. Use `var` only when mutation is required.
- Use computed properties for derived values: `var fullName: String { ... }`.
- Use property observers (`willSet`, `didSet`) for side effects on change.
- Use `lazy var` for expensive initialization deferred until first access.
- Use `@Published` (Combine) for observable properties in classes.

## Functions
- Use argument labels for clarity: `func move(from source: Int, to destination: Int)`.
- Omit argument labels when the function name makes the role clear: `func contains(_ element: T)`.
- Use default parameter values instead of multiple overloads.
- Use `throws` / `async throws` for fallible operations.
- Use trailing closure syntax for the last closure parameter.

## Access Control
- Use `private` for implementation details. Use `fileprivate` sparingly.
- Use `internal` (default) for module-scoped access.
- Use `public` for framework API. Use `open` only when subclassing is intended.
- Prefer `private(set)` for read-only external access with internal mutation.

## Formatting
- Use SwiftLint for automated style enforcement.
- Use SwiftFormat for automated code formatting.
- Commit `.swiftlint.yml` and `.swiftformat` to the repository.
- Max line length: 120 characters (SwiftLint default).
- Use trailing commas in multi-line arrays and dictionaries.

# Swift Frameworks

## SwiftUI
- Use `VStack`, `HStack`, `ZStack` for layout composition.
- Use `List` with `ForEach` for dynamic content. Use `LazyVStack` for large lists.
- Use `NavigationStack` (iOS 16+) with `navigationDestination(for:)` for type-safe navigation.
- Use `.task { }` modifier for async data loading tied to view lifecycle.
- Use `@ViewBuilder` for conditional view composition in custom containers.
- Use `PreviewProvider` or `#Preview` macro for rapid UI iteration.

## UIKit (Legacy / Hybrid)
- Use `UIHostingController` to embed SwiftUI views in UIKit.
- Use `UIViewRepresentable` to wrap UIKit views in SwiftUI.
- Use Auto Layout with constraints or `UIStackView` for layout.
- Use `UICollectionViewCompositionalLayout` for complex collection layouts.
- Use `Coordinator` pattern for delegate-based UIKit interop in SwiftUI.

## Combine
- Use `Publisher` / `Subscriber` for reactive data streams.
- Use `sink` for subscribing. Store cancellables in `Set<AnyCancellable>`.
- Use `map`, `filter`, `flatMap`, `combineLatest` for stream transformation.
- Use `@Published` on class properties for automatic publisher generation.
- Prefer `AsyncSequence` (async/await) over Combine for new code.

## Swift Data
- Use `@Model` macro for persistent model definitions.
- Use `@Query` in SwiftUI views for automatic fetching and observation.
- Use `ModelContext` for CRUD operations: `context.insert(item)`, `context.delete(item)`.
- Use `#Predicate` macro for type-safe query filtering.
- Use `ModelConfiguration` for custom store locations and migration options.

## Core Data (Legacy)
- Use `NSPersistentContainer` for stack setup.
- Use `NSFetchRequest` with `NSPredicate` for querying.
- Use `performBackgroundTask` for background context operations.
- Use lightweight migrations for schema changes when possible.
- Prefer SwiftData for new projects (iOS 17+).

## Vapor (Server-Side)
- Use `routes.get("users")` for route definitions.
- Use `Content` protocol for request/response body codable conformance.
- Use Fluent ORM with migrations for database access.
- Use middleware for authentication, CORS, and error handling.
- Use `async`/`await` natively (Vapor 4+ is fully async).

## Networking
- Use `URLSession` with `async/await` for HTTP requests.
- Use `Codable` with `JSONDecoder` for response parsing.
- Use `URLCache` and `ETag` for response caching.
- Set `timeoutIntervalForRequest` on `URLSessionConfiguration`.
- Use `TaskLocal` for request-scoped values (tracing, auth context).

## Package Management
- Use Swift Package Manager (SPM) for dependency management.
- Define dependencies in `Package.swift` with exact version or version ranges.
- Use `Package.resolved` committed to the repository for reproducible builds.
- Prefer SPM over CocoaPods/Carthage for new projects.

# Swift Patterns

## Error Handling
- Use `enum AppError: Error` for typed, exhaustive error handling.
- Use `throws` functions with `do-catch` for recoverable errors.
- Use `Result<Success, Failure>` for asynchronous error propagation.
- Use `try?` for optional conversion. Use `try!` only in tests or guaranteed paths.
- Add `LocalizedError` conformance for user-facing error messages.

## Protocol-Oriented Design
- Define capabilities as protocols: `protocol Fetchable { func fetch() async throws -> Data }`.
- Use protocol extensions for default implementations.
- Use protocol composition: `func process(_ item: Sendable & Codable)`.
- Use associated types for generic protocols: `associatedtype Output`.
- Use `some Protocol` (opaque types) for return types hiding concrete implementations.

## Async/Await
- Use `async` functions for all asynchronous operations.
- Use `async let` for concurrent, independent operations.
- Use `TaskGroup` for dynamic parallelism with collected results.
- Use `Task { }` to bridge sync to async. Avoid `.task { }` in views for complex logic.
- Use `withThrowingTaskGroup` for concurrent operations that can fail.

## Actors
- Use `actor` for thread-safe mutable state (replaces manual locks).
- Use `@MainActor` for UI-related state and methods.
- Use `nonisolated` for actor methods that do not access mutable state.
- Use `GlobalActor` for custom isolation domains.
- Minimize `await` calls on actors to reduce suspension points.

## SwiftUI Patterns
- Use `@State` for view-local mutable state.
- Use `@Binding` for child-to-parent state communication.
- Use `@Observable` (Observation framework) for model objects (preferred over `@ObservedObject`).
- Use `@Environment` for dependency injection: `@Environment(\.modelContext)`.
- Use `ViewModifier` for reusable view transformations.
- Extract subviews into separate structs for readability and performance.

## Codable
- Use `Codable` for JSON serialization/deserialization.
- Use `CodingKeys` enum for custom key mapping.
- Use `JSONDecoder` with `.convertFromSnakeCase` for API compatibility.
- Use `@propertyWrapper` for custom decoding strategies (e.g., date formats).
- Use `nestedContainer` for flattening nested JSON structures.

## Dependency Injection
- Use initializer injection for required dependencies.
- Use `@Environment` in SwiftUI for framework-provided values.
- Use `swift-dependencies` library for testable, controlled dependency management.
- Use `@Dependency(\.apiClient) var apiClient` for automatic resolution.

## Anti-Patterns
- Force-unwrapping optionals: use `guard let` or `??`.
- Massive view controllers/views: split into subviews and view models.
- Reference cycles: use `[weak self]` in closures capturing `self`.
- Blocking the main thread: use `Task` or `DispatchQueue.global()`.
- Stringly-typed APIs: use enums, protocols, and strong types.

# Swift Security

## Keychain
- Use Keychain Services for storing passwords, tokens, and cryptographic keys.
- Use `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` for sensitive items.
- Use `KeychainAccess` or similar wrapper libraries for cleaner API.
- Never store secrets in `UserDefaults` (unencrypted plist on disk).
- Delete keychain items on user logout.

## App Transport Security (ATS)
- Use HTTPS for all network connections. ATS enforces this by default.
- Never add blanket `NSAllowsArbitraryLoads` exception.
- Use per-domain exceptions only when connecting to legacy servers.
- Implement certificate pinning for high-security connections.
- Validate server certificates in `URLSessionDelegate` for custom pinning.

## Input Validation
- Validate all user input before processing or displaying.
- Use `NSRegularExpression` or Swift Regex for pattern validation.
- Sanitize strings before using in URL construction, SQL, or HTML.
- Validate deep link URL parameters before navigation.
- Limit input lengths in `UITextField` / `TextField` to prevent abuse.

## Data Protection
- Use `Data Protection` API: set `FileProtectionType.complete` on sensitive files.
- Use `CryptoKit` for hashing (`SHA256`), encryption (`AES.GCM`), and signing.
- Use `SecureEnclave` for hardware-backed key storage on supported devices.
- Zero sensitive data in memory after use: `withUnsafeMutableBytes { $0.initializeMemory(as: UInt8.self, repeating: 0) }`.
- Use `@Sendable` closures to prevent data races in concurrent access.

## Authentication
- Use `AuthenticationServices` for Sign in with Apple and passkeys.
- Use `LocalAuthentication` (Face ID / Touch ID) for biometric auth.
- Store authentication tokens in Keychain, not in memory or UserDefaults.
- Use short-lived access tokens with refresh token rotation.
- Implement session timeout for inactive users.

## Network Security
- Use `URLSession` with certificate pinning for sensitive API calls.
- Validate response `Content-Type` headers before parsing.
- Use `Codable` for structured deserialization (prevents injection).
- Set request timeouts to prevent hanging connections.
- Do not log request/response bodies containing sensitive data.

## Code Security
- Use `[weak self]` in closures to prevent retain cycles and memory leaks.
- Use `@Sendable` and actor isolation for thread-safe concurrent code.
- Avoid `UnsafePointer` / `UnsafeMutablePointer` unless absolutely necessary.
- Use `#if DEBUG` guards for debug-only code. Never ship debug features.
- Enable Xcode hardened runtime for macOS apps.

## Dependency Security
- Audit SPM dependencies before adding. Check maintainer reputation.
- Pin dependency versions in `Package.resolved`.
- Review `Package.swift` of dependencies for unusual build plugins.
- Prefer dependencies with active security response and disclosure processes.
- Minimize third-party dependencies for security-critical modules.

# Swift Testing

## Framework
- Use Swift Testing (`import Testing`) for new projects (Swift 5.10+).
- Use XCTest for existing projects and UIKit-based UI tests.
- Use swift-snapshot-testing for visual regression testing.
- Use swift-dependencies for controlled dependency injection in tests.

## File Naming
- Test files: `FooTests.swift` in `Tests/` target.
- Mirror source module structure in test target.
- Use `@Test` attribute (Swift Testing) or `test` prefix (XCTest) for test methods.
- Use `@Suite` (Swift Testing) for test grouping.

## Structure (Swift Testing)
- Use `@Test("description")` for individual test cases.
- Use `@Test(arguments: [...])` for parameterized tests.
- Use `#expect(condition)` for assertions. Use `#require(condition)` for preconditions.
- Use `#expect(throws: FooError.self) { try riskyOperation() }` for error testing.
- Use `@Suite` structs for grouping. Properties serve as shared setup.

## Structure (XCTest)
- Use `setUp()` / `tearDown()` for per-test initialization and cleanup.
- Use `setUpWithError()` for throwing setup code.
- Use `XCTAssertEqual`, `XCTAssertTrue`, `XCTAssertNil` for assertions.
- Use `XCTAssertThrowsError` for exception testing.
- Use `expectation(description:)` + `wait(for:timeout:)` for async assertions.

## Async Testing
- Use `async` test functions: `@Test func fetchUser() async throws { ... }`.
- Use `confirmation()` (Swift Testing) for event-based async assertions.
- XCTest: use `XCTestExpectation` with `fulfillment()` for callback-based async.
- Test `AsyncSequence` with `for await` loops and assertion on collected values.

## Mocking
- Use protocol-based dependency injection for testability.
- Create manual mock implementations conforming to protocols.
- Use `swift-dependencies` for environment-controlled dependency overrides.
- Use `@Dependency` property wrapper for automatic mock injection in tests.
- Avoid mocking frameworks when protocol mocks are straightforward.

## UI Testing (XCTest)
- Use `XCUIApplication` for UI automation tests.
- Use accessibility identifiers for reliable element lookup.
- Use `app.buttons["Submit"].tap()` for interaction simulation.
- Use `waitForExistence(timeout:)` for async UI element appearance.
- Keep UI tests focused on critical user flows only (slow to run).

## Best Practices
- Test behavior through public API. Avoid `@testable import` when possible.
- Use `@testable import Module` only when testing internal members is necessary.
- Use `withDependencies { }` for scoped dependency overrides in tests.
- Test on multiple platforms (iOS, macOS) when shipping cross-platform.
- Run tests with `swift test` or `xcodebuild test` in CI.
