Use when writing, reviewing, or refactoring SwiftUI code for iOS or macOS, including state management and `@Observable` data flow, view composition and invalidation/performance, lists and `ForEach` identity, environment usage, localization, animations, Liquid Glass adoption, migrating soft-deprecated APIs, or Instruments `.trace` capture/analysis for hangs, hitches, CPU hotspots, or excessive view updates.
All-time #851Trending #1724First seen Jan 24, 2026
Expert SwiftUI code review, refactoring, and implementation guidance with iOS 26+ and macOS support.
Covers state management, view composition, performance optimization, and modern SwiftUI patterns across iOS 15+ through iOS 26+ Flags deprecated APIs and suggests modern replacements; gates version-specific features with #available and fallbacks Addresses lists, layouts, animations, accessibility, Swift Charts, and platform-specific patterns (macOS scenes, Liquid Glass) Enforces correctness rules for property wrappers, ForEach identity, and view diffing; presents performance improvements as optional suggestions
Similar popular skills
Related neighbors and high-traction skills in the same topics — useful to compare before installing.
Pick a template based on target kind — the SwiftUI template populates the SwiftUI lane on any real device: a physical iOS/iPadOS device or the host Mac. The only exception is the iOS Simulator, where the SwiftUI lane comes back empty — switch to --template "Time Profiler" in that case (still gives Time Profiler + Hangs + Animation Hitches). Always check --list-devices: simulators kind → Time Profiler; devices kind (real devices and the host Mac) → default SwiftUI. Full decision table in references/trace-recording.md.
Start the recording. For agent-driven sessions where the user says "I'll tell you when I'm done", start in the background and use a stop-file:
``bash python3 "${SKILLDIR}/scripts/recordtrace.py" \ --device "<name|udid>" --attach "<AppName>" \ --stop-file /tmp/stop-trace --output ~/Desktop/session.trace `` For interactive sessions, just tell the user to press Ctrl+C when done.
Signal stop — when the user says they've finished exercising the app, touch /tmp/stop-trace. The script cleanly SIGINTs xctrace and waits up to 60s for finalisation.
Analyse the resulting trace (flow into the "Trace-driven improvement" workflow below).
Trigger whenever the user's request references a .trace file. A target SwiftUI source file is optional — if given, cite specific lines; if not, recommend where to look based on view names and symbols the trace already reveals.
Full reference: references/trace-analysis.md. Summary of the composition pattern:
Scope the analysis. Ask yourself: does the user want the whole trace, or a slice?
- "focus on X / after X / between X and Y / during X" → resolve to a window first (see step 2). - No scoping cue → analyse the whole trace.
Resolve a window (only if the user scoped). The parser exposes two discovery modes:
``bash # Find a log that marks the start/end of the region of interest: python3 "${SKILLDIR}/scripts/analyzetrace.py" --trace <path> \ --list-logs --log-message-contains "loaded feed" --log-limit 5 # Or list ossignpost intervals (paired begin/end), filterable by name: python3 "${SKILLDIR}/scripts/analyzetrace.py" --trace <path> \ --list-signposts --signpost-name-contains "ImageDecode" ` Both modes accept --window STARTMS:ENDMS to scope discovery. Pick the timems (for logs) or startms/endms (for signposts) that match the user's description. Build a window like --window 10400:11700`.
Interpret with references/trace-analysis.md — key diagnostics:
- mainrunningcoveragepct inside each correlation (<25% = blocked; ≥75% = CPU-bound). - swiftui-causes.topsources reveals why updates keep happening — high-edge-count sources like UserDefaultObserver.send() or wide EnvironmentWriter entries are structural invalidation bugs. Fixing one often collapses many downstream hot views.
When a specific view shows as expensive, ask who's invalidating it. Use --fanin-for "<view name>" to get the ranked list of source nodes driving the updates.
Optionally ground in source. If the user pointed at a file, read it and match view names / user-code symbols against identifiers there. If not, recommend which files to open based on the view names SwiftUI reported.
Return a prioritised plan. Cite evidence (coverage %, hot symbol, overlapping view, log timestamp, cause-graph edges) and route each recommendation to a Topic Router reference.
Only edit code if the user asked for edits.
Topic Router
Consult the reference file for each topic relevant to the current task:
Topic
Reference
State management
references/state-management.md
View composition
references/view-structure.md
Performance
references/performance-patterns.md
Lists and ForEach
references/list-patterns.md
Layout
references/layout-best-practices.md
Sheets and navigation
references/sheet-navigation-patterns.md
ScrollView, scroll position, and scroll geometry
references/scroll-patterns.md
Focus management
references/focus-patterns.md
Animations (basics)
references/animation-basics.md
Animations (transitions)
references/animation-transitions.md
Animations (advanced)
references/animation-advanced.md
Accessibility
references/accessibility-patterns.md
Swift Charts
references/charts.md
Charts accessibility
references/charts-accessibility.md
Image optimization
references/image-optimization.md
Liquid Glass (iOS 26+)
references/liquid-glass.md
macOS scenes
references/macos-scenes.md
macOS window styling
references/macos-window-styling.md
macOS views
references/macos-views.md
Text patterns
references/text-patterns.md
Localization
references/localization.md
Deprecated API lookup
references/latest-apis.md
Handling soft-deprecated APIs
references/soft-deprecation.md
Previews
references/previews.md
Instruments trace analysis
references/trace-analysis.md
Instruments trace recording
references/trace-recording.md
Correctness Checklist
These are hard rules -- violations are always bugs:
@State properties are private
@Binding only where a child modifies parent state
Passed values never declared as @State or @StateObject (they ignore updates)
@StateObject for view-owned objects; @ObservedObject for injected
iOS 17+: @State with @Observable; @Bindable for injected observables needing bindings
ForEach uses stable identity (never .indices/\.offset; id outlives the view and isn't derived from mutable content)
Constant number of views per ForEach element; List rows are unary
No closures stored in custom @Environment/@FocusedValue keys
Custom @Entry default values are stable (no Model()/Date()/UUID() expressions)
.animation(_:value:) always includes the value parameter
@FocusState properties are private
No redundant @FocusState writes inside tap gesture handlers on .focusable() views
iOS 26+ APIs gated with #available and fallback provided
import Charts present in files using chart types
Previews use self-contained mock data; no dependency on live services or network
References
references/latest-apis.md -- Read first for every task. Deprecated-to-modern API transitions (iOS 15+ through iOS 26+)
references/state-management.md -- Property wrappers, data flow, @Observable migration
references/soft-deprecation.md -- How to behave with soft-deprecated APIs (when to migrate, scoping rule, don't migrate during unrelated edits)
references/trace-analysis.md -- Parse Instruments .trace files via scripts/analyze_trace.py; interpret main-thread coverage, high-severity SwiftUI updates, hitch narratives, and map findings back to source files
references/trace-recording.md -- Record a new trace via scripts/record_trace.py: attach to a running app, launch one fresh, or capture a manually-stopped session; supports stop-file for agent-driven flows