apple-swift
ABOUTME: Apple platform guide, Swift, SwiftUI, concurrency, testing, performance
ABOUTME: Conventions, strict concurrency rules, MVVM + DI patterns, review checklists
Apple Platform Development
Quick Reference
# Build
xcodebuild -scheme MyApp -sdk iphoneos build
xcodebuild -scheme MyApp -sdk macosx build
# Tests
xcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 16'
# SwiftLint / SPM
swiftlint lint [--fix]
swift build && swift test && swift package resolve
# ast-grep
sg --pattern '@Observable final class $NAME { $$$ }' --lang swift
sg --pattern '@MainActor' --lang swift
See also: _AST_GREP.md, _PATTERNS.md, source-control, references/
Version (determine, don't assume)
Never assume a Swift/Xcode version from prior knowledge: it rots fast and you miss CVE fixes. Fetch the truth:
swift --version # local toolchain
xcodebuild -version # Xcode version
cat .swift-version 2>/dev/null # pinned toolchain (if present)
grep -E 'swift-tools-version' Package.swift # SPM minimum
curl -s https://api.github.com/repos/swiftlang/swift/releases/latest | jq -r .tag_name # latest upstream
For a new project, pin to the latest stable. For an existing one, read Package.swift (plus .xcode-version / .tool-versions if present) and prefer idioms gated to that version or lower.
Pre-Commit Verification (MANDATORY)
Before every commit, both of these MUST pass:
make check # project-wide gate (lint, types, tests, security)
make test-e2e # end-to-end tests (UI tests, integration target, or project's e2e scheme)
If make check is missing, scaffold it with the project-checks skill. If there is no e2e target, do NOT silently skip: flag it to the user and ask whether to proceed or add one.
Full raw toolchain (what make check should expand to):
swift build # compilation check
swift test # unit tests
swiftlint lint --strict # lint (fail on warnings)
swift-format lint --recursive Sources Tests # formatting check
xcodebuild -scheme MyApp test \
-destination 'platform=iOS Simulator,name=iPhone 16' # platform tests
SwiftUI
Property Wrappers
| Wrapper | Use |
|---|---|
@State |
View-owned values, @Observable |
@Binding |
Two-way to parent |
@Bindable |
Two-way to @Observable props |
@Environment |
System/app values |
@StateObject |
View-owned ObservableObject (legacy) |
View property order: @Environment, let, @State/@Binding, computed, init, body, private methods
View sizing: <100 lines = single view; 100-200 = extract subviews; >200 = multiple files; business logic = @Observable VM; network/DB = repository pattern
For NavigationStack, SwiftData, MVVM, and DI patterns, see references/swiftui-patterns.md.
Concurrency
Common Fixes
| Error | Fix |
|---|---|
| Main actor isolation | Add @MainActor to class/func |
| Non-isolated access | Mark nonisolated |
| Sendable violation | Add @unchecked Sendable or fix |
| Protocol async | Require async in protocol |
| Closure capture | Use @Sendable closure |
When to use what
| Use Case | Choice |
|---|---|
| One-shot network | async/await |
| Parallel fetches | async let / TaskGroup |
| Real-time streams | Combine / AsyncStream |
| UI events, debounce | Combine |
NON-NEGOTIABLE: UI updates always on @MainActor. Cross-actor value types must be Sendable. Respect task cancellation: check Task.isCancelled in long-running work.
For MainActor, parallel execution, and actor patterns, see references/concurrency-patterns.md.
Architecture
MVVM with @Observable: @Observable @MainActor final class VM with private(set) properties, injected service protocols, async load methods with defer { isLoading = false }.
DI: Protocol-based with EnvironmentKey for SwiftUI injection.
Coordinator pattern: For flow-heavy apps, lift navigation state out of views into a Coordinator type owning a NavigationPath (or route enum) and exposing intents.
Testing (Swift Testing): @Suite, @Test, #expect. Parameterized with arguments:. Mock via protocol injection. XCTest still valid where needed (UI tests, legacy targets).
For full code examples, see references/swiftui-patterns.md and references/swift6-patterns.md.
Review Checklists
Concurrency: MainActor for UI, Sendable for cross-actor data, task cancellation handled, no data races
SwiftUI: @Observable over ObservableObject where available, NavigationStack over NavigationView, .task over .onAppear + Task, LazyVStack for long lists
CRITICAL: Force unwrap without safety, UI updates off MainActor, data races, retain cycles
HIGH: Legacy ObservableObject when @Observable fits, NavigationView in new code
Detailed References
references/swift6-patterns.md- Strict concurrency migration, Sendable, actors, macrosreferences/swiftui-patterns.md- NavigationStack, SwiftData, MVVM, dependency injectionreferences/concurrency-patterns.md- async/await, TaskGroup, MainActor, actors, AsyncStreamreferences/performance.md- Optimization, Instruments profiling, memory management
Official: Swift | SwiftUI | SwiftData
Libraries: TCA | Snapshot Testing | SwiftLint
More from maroffo/claude-forge
email-cleanup
Clean up Gmail - archive old emails, delete promotions, manage storage. Use when user wants to clean inbox, archive emails, or free up space.
25newsletter-digest
Process newsletters into Second Brain digest. Use when user wants to process newsletters, create digest, or catch up on subscriptions. Not for web clippings (use process-clippings) or email bookmarks (use process-email-bookmarks).
22table-image
Render markdown tables as hand-drawn sketch images. Use when user wants a table rendered as an image, visual table, or diagram illustration.
21react-nextjs
React + Next.js App Router development. Use when working with .tsx/.jsx files, next.config, or user asks about Server Components, data fetching, state management, forms, or React testing.
20inbox-triage
Review and prioritize Gmail inbox. Use when user wants to check email, review inbox, or see what needs attention.
19project-analyzer
Analyze codebase and create CLAUDE.md documentation. Use when analyzing project, understanding codebase, or creating documentation.
17