← SwiftUI ExpertCONTENT HISTORYWHAT CHANGED · RULE-BASED ANALYSIS
Update to SwiftUI Expert
Snapshot Sep 30, 2026 · 23:14 UTC · version 5.2.0
Collection source: not recorded for this historical snapshot.
First saved snapshot
No earlier snapshot is available to establish a change.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Use when writing, reviewing, or refactoring SwiftUI code for iOS or macOS, including state and `@Observable` data flow, view composition, resizable layouts, safe areas, display scale, performance, lists, environment, localization, animation, Liquid Glass, and API migration. Also use for iPhone Duo, foldable, or large-display layouts (`NavigationSplitView` on large displays, two-column reflow, foldable grids, `ArrangementView`, `ReservedRegion`), hinge effects, vertical bars, `@State` initialization or synthesized-property diagnostics, `@ContentBuilder` ambiguity, `reorderable` drag/drop, custom `AsyncImage` `URLSession`, swipe actions outside List, item-bound `alert`/`confirmationDialog`, `ToolbarOverflowMenu`, `AnimatableValues`, Document APIs (`Document`/`DocumentReader`), and Instruments `.trace` capture or analysis.",
"included_files": [
{
"relative_path": "agents/openai.yaml",
"size_in_bytes": 228
},
{
"relative_path": "assets/logo-small.png",
"size_in_bytes": 29649
},
{
"relative_path": "assets/logo.png",
"size_in_bytes": 398841
},
{
"relative_path": "assets/logo.svg",
"size_in_bytes": 2006
},
{
"relative_path": "references/accessibility-patterns.md",
"size_in_bytes": 6073
},
{
"relative_path": "references/animation-advanced.md",
"size_in_bytes": 11439
},
{
"relative_path": "references/animation-basics.md",
"size_in_bytes": 6865
},
{
"relative_path": "references/animation-transitions.md",
"size_in_bytes": 8052
},
{
"relative_path": "references/charts-accessibility.md",
"size_in_bytes": 6291
},
{
"relative_path": "references/charts.md",
"size_in_bytes": 17219
},
{
"relative_path": "references/document-apps.md",
"size_in_bytes": 11319
},
{
"relative_path": "references/environment-patterns.md",
"size_in_bytes": 7278
},
{
"relative_path": "references/focus-patterns.md",
"size_in_bytes": 8877
},
{
"relative_path": "references/image-optimization.md",
"size_in_bytes": 10617
},
{
"relative_path": "references/iphone-duo.md",
"size_in_bytes": 7254
},
{
"relative_path": "references/latest-apis.md",
"size_in_bytes": 25032
},
{
"relative_path": "references/layout-best-practices.md",
"size_in_bytes": 19754
},
{
"relative_path": "references/liquid-glass.md",
"size_in_bytes": 12660
},
{
"relative_path": "references/list-patterns.md",
"size_in_bytes": 21013
},
{
"relative_path": "references/localization.md",
"size_in_bytes": 9920
},
{
"relative_path": "references/macos-scenes.md",
"size_in_bytes": 9757
},
{
"relative_path": "references/macos-views.md",
"size_in_bytes": 11027
},
{
"relative_path": "references/macos-window-styling.md",
"size_in_bytes": 8815
},
{
"relative_path": "references/modifier-patterns.md",
"size_in_bytes": 2530
},
{
"relative_path": "references/performance-patterns.md",
"size_in_bytes": 11933
},
{
"relative_path": "references/previews.md",
"size_in_bytes": 8327
},
{
"relative_path": "references/scroll-patterns.md",
"size_in_bytes": 10839
},
{
"relative_path": "references/sheet-navigation-patterns.md",
"size_in_bytes": 13932
},
{
"relative_path": "references/soft-deprecation.md",
"size_in_bytes": 3002
},
{
"relative_path": "references/state-management.md",
"size_in_bytes": 22009
},
{
"relative_path": "references/styled-text-editing.md",
"size_in_bytes": 8938
},
{
"relative_path": "references/text-patterns.md",
"size_in_bytes": 1637
},
{
"relative_path": "references/toolbar-patterns.md",
"size_in_bytes": 10159
},
{
"relative_path": "references/trace-analysis.md",
"size_in_bytes": 14415
},
{
"relative_path": "references/trace-recording.md",
"size_in_bytes": 5939
},
{
"relative_path": "references/view-structure.md",
"size_in_bytes": 28979
},
{
"relative_path": "references/webkit-integration.md",
"size_in_bytes": 9732
},
{
"relative_path": "scripts/analyze_trace.py",
"size_in_bytes": 10155
},
{
"relative_path": "scripts/instruments_parser/__init__.py",
"size_in_bytes": 69
},
{
"relative_path": "scripts/instruments_parser/causes.py",
"size_in_bytes": 6092
},
{
"relative_path": "scripts/instruments_parser/correlate.py",
"size_in_bytes": 6367
},
{
"relative_path": "scripts/instruments_parser/events.py",
"size_in_bytes": 10804
},
{
"relative_path": "scripts/instruments_parser/hangs.py",
"size_in_bytes": 3507
},
{
"relative_path": "scripts/instruments_parser/hitches.py",
"size_in_bytes": 4634
},
{
"relative_path": "scripts/instruments_parser/summary.py",
"size_in_bytes": 9291
},
{
"relative_path": "scripts/instruments_parser/swiftui.py",
"size_in_bytes": 6798
},
{
"relative_path": "scripts/instruments_parser/time_profiler.py",
"size_in_bytes": 4385
},
{
"relative_path": "scripts/instruments_parser/xctrace.py",
"size_in_bytes": 3607
},
{
"relative_path": "scripts/instruments_parser/xml_utils.py",
"size_in_bytes": 7536
},
{
"relative_path": "scripts/record_trace.py",
"size_in_bytes": 10831
}
],
"name": "swiftui-expert-skill",
"skill_md_contents": "---\nname: swiftui-expert-skill\ndescription: Use when writing, reviewing, or refactoring SwiftUI code for iOS or macOS, including state and `@Observable` data flow, view composition, resizable layouts, safe areas, display scale, performance, lists, environment, localization, animation, Liquid Glass, and API migration. Also use for iPhone Duo, foldable, or large-display layouts (`NavigationSplitView` on large displays, two-column reflow, foldable grids, `ArrangementView`, `ReservedRegion`), hinge effects, vertical bars, `@State` initialization or synthesized-property diagnostics, `@ContentBuilder` ambiguity, `reorderable` drag/drop, custom `AsyncImage` `URLSession`, swipe actions outside List, item-bound `alert`/`confirmationDialog`, `ToolbarOverflowMenu`, `AnimatableValues`, Document APIs (`Document`/`DocumentReader`), and Instruments `.trace` capture or analysis.\n---\n\n# SwiftUI Expert Skill\n\n## Operating Rules\n\n- Treat each `View` type as an invalidation boundary: give it only the data it reads and keep frequently changing dependencies close to the smallest affected subtree\n- Search `references/latest-apis.md` when writing, reviewing, or migrating API usage; look up only the APIs relevant to the task\n- Replace hard-deprecated APIs with modern equivalents. During feature work, flag soft-deprecated APIs and leave them in place (see `references/soft-deprecation.md`)\n- Prefer native SwiftUI APIs over UIKit/AppKit bridging unless bridging is necessary\n- Focus on correctness and performance; do not enforce specific architectures (MVVM, VIPER, etc.)\n- Encourage separating business logic from views for testability without mandating how\n- Follow Apple's Human Interface Guidelines and API design patterns\n- Only adopt Liquid Glass when explicitly requested by the user (see `references/liquid-glass.md`)\n- Present performance optimizations as suggestions, not requirements\n- Use `#available` gating with sensible fallbacks for version-specific APIs\n- For layout and rendering inputs, read the value nearest the SwiftUI view that consumes it; do not substitute process-global screen state\n\n## Task Workflow\n\n### Review existing SwiftUI code\n- Read the code under review and identify which topics apply\n- Flag deprecated APIs (compare against `references/latest-apis.md`); replace hard-deprecated APIs, and flag soft-deprecated APIs without rewriting them unless the user asked to migrate\n- Run the Topic Router below for each relevant topic\n- Validate `#available` gating and fallback paths for version-specific features\n- For broad codebase reviews, first identify smaller focus areas and present them one at a time; if the user requests a whole-codebase review, divide it into a TODO list\n\n### Improve existing SwiftUI code\n- Audit current implementation against the Topic Router topics\n- Replace hard-deprecated APIs with modern equivalents from `references/latest-apis.md`; flag soft-deprecated APIs and do not rewrite them during feature work\n- Refactor hot paths to reduce unnecessary state updates\n- Extract complex view bodies into separate subviews\n- Suggest image downsampling when `UIImage(data:)` is encountered (optional optimization, see `references/image-optimization.md`)\n\n### Implement new SwiftUI feature\n- Design data flow first: identify owned vs injected state\n- Structure views for optimal diffing (extract subviews early)\n- Apply correct animation patterns (implicit vs explicit, transitions)\n- Use `Button` for all tappable elements; add accessibility grouping and labels\n- Gate version-specific APIs with `#available` and provide fallbacks\n\n### Record a new Instruments trace\nTrigger when the user asks to \"record a trace\", \"profile the app\", \"capture a session\", etc. Full reference: `references/trace-recording.md`.\n\n1. **Confirm target** — attach to a running app, launch an app, or record all processes? If the user didn't say, ask. List connected devices when useful:\n ```bash\n python3 \"${SKILL_DIR}/scripts/record_trace.py\" --list-devices\n ```\n2. **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`.\n3. **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:\n ```bash\n python3 \"${SKILL_DIR}/scripts/record_trace.py\" \\\n --device \"<name|udid>\" --attach \"<AppName>\" \\\n --stop-file /tmp/stop-trace --output ~/Desktop/session.trace\n ```\n For interactive sessions, just tell the user to press Ctrl+C when done.\n4. **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.\n5. **Analyse** the resulting trace (flow into the \"Trace-driven improvement\" workflow below).\n\n### Trace-driven improvement (Instruments `.trace` provided)\nTrigger 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.\n\nFull reference: `references/trace-analysis.md`. Summary of the composition pattern:\n\n1. **Scope the analysis.** Ask yourself: does the user want the whole trace, or a slice?\n - \"focus on X / after X / between X and Y / during X\" → **resolve to a window first** (see step 2).\n - No scoping cue → analyse the whole trace.\n2. **Resolve a window (only if the user scoped).** The parser exposes two discovery modes:\n ```bash\n # Find a log that marks the start/end of the region of interest:\n python3 \"${SKILL_DIR}/scripts/analyze_trace.py\" --trace <path> \\\n --list-logs --log-message-contains \"loaded feed\" --log-limit 5\n # Or list os_signpost intervals (paired begin/end), filterable by name:\n python3 \"${SKILL_DIR}/scripts/analyze_trace.py\" --trace <path> \\\n --list-signposts --signpost-name-contains \"ImageDecode\"\n ```\n Both modes accept `--window START_MS:END_MS` to scope discovery. Pick the `time_ms` (for logs) or `start_ms`/`end_ms` (for signposts) that match the user's description. Build a window like `--window 10400:11700`.\n3. **Run the main analysis** (with or without `--window`):\n ```bash\n python3 \"${SKILL_DIR}/scripts/analyze_trace.py\" --trace <path> \\\n --json-only --top 10 [--window START_MS:END_MS]\n ```\n4. **Interpret with `references/trace-analysis.md`** — key diagnostics:\n - `main_running_coverage_pct` inside each correlation (<25% = blocked; ≥75% = CPU-bound).\n - `swiftui-causes.top_sources` 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.\n5. **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.\n6. **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.\n7. **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.\n8. Only edit code if the user asked for edits.\n\n### Topic Router\n\nConsult the reference file for each topic relevant to the current task:\n\n| Topic | Reference |\n|-------|-----------|\n| State management | `references/state-management.md` |\n| Environment and `@Entry` | `references/environment-patterns.md` |\n| View composition | `references/view-structure.md` |\n| View modifiers and identity | `references/modifier-patterns.md` |\n| Performance | `references/performance-patterns.md` |\n| Lists and ForEach | `references/list-patterns.md` |\n| Resizable layout, safe areas, two-column reflow, foldable grids, arrangements, and reserved regions | `references/layout-best-practices.md` |\n| iPhone Duo, foldable, or large-display screens (read first to choose the technique) | `references/iphone-duo.md` |\n| Sheets, navigation, and `NavigationSplitView` on large displays | `references/sheet-navigation-patterns.md` |\n| ScrollView, scroll position, and scroll geometry | `references/scroll-patterns.md` |\n| Focus management | `references/focus-patterns.md` |\n| Animations (basics) | `references/animation-basics.md` |\n| Animations (transitions) | `references/animation-transitions.md` |\n| Animations (advanced) | `references/animation-advanced.md` |\n| Accessibility | `references/accessibility-patterns.md` |\n| Swift Charts | `references/charts.md` |\n| Charts accessibility | `references/charts-accessibility.md` |\n| Image optimization and display scale | `references/image-optimization.md` |\n| Toolbars | `references/toolbar-patterns.md` |\n| Document-based apps | `references/document-apps.md` |\n| WebKit | `references/webkit-integration.md` |\n| Styled text editing | `references/styled-text-editing.md` |\n| Liquid Glass (iOS 26+) | `references/liquid-glass.md` |\n| macOS scenes | `references/macos-scenes.md` |\n| macOS window styling | `references/macos-window-styling.md` |\n| macOS views | `references/macos-views.md` |\n| Text patterns | `references/text-patterns.md` |\n| Localization | `references/localization.md` |\n| Deprecated API lookup | `references/latest-apis.md` |\n| Handling soft-deprecated APIs | `references/soft-deprecation.md` |\n| Previews | `references/previews.md` |\n| Instruments trace analysis | `references/trace-analysis.md` |\n| Instruments trace recording | `references/trace-recording.md` |\n\n## Correctness Checklist\n\nThese are hard rules -- violations are always bugs:\n\n- [ ] `@State` properties are `private`\n- [ ] `@Binding` only where a child modifies parent state\n- [ ] Changing parent-owned inputs are not stored as `@State`/`@StateObject`; intentional state seeds are documented as one-time\n- [ ] `@StateObject` for view-owned objects; `@ObservedObject` for injected\n- [ ] iOS 17+: `@State` with `@Observable`; `@Bindable` for injected observables needing bindings\n- [ ] `ForEach` uses stable identity (never `.indices`/`\\.offset`; id outlives the view and isn't derived from mutable content)\n- [ ] Constant number of views per `ForEach` element; `List` rows are unary\n- [ ] No closures stored in custom `@Environment`/`@FocusedValue` keys\n- [ ] Custom `@Entry` default values are stable (no `Model()`/`Date()`/`UUID()` expressions)\n- [ ] SwiftUI display scale comes from `@Environment(\\.displayScale)`, not global screen state\n- [ ] Safe-area content does not double-apply `GeometryProxy.safeAreaInsets`\n- [ ] `.animation(_:value:)` always includes the `value` parameter\n- [ ] `@FocusState` properties are `private`\n- [ ] No redundant `@FocusState` writes inside tap gesture handlers on `.focusable()` views\n- [ ] Version-specific APIs are gated with `#available` and have sensible fallbacks\n- [ ] `import Charts` present in files using chart types\n- [ ] Previews use self-contained mock data; no dependency on live services or network\n"
}SHA-256 of public snapshot: 3425dd123aaaf70c5ab44635f23a51d04dfb545aded9d47cbf45aba87724bb08