npx skills add ...
npx skills add affaan-m/ecc --skill swiftui-patterns
SwiftUI architecture patterns, state management with @Observable, view composition, navigation, performance optimization, and modern iOS/macOS UI best practices. Use when building or reviewing SwiftUI views, @Observable state, navigation, or render performance.
npx skills add affaan-m/ecc --skill swiftui-patterns
Modern SwiftUI patterns for building declarative, performant user interfaces on Apple platforms. Covers the Observation framework, view composition, type-safe navigation, and performance optimization.
@State, @Observable, @Binding)NavigationStackChoose the simplest wrapper that fits:
| Wrapper | Use Case |
|---|---|
@State | View-local value types (toggles, form fields, sheet presentation) |
@Binding | Two-way reference to parent's @State |
@Observable class + @State | Owned model with multiple properties |
@Observable class (no wrapper) | Read-only reference passed from parent |
@Bindable | Two-way binding to an @Observable property |
@Environment | Shared dependencies injected via .environment() |
Use @Observable (not ObservableObject) — it tracks property-level changes so SwiftUI only re-renders views that read the changed property:
Replace @EnvironmentObject with @Environment:
Break views into small, focused structs. When state changes, only the subview reading that state re-renders:
Use NavigationStack with NavigationPath for programmatic, type-safe routing:
LazyVStack and LazyHStack create views only when visible:
Always use stable, unique IDs in ForEach — avoid using array indices:
body.task {} for async work — it cancels automatically when the view disappears.sensoryFeedback() and .geometryGroup() sparingly in scroll views.shadow(), .blur(), and .mask() in lists — they trigger offscreen renderingFor views with expensive bodies, conform to Equatable to skip unnecessary re-renders:
Use #Preview macro with inline mock data for fast iteration:
ObservableObject / @Published / @StateObject / @EnvironmentObject in new code — migrate to @Observablebody or init — use .task {} or explicit load methods@State inside child views that don't own the data — pass from parent insteadAnyView type erasure — prefer @ViewBuilder or Group for conditional viewsSendable requirements when passing data to/from actorsSee skill: swift-actor-persistence for actor-based persistence patterns.
See skill: swift-protocol-di-testing for protocol-based DI and testing with Swift Testing.