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.
For architectural decisions (MVVM, MVC, VIPER, Clean Architecture, Coordinator, two-layer view patterns), defer to the
architecture-design-skill
.
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-composition.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 (see
references/liquid-glass.md
)
2) Improve existing SwiftUI code
- Audit state management for correct wrapper selection (prefer over )
- Replace deprecated APIs with modern equivalents (see
references/modern-apis.md
)
- Extract complex views into separate subviews (see
references/view-composition.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 (see
references/animation-basics.md
, references/animation-transitions.md
)
- Suggest image downsampling when is used (optional, 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 for shared state (with if not using default actor isolation)
- Structure views for optimal diffing: extract subviews early, keep views small (see
references/view-composition.md
)
- Separate business logic into testable models when needed (see
references/state-management.md#when-to-use-a-viewmodel
)
- Use correct animation patterns (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 and provide fallbacks
Core Guidelines
State Management
- Always prefer over for new code
- Mark classes with unless using default actor isolation
- Always mark and as (makes dependencies clear)
- Never declare passed values as or (they only accept initial values)
- Use with classes (not )
- only when child needs to modify parent state
- for injected objects needing bindings
- Use for read-only values; + for reactive reads
- Legacy: for owned ; for injected
- Nested doesn't work (pass nested objects directly); handles nesting fine
When to Use a ViewModel
Use a separate model class (often called a ViewModel) when:
- Testability: you need to unit-test state transitions or business logic
- Complex state: multiple interdependent state properties that evolve together
- Async coordination: orchestrating multiple async operations with loading/error state
- Reuse across views: the same logic is needed in more than one view
For simple views with straightforward display logic, keeping
directly in the view is fine and often cleaner.
This is a practical guide, not an MVVM mandate — see
architecture-design-skill
if you need to decide on an app-wide architectural pattern.
View Composition
- Prefer modifiers over conditional views for state changes (maintains view identity)
- Extract complex views into separate subviews for performance (not functions)
- Keep views small; each view should have one clear responsibility
- Keep view simple and pure (no side effects or complex logic)
- Use functions only for small, simple sections
- Prefer
@ViewBuilder let content: Content
over closure-based content properties in containers
- Use specialized built-in components when they fit: , , ,
- Build custom view styles (, ) to reuse styling across instances
- 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)
Reusability Recommendation
Prefer passing primitive/value types to presentational (content) views rather than full model objects. This makes views more reusable, simplifies previews, and reduces dependencies.
swift
// Preferred for pure display views
struct UserRow: View {
let name: String
let avatarURL: URL?
let isOnline: Bool
}
// Acceptable when the view needs the full model or has bindings to it
struct UserEditor: View {
@Bindable var user: UserModel
}
This is a recommendation for reusability, not a strict rule. Views that edit or coordinate model state often need the model directly.
Modern APIs
- Use instead of
- Use
clipShape(.rect(cornerRadius:))
instead of
- Use API instead of (iOS 18+)
- Use instead of (unless need location/count)
- Use instead of
- Use
navigationDestination(for:)
for type-safe navigation
- Use two-parameter or no-parameter variant
- Use for rendering SwiftUI views
- Use instead of for model-based content
- Sheets should own their actions and call internally
- Use for programmatic scrolling with stable IDs
- Avoid for sizing
- Avoid when alternatives exist (e.g., )
Swift Best Practices
- Use modern Text formatting ( parameters, not )
- Use
localizedStandardContains()
for user-input filtering (not )
- Prefer static member lookup ( vs )
- Use modifier for automatic cancellation of async work
- Use for value-dependent tasks
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 , , scroll handlers
- Minimize work in frequently executed code paths
- Use / for large lists
- Use stable identity for (never for dynamic content)
- Ensure constant number of views per element
- Avoid inline filtering in (prefilter and cache)
- Avoid in list rows
- Consider POD views for fast diffing (or wrap expensive views in POD parents)
- Suggest image downsampling when is encountered (optional optimization)
- Avoid layout thrash (deep hierarchies, excessive )
- Gate frequent geometry updates by thresholds
- Use to debug unexpected view updates
Animations
- Use with value parameter (deprecated version without value is too broad)
- Use for event-driven animations (button taps, gestures)
- Prefer transforms (, , ) over layout changes () for performance
- Transitions require animations outside the conditional structure
- Custom implementations must have explicit
- Use for multi-step sequences (iOS 17+)
- Use for precise timing control (iOS 17+)
- Animation completion handlers need 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 , , and glass button styles
- Wrap multiple glass elements in
- Apply after layout and visual modifiers
- Use only for tappable/focusable elements
- Use with for morphing transitions
Quick Reference
Property Wrapper Selection (Modern)
| Wrapper | Use When |
|---|
| Internal view state (must be ), or owned class |
| Child modifies parent's state |
| Injected needing bindings |
| Read-only value from parent |
| Read-only value watched via |
Legacy (Pre-iOS 17):
| Wrapper | Use When |
|---|
| View owns an (use with instead) |
| View receives an |
Modern API Replacements
| Deprecated | Modern Alternative |
|---|
| |
| clipShape(.rect(cornerRadius:))
|
| API (iOS 18+) |
| (unless need location/count) |
| |
onChange(of:) { value in }
| onChange(of:) { old, new in }
or |
| |
| or |
| .scrollIndicators(.hidden)
|
String(format: "%.2f", value)
| Text(value, format: .number.precision(.fractionLength(2)))
|
| string.localizedStandardContains(search)
(for user input) |
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
Modern APIs (see references/modern-apis.md
)
Sheets & Navigation (see references/navigation-patterns.md
)
ScrollView (see references/scroll-patterns.md
)
Text & Formatting (see references/text-formatting.md
)
View Composition (see references/view-composition.md
)
Performance (see references/performance-patterns.md
)
List Patterns (see references/list-patterns.md
)
Layout (see references/view-composition.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, data flow, when to use a ViewModel
references/view-composition.md
— View extraction, styles, specialized views, reusability
references/performance-patterns.md
— Performance optimization techniques and anti-patterns
references/list-patterns.md
— ForEach identity, stability, and list best practices
references/modern-apis.md
— Modern API usage and deprecated replacements
references/animation-basics.md
— Core animation concepts, implicit/explicit animations
references/animation-transitions.md
— Transitions, custom transitions, Animatable protocol
references/animation-advanced.md
— Transactions, phase/keyframe animations, completion handlers
references/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 (adopt only when explicitly requested)
Philosophy
This skill focuses on facts and best practices, not architectural opinions:
- We don't enforce specific architectures (e.g., MVVM, VIPER) — see
architecture-design-skill
- We do encourage separating business logic for testability when warranted
- We prioritize modern APIs over deprecated ones
- We emphasize thread safety with and
- We optimize for performance and maintainability
- We follow Apple's Human Interface Guidelines and API design patterns
- We use "prefer"/"avoid" for recommendations; "always"/"never" only for correctness of API