swiftui-expert-skill
Original:🇺🇸 English
Not Translated
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.
2installs
Sourcepetekp/claude-code-setup
Added on
NPX Install
npx skill4agent add petekp/claude-code-setup swiftui-expert-skillSKILL.md Content
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 - 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 over
@Observable)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 - Suggest image downsampling when is used (as optional optimization, see
UIImage(data:))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 for shared state (with
@Observableif not using default actor isolation)@MainActor - 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 - Apply glass effects after layout/appearance modifiers (see )
references/liquid-glass.md - Gate iOS 26+ features with and provide fallbacks
#available
Core Guidelines
State Management
- Always prefer over
@Observablefor new codeObservableObject - Mark classes with
@Observableunless using default actor isolation@MainActor - Always mark and
@Stateas@StateObject(makes dependencies clear)private - Never declare passed values as or
@State(they only accept initial values)@StateObject - Use with
@Stateclasses (not@Observable)@StateObject - only when child needs to modify parent state
@Binding - for injected
@Bindableobjects needing bindings@Observable - Use for read-only values;
let+varfor reactive reads.onChange() - Legacy: for owned
@StateObject;ObservableObjectfor injected@ObservedObject - Nested doesn't work (pass nested objects directly);
ObservableObjecthandles nesting fine@Observable
Modern APIs
- Use instead of
foregroundStyle()foregroundColor() - Use instead of
clipShape(.rect(cornerRadius:))cornerRadius() - Use API instead of
TabtabItem() - Use instead of
Button(unless need location/count)onTapGesture() - Use instead of
NavigationStackNavigationView - Use for type-safe navigation
navigationDestination(for:) - Use two-parameter or no-parameter variant
onChange() - Use for rendering SwiftUI views
ImageRenderer - Use instead of
.sheet(item:)for model-based content.sheet(isPresented:) - Sheets should own their actions and call internally
dismiss() - Use for programmatic scrolling with stable IDs
ScrollViewReader - Avoid for sizing
UIScreen.main.bounds - Avoid when alternatives exist (e.g.,
GeometryReader)containerRelativeFrame()
Swift Best Practices
- Use modern Text formatting (parameters, not
.format)String(format:) - Use for user-input filtering (not
localizedStandardContains())contains() - Prefer static member lookup (vs
.blue)Color.blue - Use modifier for automatic cancellation of async work
.task - Use for value-dependent tasks
.task(id:)
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 simple and pure (no side effects or complex logic)
body - Use functions only for small, simple sections
@ViewBuilder - Prefer over closure-based content properties
@ViewBuilder let content: Content - 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, scroll handlersonChange - Minimize work in frequently executed code paths
- Use /
LazyVStackfor large listsLazyHStack - Use stable identity for (never
ForEachfor dynamic content).indices - Ensure constant number of views per element
ForEach - Avoid inline filtering in (prefilter and cache)
ForEach - Avoid in list rows
AnyView - Consider POD views for fast diffing (or wrap expensive views in POD parents)
- Suggest image downsampling when is encountered (as optional optimization)
UIImage(data:) - Avoid layout thrash (deep hierarchies, excessive )
GeometryReader - Gate frequent geometry updates by thresholds
- Use to debug unexpected view updates
Self._printChanges()
Liquid Glass (iOS 26+)
Only adopt when explicitly requested by the user.
- Use native ,
glassEffect, and glass button stylesGlassEffectContainer - Wrap multiple glass elements in
GlassEffectContainer - Apply after layout and visual modifiers
.glassEffect() - Use only for tappable/focusable elements
.interactive() - Use with
glassEffectIDfor morphing transitions@Namespace
Quick Reference
Property Wrapper Selection (Modern)
| Wrapper | Use When |
|---|---|
| Internal view state (must be |
| Child modifies parent's state |
| Injected |
| Read-only value from parent |
| Read-only value watched via |
Legacy (Pre-iOS 17):
| Wrapper | Use When |
|---|---|
| View owns an |
| View receives an |
Modern API Replacements
| Deprecated | Modern Alternative |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
Liquid Glass Patterns
swift
// Basic glass effect with fallback
if #available(iOS 26, *) {
content
.padding()
.glassEffect(.regular.interactive(), in: .rect(cornerRadius: 16))
} else {
content
.padding()
.background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: 16))
}
// Grouped glass elements
GlassEffectContainer(spacing: 24) {
HStack(spacing: 24) {
GlassButton1()
GlassButton2()
}
}
// Glass buttons
Button("Confirm") { }
.buttonStyle(.glassProminent)Review Checklist
State Management
- Using instead of
@Observablefor new codeObservableObject - classes marked with
@Observable(if needed)@MainActor - Using with
@Stateclasses (not@Observable)@StateObject - and
@Stateproperties are@StateObjectprivate - Passed values NOT declared as or
@State@StateObject - only where child modifies parent state
@Binding - for injected
@Bindableneeding bindings@Observable - Nested avoided (or passed directly to child views)
ObservableObject
Modern APIs (see references/modern-apis.md
)
references/modern-apis.md- Using instead of
foregroundStyle()foregroundColor() - Using instead of
clipShape(.rect(cornerRadius:))cornerRadius() - Using API instead of
TabtabItem() - Using instead of
Button(unless need location/count)onTapGesture() - Using instead of
NavigationStackNavigationView - Avoiding
UIScreen.main.bounds - Using alternatives to when possible
GeometryReader - Button images include text labels for accessibility
Sheets & Navigation (see references/sheet-navigation-patterns.md
)
references/sheet-navigation-patterns.md- Using for model-based sheets
.sheet(item:) - Sheets own their actions and dismiss internally
- Using for type-safe navigation
navigationDestination(for:)
ScrollView (see references/scroll-patterns.md
)
references/scroll-patterns.md- Using with stable IDs for programmatic scrolling
ScrollViewReader - Using instead of initializer parameter
.scrollIndicators(.hidden)
Text & Formatting (see references/text-formatting.md
)
references/text-formatting.md- Using modern Text formatting (not )
String(format:) - Using for search filtering
localizedStandardContains()
View Structure (see references/view-structure.md
)
references/view-structure.md- Using modifiers instead of conditionals for state changes
- Complex views extracted to separate subviews
- Views kept small for performance
- Container views use
@ViewBuilder let content: Content
Performance (see references/performance-patterns.md
)
references/performance-patterns.md- View kept simple and pure (no side effects)
body - Passing only needed values (not large config objects)
- Eliminating unnecessary dependencies
- State updates check for value changes before assigning
- Hot paths minimize state updates
- No object creation in
body - Heavy computation moved out of
body
List Patterns (see references/list-patterns.md
)
references/list-patterns.md- ForEach uses stable identity (not )
.indices - Constant number of views per ForEach element
- No inline filtering in ForEach
- No in list rows
AnyView
Layout (see references/layout-best-practices.md
)
references/layout-best-practices.md- Avoiding layout thrash (deep hierarchies, excessive GeometryReader)
- Gating frequent geometry updates by thresholds
- Business logic separated into testable models
- Action handlers reference methods (not inline logic)
- Using relative layout (not hard-coded constants)
- Views work in any context (context-agnostic)
Liquid Glass (iOS 26+)
- with fallback for Liquid Glass
#available(iOS 26, *) - Multiple glass views wrapped in
GlassEffectContainer - applied after layout/appearance modifiers
.glassEffect() - only on user-interactable elements
.interactive() - Shapes and tints consistent across related elements
References
- - Property wrappers and data flow (prefer
references/state-management.md)@Observable - - View composition, extraction, and container patterns
references/view-structure.md - - Performance optimization techniques and anti-patterns
references/performance-patterns.md - - ForEach identity, stability, and list best practices
references/list-patterns.md - - Layout patterns, context-agnostic views, and testability
references/layout-best-practices.md - - Modern API usage and deprecated replacements
references/modern-apis.md - - Sheet presentation and navigation patterns
references/sheet-navigation-patterns.md - - ScrollView patterns and programmatic scrolling
references/scroll-patterns.md - - Modern text formatting and string operations
references/text-formatting.md - - AsyncImage, image downsampling, and optimization
references/image-optimization.md - - iOS 26+ Liquid Glass API
references/liquid-glass.md
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 and
@MainActor@Observable - We optimize for performance and maintainability
- We follow Apple's Human Interface Guidelines and API design patterns