Jko-claude-plugins swiftui-expert

This skill should be used when the user is building, reviewing, or debugging SwiftUI views and apps. Detects iOS and Swift version from the project. Covers creating views, state management with @Observable, NavigationStack routing, animations, accessibility, performance optimization, Liquid Glass adoption, design systems, and clean code architecture. Use when the user asks things like "create a SwiftUI list view", "my @State isn't updating", "add navigation to my app", "make this accessible", "optimize SwiftUI performance", "add Liquid Glass to my toolbar", "fix my Swift concurrency warning", "set up SwiftData models", "critique my SwiftUI code", "fix my SwiftUI layout", "create a custom ViewModifier", or "add Dark Mode support".

install
source · Clone the upstream repo
git clone https://github.com/johnkozaris/jko-claude-plugins
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/johnkozaris/jko-claude-plugins "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/swiftui/skills/swiftui-expert" ~/.claude/skills/johnkozaris-jko-claude-plugins-swiftui-expert && rm -rf "$T"
manifest: plugins/swiftui/skills/swiftui-expert/SKILL.md
source content

SwiftUI Expert

Provide expert guidance on SwiftUI development. Detect the project's deployment target and Swift version from

Package.swift
,
.xcodeproj
, or project settings and adapt guidance accordingly. Apply modern API usage, clean code principles, design craft, accessibility, and performance best practices. Do not invent APIs — if unsure, say so.

Core Principles

  • Target the latest stable iOS and Swift unless the project specifies otherwise. Check the deployment target before suggesting version-gated APIs.
  • Prefer SwiftUI-native solutions. Avoid UIKit unless explicitly needed or for gaps SwiftUI cannot fill.
  • Do not introduce third-party frameworks without asking first.
  • Each type (struct, class, enum) belongs in its own file. Flag files with multiple type definitions.
  • Use a feature-based folder structure, not layer-based (no "ViewModels/" folder).
  • Code must adhere to Apple's Human Interface Guidelines.

Modern API — Always Use

Replace deprecated API immediately. See

references/modern-api.md
for the complete table.

Key replacements:

  • foregroundStyle()
    not
    foregroundColor()
  • clipShape(.rect(cornerRadius:))
    not
    cornerRadius()
  • NavigationStack
    /
    NavigationSplitView
    not
    NavigationView
  • @Observable
    not
    ObservableObject
    /
    @Published
    /
    @StateObject
    /
    @ObservedObject
  • navigationDestination(for:)
    not
    NavigationLink(destination:)
  • Tab
    API not
    tabItem()
  • sensoryFeedback()
    not UIKit haptics
  • #Preview
    not
    PreviewProvider
  • containerRelativeFrame()
    or
    visualEffect()
    not
    GeometryReader
    (when possible)
  • Task.sleep(for:)
    not
    Task.sleep(nanoseconds:)
  • @Entry
    macro for custom environment/focus/transaction keys

State Management

  • @State
    must be
    private
    . It is owned by the view that creates it.
  • @Observable
    classes must be
    @MainActor
    (unless the project uses default MainActor isolation).
  • Use
    @State
    for ownership of
    @Observable
    objects,
    @Bindable
    for bindings to them,
    @Environment
    for passing them.
  • Never use
    @AppStorage
    inside
    @Observable
    classes — it will not trigger view updates.
  • Never create
    Binding(get:set:)
    in body — use
    @State
    +
    onChange()
    .
  • Nested
    @Observable
    works fine. Nested
    ObservableObject
    does not propagate changes.
  • For
    @Observable
    classes, mark properties you don't want tracked with
    @ObservationIgnored
    .

See

references/state-data.md
for property wrapper decision flowchart and SwiftData rules.

View Composition & Clean Code

  • Strongly prefer extracting subviews into separate
    View
    structs over computed properties or methods returning
    some View
    . Computed properties get no re-evaluation isolation from
    @Observable
    — separate structs allow SwiftUI to skip unchanged subviews entirely.
  • Keep
    body
    short and computation-free. No sorting, filtering, or formatter creation in
    body
    .
  • Extract button actions into methods. No inline business logic in
    task()
    ,
    onAppear()
    , or
    body
    .
  • Apply DRY: repeated styling →
    ViewModifier
    . Repeated layout → extracted
    View
    . Repeated logic → model/service method.
  • Apply Single Responsibility: each view does one thing. Each model owns one domain. Each file contains one type.
  • Apply Open/Closed: extend behavior through protocols and extensions, not by modifying existing types. Use
    ViewModifier
    +
    View
    extension for reusable styling.
  • Prefer
    overlay
    /
    background
    for decoration;
    ZStack
    for peer composition.
  • Container views: use
    @ViewBuilder let content: Content
    (not stored closures).

See

references/view-composition.md
for patterns, ordering conventions, and anti-patterns.

Design Craft

  • Establish a clear visual direction — commit to an aesthetic, don't mix styles.
  • Avoid generic AI aesthetics: no cards for everything, no gratuitous gradients/materials/shadows, no bouncy animations everywhere, no web-style CTA buttons. Use native iOS components. Apply the squint test — hierarchy should be visible even blurred.
  • Define design tokens (colors, typography, spacing, corner radii, animation timings) in a shared constants enum. One source of truth.
  • Follow the 60-30-10 color rule: 60% dominant neutral, 30% secondary, 10% accent.
  • Use semantic color names for role, not RGB. Support dark mode with system colors or asset catalog.
  • Establish clear typographic hierarchy: 3-4 levels max. Use weight contrast more than size contrast.
  • Use Dynamic Type fonts exclusively. Never hardcode
    .font(.system(size:))
    .
  • Minimum 44x44pt tap targets. Handle all interaction states (default, pressed, disabled, loading, error, success).
  • Write clear UX copy: verb-based button labels, actionable error messages, helpful empty states.

See

references/design-craft.md
for visual hierarchy, color harmony, typography pairing, interaction states, spacing tokens, UX writing, and HIG alignment.

Animation & Motion

  • Use
    withAnimation { }
    (explicit) for control. Use
    .animation(_:value:)
    for implicit — always with a
    value:
    parameter.
  • Prefer GPU-friendly transforms (
    offset
    ,
    scale
    ,
    rotation
    ) over layout changes (
    frame
    ).
  • Use
    @Animatable
    macro (iOS 26+) not manual
    animatableData
    .
  • Chain animations via
    withAnimation
    completion closures, not delays.
  • matchedGeometryEffect
    with
    @Namespace
    for hero transitions.
  • Respect
    accessibilityReduceMotion
    — replace motion with opacity.
  • Spring animations for natural feel.
    .bouncy
    for playful,
    .smooth
    for subtle.

See

references/animation.md
for phase/keyframe animators, transitions, and Liquid Glass morphing.

Accessibility

  • VoiceOver: every interactive element needs a text label.
    Button("Add", systemImage: "plus", action:)
    not icon-only.
  • Use
    Image(decorative:)
    or
    accessibilityHidden()
    for decorative images.
  • Never use
    onTapGesture()
    when
    Button
    works. If you must, add
    .accessibilityAddTraits(.isButton)
    .
  • Respect
    accessibilityDifferentiateWithoutColor
    — don't rely on color alone.
  • Use
    accessibilityInputLabels()
    for complex/changing button labels.
  • Test with VoiceOver and Accessibility Inspector.

See

references/accessibility.md
for Dynamic Type, element grouping, custom controls, and charts accessibility.

Performance

  • Ternary expressions over
    if/else
    view branching (avoids
    _ConditionalContent
    ).
  • No
    AnyView
    . Use
    @ViewBuilder
    ,
    Group
    , or generics.
  • Fine-grained
    @Observable
    models — avoid broad dependencies on arrays.
  • Use
    LazyVStack
    /
    LazyHStack
    for large data sets.
  • task()
    over
    onAppear()
    for async (auto-cancellation).
  • Keep view initializers trivial. Defer work to
    task()
    .
  • Debug with
    Self._printChanges()
    and random background colors.

See

references/performance.md
for the full code smell catalog and remediation patterns.

Concurrency

  • Always
    async
    /
    await
    over closures. Never use GCD (
    DispatchQueue
    ).
  • @MainActor
    on
    @Observable
    classes (if not using default MainActor isolation).
  • Use
    .task
    modifier for async work in views.
  • Prefer
    Task
    over
    Task.detached
    (inherits actor context).
  • Flag unprotected mutable shared state.
  • Assume strict concurrency. Flag
    @Sendable
    violations.

See

references/concurrency.md
for actor patterns, Sendable, and Swift 6 migration.

Navigation & Presentation

  • NavigationStack
    with
    navigationDestination(for:)
    . Never mix with
    NavigationLink(destination:)
    .
  • sheet(item:)
    over
    sheet(isPresented:)
    for optional data.
  • Attach
    confirmationDialog()
    to its trigger (for Liquid Glass animations).
  • Single "OK" alert buttons can be omitted.
  • TabView(selection:)
    binds to an enum, not Int/String.

See

references/navigation.md
for split views, inspector patterns, and deep links.

Liquid Glass (iOS 26+)

  • Recompile with Xcode 26 for automatic adoption on NavigationBar, TabBar, Toolbar.
  • Apply
    .glassEffect()
    after layout and visual modifiers.
  • Use
    GlassEffectContainer
    when multiple glass elements coexist.
  • .interactive()
    only on tappable/focusable elements.
  • .buttonStyle(.glass)
    /
    .buttonStyle(.glassProminent)
    for actions.
  • Gate with
    #available(iOS 26, *)
    and provide
    .ultraThinMaterial
    fallback.

See

references/liquid-glass.md
for morphing transitions, design system notes, and scroll edge effects.