plugins/swift-engineering/skills/swiftui-patterns/SKILL.md
Use when implementing iOS 17+ SwiftUI patterns: @Observable/@Bindable, MVVM architecture, NavigationStack, lazy loading, UIKit interop, accessibility (VoiceOver/Dynamic Type), async operations (.task/.refreshable), or migrating from ObservableObject/@StateObject.
npx skillsauth add johnrogers/claude-swift-engineering swiftui-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
SwiftUI 17+ removes ObservableObject boilerplate with @Observable, simplifies environment injection with @Environment, and introduces task-based async patterns. The core principle: use Apple's modern APIs instead of reactive libraries.
| Need | Use (iOS 17+) | NOT |
|------|---------------|-----|
| Observable model | @Observable | ObservableObject |
| Published property | Regular property | @Published |
| Own state | @State | @StateObject |
| Passed model (binding) | @Bindable | @ObservedObject |
| Environment injection | environment(_:) | environmentObject(_:) |
| Environment access | @Environment(Type.self) | @EnvironmentObject |
| Async on appear | .task { } | .onAppear { Task {} } |
| Value change | onChange(of:initial:_:) | onChange(of:perform:) |
@Observable for model classes (no @Published needed)@State for view-owned models, @Bindable for passed models.task { } for async work (auto-cancels on disappear)NavigationStack with NavigationPath for programmatic navigation.accessibilityLabel() and .accessibilityHint() to interactive elementsALWAYS load reference files if there is even a small chance the content may be required. It's better to have the context than to miss a pattern or make a mistake.
| Reference | Load When |
|-----------|-----------|
| Observable | Creating new @Observable model classes |
| State Management | Deciding between @State, @Bindable, @Environment |
| Environment | Injecting dependencies into view hierarchy |
| View Modifiers | Using onChange, task, or iOS 17+ modifiers |
| Migration Guide | Updating iOS 16 code to iOS 17+ |
| MVVM Observable | Setting up view model architecture |
| Navigation | Programmatic or deep-link navigation |
| Performance | Lists with 100+ items or excessive re-renders |
| UIKit Interop | Wrapping UIKit components (WKWebView, PHPicker) |
| Accessibility | VoiceOver, Dynamic Type, accessibility actions |
| Async Patterns | Loading states, refresh, background tasks |
| Composition | Reusable view modifiers or complex conditional UI |
Over-using @Bindable for passed models — Creating @Bindable for every property causes unnecessary view reloads. Use @Bindable only for mutable model properties that need two-way binding. Read-only computed properties should use regular properties.
State placement errors — Putting model state in the view instead of a dedicated @Observable model causes view logic to become tangled. Always separate model and view concerns.
NavigationPath state corruption — Mutating NavigationPath incorrectly can leave it in inconsistent state. Use navigationDestination(for:destination:) with proper state management to avoid path corruption.
Missing .task cancellation — .task handles cancellation on disappear automatically, but nested Tasks don't. Complex async flows need explicit cancellation tracking to avoid zombie tasks.
Ignoring environment invalidation — Changing environment values at parent doesn't invalidate child views automatically. Use @Environment consistently and understand when re-renders happen based on observation.
UIKit interop memory leaks — UIViewRepresentable and UIViewControllerRepresentable can leak if delegate cycles aren't broken. Weak references and explicit cleanup are required.
tools
Use when implementing gesture composition (simultaneous, sequenced, exclusive), adaptive layouts (ViewThatFits, AnyLayout, size classes), or choosing architecture patterns (MVVM vs TCA vs vanilla, State-as-Bridge). Covers advanced SwiftUI patterns beyond basic views.
testing
Use when writing tests with Swift Testing (@Test,
development
Swift code style conventions for clean, readable code. Use when writing Swift code to ensure consistent formatting, naming, organization, and idiomatic patterns.
development
Use when implementing Network.framework connections (NWConnection, NetworkConnection), debugging connection failures, migrating from sockets/URLSession streams, or handling network transitions. Covers UDP/TCP patterns, structured concurrency networking (iOS 26+), and common anti-patterns.