| name | swiftui-expert-skill |
| description | Write, review, or improve SwiftUI code following best practices for state management, view composition, performance, modern APIs, Swift concurrency, and iOS 26+ Liquid Glass adoption. Use when building new SwiftUI features, refactoring existing views, reviewing code quality, or adopting modern SwiftUI patterns. |
SwiftUI Expert Skill
Overview
Use this skill to build, review, or improve SwiftUI features with correct state management, modern API usage, Swift concurrency best practices, optimal view composition, and iOS 26+ Liquid Glass styling. Prioritize native APIs, Apple design guidance, and performance-conscious patterns. This skill focuses on facts and best practices without enforcing specific architectural patterns.
Workflow Decision Tree
1) Review existing SwiftUI code
- Check property wrapper usage against the selection guide (see
references/state-management.md)
- Verify modern API usage (see
references/modern-apis.md)
- Verify view composition follows extraction rules (see
references/view-structure.md)
- Check performance patterns are applied (see
references/performance-patterns.md)
- Verify list patterns use stable identity (see
references/list-patterns.md)
- Check animation patterns for correctness (see
references/animation-basics.md, references/animation-transitions.md)
- Inspect Liquid Glass usage for correctness and consistency (see
references/liquid-glass.md)
- Validate iOS 26+ availability handling with sensible fallbacks
2) Improve existing SwiftUI code
- Audit state management for correct wrapper selection (prefer
@Observable over ObservableObject)
- Replace deprecated APIs with modern equivalents (see
references/modern-apis.md)
- Extract complex views into separate subviews (see
references/view-structure.md)
- Refactor hot paths to minimize redundant state updates (see
references/performance-patterns.md)
- Ensure ForEach uses stable identity (see
references/list-patterns.md)
- Improve animation patterns (use value parameter, proper transitions, see
references/animation-basics.md, references/animation-transitions.md)
- Suggest image downsampling when
UIImage(data:) is used (as optional optimization, see references/image-optimization.md)
- Adopt Liquid Glass only when explicitly requested by the user
3) Implement new SwiftUI feature
- Design data flow first: identify owned vs injected state (see
references/state-management.md)
- Use modern APIs (no deprecated modifiers or patterns, see
references/modern-apis.md)
- Use
@Observable for shared state (with @MainActor if not using default actor isolation)
- Structure views for optimal diffing (extract subviews early, keep views small, see
references/view-structure.md)
- Separate business logic into testable models (see
references/layout-best-practices.md)
- Use correct animation patterns (implicit vs explicit, transitions, see
references/animation-basics.md, references/animation-transitions.md, references/animation-advanced.md)
- Apply glass effects after layout/appearance modifiers (see
references/liquid-glass.md)
- Gate iOS 26+ features with
#available and provide fallbacks
Core Guidelines
State Management
- Always prefer
@Observable over ObservableObject for new code
- Mark
@Observable classes with @MainActor unless using default actor isolation
- Always mark
@State and @StateObject as private (makes dependencies clear)
- Never declare passed values as
@State or @StateObject (they only accept initial values)
- Use
@State with @Observable classes (not @StateObject)
@Binding only when child needs to modify parent state
@Bindable for injected @Observable objects needing bindings
- Use
let for read-only values; var + .onChange() for reactive reads
- Legacy:
@StateObject for owned ObservableObject; @ObservedObject for injected
- Nested
ObservableObject doesn't work (pass nested objects directly); @Observable handles nesting fine
Modern APIs
- Use
foregroundStyle() instead of foregroundColor()
- Use
clipShape(.rect(cornerRadius:)) instead of cornerRadius()
- Use
Tab API instead of tabItem()
- Use
Button instead of onTapGesture() (unless need location/count)
- Use
NavigationStack instead of NavigationView
- Use
navigationDestination(for:) for type-safe navigation
- Use two-parameter or no-parameter
onChange() variant
- Use
ImageRenderer for rendering SwiftUI views
- Use
.sheet(item:) instead of .sheet(isPresented:) for model-based content
- Sheets should own their actions and call
dismiss() internally
- Use
ScrollViewReader for programmatic scrolling with stable IDs
- Avoid
UIScreen.main.bounds for sizing
- Avoid
GeometryReader when alternatives exist (e.g., containerRelativeFrame())
Swift Best Practices
- Use modern Text formatting (
.format parameters, not String(format:))
- Use
localizedStandardContains() for user-input filtering (not contains())
- Prefer static member lookup (
.blue vs Color.blue)
- Use
.task modifier for automatic cancellation of async work
- Use
.task(id:) for value-dependent tasks
View Composition
- Prefer modifiers over conditional views for state changes (maintains view identity)
- Extract complex views into separate subviews for better readability and performance
- Keep views small for optimal performance
- Keep view
body simple and pure (no side effects or complex logic)
- Use
@ViewBuilder functions only for small, simple sections
- Prefer
@ViewBuilder let content: Content over closure-based content properties
- Separate business logic into testable models (not about enforcing architectures)
- Action handlers should reference methods, not contain inline logic
- Use relative layout over hard-coded constants
- Views should work in any context (don't assume screen size or presentation style)
Performance
- Pass only needed values to views (avoid large "config" or "context" objects)
- Eliminate unnecessary dependencies to reduce update fan-out
- Check for value changes before assigning state in hot paths
- Avoid redundant state updates in
onReceive, onChange, scroll handlers
- Minimize work in frequently executed code paths
- Use
LazyVStack/LazyHStack for large lists
- Use stable identity for
ForEach (never .indices for dynamic content)
- Ensure constant number of views per
ForEach element
- Avoid inline filtering in
ForEach (prefilter and cache)
- Avoid
AnyView in list rows
- Consider POD views for fast diffing (or wrap expensive views in POD parents)
- Suggest image downsampling when
UIImage(data:) is encountered (as optional optimization)
- Avoid layout thrash (deep hierarchies, excessive
GeometryReader)
- Gate frequent geometry updates by thresholds
- Use
Self._printChanges() to debug unexpected view updates
Animations
- Use
.animation(_:value:) with value parameter (deprecated version without value is too broad)
- Use
withAnimation for event-driven animations (button taps, gestures)
- Prefer transforms (
offset, scale, rotation) over layout changes (frame) for performance
- Transitions require animations outside the conditional structure
- Custom
Animatable implementations must have explicit animatableData
- Use
.phaseAnimator for multi-step sequences (iOS 17+)
- Use
.keyframeAnimator for precise timing control (iOS 17+)
- Animation completion handlers need
.transaction(value:) for reexecution
- Implicit animations override explicit animations (later in view tree wins)
Liquid Glass (iOS 26+)
Only adopt when explicitly requested by the user.
- Use native
glassEffect, GlassEffectContainer, and glass button styles
- Wrap multiple glass elements in
GlassEffectContainer
- Apply
.glassEffect() after layout and visual modifiers
- Use
.interactive() only for tappable/focusable elements
- Use
glassEffectID with @Namespace for morphing transitions
Quick Reference
Property Wrapper Selection (Modern)
| Wrapper | Use When |
|---|
@State | Internal view state (must be private), or owned @Observable class |
@Binding | Child modifies parent's state |
@Bindable | Injected @Observable needing bindings |
let | Read-only value from parent |
var | Read-only value watched via .onChange() |
Legacy (Pre-iOS 17):
| Wrapper | Use When |
|---|
@StateObject | View owns an ObservableObject (use @State with @Observable instead) |
@ObservedObject | View receives an ObservableObject |
Modern API Replacements
| Deprecated | Modern Alternative |
|---|
foregroundColor() | foregroundStyle() |
cornerRadius() | clipShape(.rect(cornerRadius:)) |
tabItem() | Tab API |
onTapGesture() | Button (unless need location/count) |
NavigationView | NavigationStack |
onChange(of:) { value in } | onChange(of:) { old, new in } or onChange(of:) { } |
fontWeight(.bold) | bold() |
GeometryReader | containerRelativeFrame() or visualEffect() |
showsIndicators: false | .scrollIndicators(.hidden) |
String(format: "%.2f", value) | Text(value, format: .number.precision(.fractionLength(2))) |
string.contains(search) | string.localizedStandardContains(search) (for user input) |
Liquid Glass Patterns
if #available(iOS 26, *) {
content
.padding()
.glassEffect(.regular.interactive(), in: .rect(cornerRadius: 16))
} else {
content
.padding()
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))
}
GlassEffectContainer(spacing: 24) {
HStack(spacing: 24) {
GlassButton1()
GlassButton2()
}
}
Button("Confirm") { }
.buttonStyle(.glassProminent)
Review Checklist
State Management
Modern APIs (see references/modern-apis.md)
Sheets & Navigation (see references/sheet-navigation-patterns.md)
ScrollView (see references/scroll-patterns.md)
Text & Formatting (see references/text-formatting.md)
View Structure (see references/view-structure.md)
Performance (see references/performance-patterns.md)
List Patterns (see references/list-patterns.md)
Layout (see references/layout-best-practices.md)
Animations (see references/animation-basics.md, references/animation-transitions.md, references/animation-advanced.md)
Liquid Glass (iOS 26+)
References
references/state-management.md - Property wrappers and data flow (prefer @Observable)
references/view-structure.md - View composition, extraction, and container patterns
references/performance-patterns.md - Performance optimization techniques and anti-patterns
references/list-patterns.md - ForEach identity, stability, and list best practices
references/layout-best-practices.md - Layout patterns, context-agnostic views, and testability
references/modern-apis.md - Modern API usage and deprecated replacements
references/animation-basics.md - Core animation concepts, implicit/explicit animations, timing, performance
references/animation-transitions.md - Transitions, custom transitions, Animatable protocol
references/animation-advanced.md - Transactions, phase/keyframe animations (iOS 17+), completion handlers (iOS 17+)
references/sheet-navigation-patterns.md - Sheet presentation and navigation patterns
references/scroll-patterns.md - ScrollView patterns and programmatic scrolling
references/text-formatting.md - Modern text formatting and string operations
references/image-optimization.md - AsyncImage, image downsampling, and optimization
references/liquid-glass.md - iOS 26+ Liquid Glass API
Philosophy
This skill focuses on facts and best practices, not architectural opinions:
- We don't enforce specific architectures (e.g., MVVM, VIPER)
- We do encourage separating business logic for testability
- We prioritize modern APIs over deprecated ones
- We emphasize thread safety with
@MainActor and @Observable
- We optimize for performance and maintainability
- We follow Apple's Human Interface Guidelines and API design patterns