/swiftui-patterns
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 -y skills add johnrogers/claude-swift-engineering --skill swiftui-patterns --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/swiftui-patterns
Context preview
The summary Claude sees to decide when to auto-load this skill.
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.
SKILL.md
swiftui-patterns.SKILL.mdname: swiftui-patterns
description: >-
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.
SwiftUI Patterns (iOS 17+)
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.
Overview
Quick Reference
| 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:)` |
Core Workflow
1. Use `@Observable` for model classes (no @Published needed) 2. Use `@State` for view-owned models, `@Bindable` for passed models 3. Use `.task { }` for async work (auto-cancels on disappear) 4. Use `NavigationStack` with `NavigationPath` for programmatic navigation 5. Apply `.accessibilityLabel()` and `.accessibilityHint()` to interactive elements
Reference Loading Guide
**ALWAYS 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](references/observable.md)** | Creating new `@Observable` model classes | | **[State Management](references/state-management.md)** | Deciding between `@State`, `@Bindable`, `@Environment` | | **[Environment](references/environment.md)** | Injecting dependencies into view hierarchy | | **[View Modifiers](references/view-modifiers.md)** | Using `onChange`, `task`, or iOS 17+ modifiers | | **[Migration Guide](references/migration-guide.md)** | Updating iOS 16 code to iOS 17+ | | **[MVVM Observable](references/mvvm-observable.md)** | Setting up view model architecture | | **[Navigation](references/navigation.md)** | Programmatic or deep-link navigation | | **[Performance](references/performance.md)** | Lists with 100+ items or excessive re-renders | | **[UIKit Interop](references/uikit-interop.md)** | Wrapping UIKit components (WKWebView, PHPicker) | | **[Accessibility](references/accessibility.md)** | VoiceOver, Dynamic Type, accessibility actions | | **[Async Patterns](references/async-patterns.md)** | Loading states, refresh, background tasks | | **[Composition](references/composition.md)** | Reusable view modifiers or complex conditional UI |
Common Mistakes
1. **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.
2. **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.
3. **NavigationPath state corruption** — Mutating `NavigationPath` incorrectly can leave it in inconsistent state. Use `navigationDestination(for:destination:)` with proper state management to avoid path corruption.
4. **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.
5. **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.
6. **UIKit interop memory leaks** — `UIViewRepresentable` and `UIViewControllerRepresentable` can leak if delegate cycles aren't broken. Weak references and explicit cleanup are required.
Read more
name: swiftui-patterns description: >- 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.
SwiftUI Patterns (iOS 17+)
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.
Overview
Quick Reference
| 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:)` |
Core Workflow
1. Use `@Observable` for model classes (no @Published needed) 2. Use `@State` for view-owned models, `@Bindable` for passed models 3. Use `.task { }` for async work (auto-cancels on disappear) 4. Use `NavigationStack` with `NavigationPath` for programmatic navigation 5. Apply `.accessibilityLabel()` and `.accessibilityHint()` to interactive elements
Reference Loading Guide
**ALWAYS 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](references/observable.md)** | Creating new `@Observable` model classes | | **[State Management](references/state-management.md)** | Deciding between `@State`, `@Bindable`, `@Environment` | | **[Environment](references/environment.md)** | Injecting dependencies into view hierarchy | | **[View Modifiers](references/view-modifiers.md)** | Using `onChange`, `task`, or iOS 17+ modifiers | | **[Migration Guide](references/migration-guide.md)** | Updating iOS 16 code to iOS 17+ | | **[MVVM Observable](references/mvvm-observable.md)** | Setting up view model architecture | | **[Navigation](references/navigation.md)** | Programmatic or deep-link navigation | | **[Performance](references/performance.md)** | Lists with 100+ items or excessive re-renders | | **[UIKit Interop](references/uikit-interop.md)** | Wrapping UIKit components (WKWebView, PHPicker) | | **[Accessibility](references/accessibility.md)** | VoiceOver, Dynamic Type, accessibility actions | | **[Async Patterns](references/async-patterns.md)** | Loading states, refresh, background tasks | | **[Composition](references/composition.md)** | Reusable view modifiers or complex conditional UI |
Common Mistakes
1. **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.
2. **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.
3. **NavigationPath state corruption** — Mutating `NavigationPath` incorrectly can leave it in inconsistent state. Use `navigationDestination(for:destination:)` with proper state management to avoid path corruption.
4. **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.
5. **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.
6. **UIKit interop memory leaks** — `UIViewRepresentable` and `UIViewControllerRepresentable` can leak if delegate cycles aren't broken. Weak references and explicit cleanup are required.
Claude Code plugin marketplace for modern Swift/SwiftUI development A specialized AI toolkit for building professional iOS/macOS features with modern Swift 6.2, TCA (The Composable Architecture), and SwiftUI.
Other skills on claude-swift-engineering.
- /composable-architecture
Use when building features with TCA (The Composable Architecture), structuring reducers, managing state, handling effects, navigation, or testing TCA features. Covers @Reducer, Store, Effect, TestStore, reducer composition, and TCA patterns.
Open skill - /foundation-models
Use when implementing on-device AI with Apple's Foundation Models framework (iOS 26+), building summarization/extraction/classification features, or using @Generable for type-safe structured output.
Open skill - /generating-swift-package-docs
Use when encountering unfamiliar import statements, exploring dependency APIs, or when user asks "what's import X" or "what does X do". Generates on-demand API documentation for Swift package dependencies.
Open skill - /grdb
Use when writing raw SQL with GRDB, complex joins across 4+ tables, window functions, ValueObservation for reactive queries, or dropping down from SQLiteData for performance. Direct SQLite access for iOS/macOS with type-safe queries and migrations.
Open skill - /haptics
Use when adding haptic feedback for user confirmations (button presses, toggles, purchases), error notifications, or custom tactile patterns (Core Haptics). Covers UIFeedbackGenerator and CHHapticEngine patterns.
Open skill - /ios-26-platform
Use when implementing iOS 26 features (Liquid Glass, new SwiftUI APIs, WebView, Chart3D), deploying iOS 26+ apps, or supporting backward compatibility with iOS 17/18.
Open skill

