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
Added on

NPX Install

npx skill4agent add petekp/claude-code-setup swiftui-expert-skill

SKILL.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
    @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
    )
  • 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
    )
  • 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

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)

WrapperUse 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):
WrapperUse When
@StateObject
View owns an
ObservableObject
(use
@State
with
@Observable
instead)
@ObservedObject
View receives an
ObservableObject

Modern API Replacements

DeprecatedModern 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

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
    @Observable
    instead of
    ObservableObject
    for new code
  • @Observable
    classes marked with
    @MainActor
    (if needed)
  • Using
    @State
    with
    @Observable
    classes (not
    @StateObject
    )
  • @State
    and
    @StateObject
    properties are
    private
  • Passed values NOT declared as
    @State
    or
    @StateObject
  • @Binding
    only where child modifies parent state
  • @Bindable
    for injected
    @Observable
    needing bindings
  • Nested
    ObservableObject
    avoided (or passed directly to child views)

Modern APIs (see
references/modern-apis.md
)

  • Using
    foregroundStyle()
    instead of
    foregroundColor()
  • Using
    clipShape(.rect(cornerRadius:))
    instead of
    cornerRadius()
  • Using
    Tab
    API instead of
    tabItem()
  • Using
    Button
    instead of
    onTapGesture()
    (unless need location/count)
  • Using
    NavigationStack
    instead of
    NavigationView
  • Avoiding
    UIScreen.main.bounds
  • Using alternatives to
    GeometryReader
    when possible
  • Button images include text labels for accessibility

Sheets & Navigation (see
references/sheet-navigation-patterns.md
)

  • Using
    .sheet(item:)
    for model-based sheets
  • Sheets own their actions and dismiss internally
  • Using
    navigationDestination(for:)
    for type-safe navigation

ScrollView (see
references/scroll-patterns.md
)

  • Using
    ScrollViewReader
    with stable IDs for programmatic scrolling
  • Using
    .scrollIndicators(.hidden)
    instead of initializer parameter

Text & Formatting (see
references/text-formatting.md
)

  • Using modern Text formatting (not
    String(format:)
    )
  • Using
    localizedStandardContains()
    for search filtering

View Structure (see
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
)

  • View
    body
    kept simple and pure (no side effects)
  • 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
)

  • ForEach uses stable identity (not
    .indices
    )
  • Constant number of views per ForEach element
  • No inline filtering in ForEach
  • No
    AnyView
    in list rows

Layout (see
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+)

  • #available(iOS 26, *)
    with fallback for Liquid Glass
  • Multiple glass views wrapped in
    GlassEffectContainer
  • .glassEffect()
    applied after layout/appearance modifiers
  • .interactive()
    only on user-interactable elements
  • Shapes and tints consistent across related elements

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/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