Swift ui design iteration mcp loop
Skill esaldgut/ai-native-engineering-workspace/global-skills/apple/swift-ui-design-iteration-mcp-loop
A methodology for iterating SwiftUI UI with a visual, MCP-driven feedback loop — write a #Preview, render it headlessly, screenshot the running view, evaluate the bitmap against Apple's Human Interface Guidelines, then refine the code and repeat, keeping code + screenshot + evaluation on one commit so the loop doesn't drift. Encodes the HIG rules to check each pass (contrast, Dynamic Type, Reduce Transparency/Motion, touch targets, "glass on controls, not content"), a SwiftUI performance checklist (state granularity, no GeometryReader in lazy stacks, .drawingGroup for static layers), and which MCP tools do which step. Use when designing or polishing a view and you want grounded visual evaluation instead of guessing from code.From its SKILL.md
npx -y skills add esaldgut/ai-native-engineering-workspace --skill swift-ui-design-iteration-mcp-loopAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.1 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it
UI design iteration with an MCP visual loop (SwiftUI)
This is a workflow skill, not an API. It runs a tight loop:
#Preview → render →
screenshot → evaluate the bitmap against the
Human Interface Guidelines →
refine → repeat. The point is grounded visual feedback — judging a rendered pixel against HIG
instead of guessing from source. It leans on the project's MCP servers (Xcode + XcodeBuild + Apple
docs) to do each step without leaving the agent.
When to invoke
- You're designing or polishing a view and want to see it, evaluate it against HIG, and iterate — not ship the first compile.
- You're checking a view across Dynamic Type, dark mode, Reduce Transparency before calling it done.
- You suspect a performance problem (jank, over-invalidation) and want a checklist pass.
Announce on invoke: "Using swift-ui-design-iteration-mcp-loop to render, screenshot, and evaluate this view against HIG, then iterate."
Do not use this as a substitute for running real interaction tests — the loop evaluates appearance and layout. Behavior (gestures, navigation, data flow) still needs the app run and tests.
The loop (each pass)
- Write/extend a
#Previewwith the states that matter (light, dark, Dynamic Type XXL, an empty and a populated state). The previewbodyis@MainActor; provide model data inline. - Render headlessly —
mcp__xcode__RenderPreviewreturns the preview bitmap; or build+run on a simulator and capture withmcp__XcodeBuildMCP__screenshot. - Evaluate the bitmap against the HIG checklist below (multimodal: actually look at the pixels).
Query specifics with
mcp__xcode__DocumentationSearch/mcp__apple-docs__search_apple_docs. - Refine the code, then re-render. Keep code + screenshot + evaluation on the same commit so the feedback doesn't drift across changes.
The HIG rules to check each pass
| Check | Rule | How |
|---|---|---|
| Contrast | Text on any surface meets WCAG 4.5:1 | Inspect the screenshot; darken/strengthen if marginal |
| Dynamic Type | Layout survives .dynamicTypeSize(.accessibility3) | Add a Dynamic Type XXL #Preview |
| Reduce Transparency | Glass/material falls back to opaque | Preview with the trait; provide a solid fallback |
| Reduce Motion | Zoom/morph transitions simplify | Suppress glassEffectID morphs and large motion |
| Touch targets | Interactive elements ≥ 44×44 pt | Measure controls in the screenshot |
| Glass scope | Glass on controls/navigation, not body content | No frosted panels behind paragraphs |
| Color semantics | Use semantic colors (.primary, .tint) not hardcoded hex | Verify in light AND dark |
| Safe areas | Content respects notch/Dynamic Island/home indicator | Screenshot on a notched device |
The SwiftUI performance checklist
- State granularity: the smallest view possible owns each
@State; prefer@Observablewith per-property tracking so a change invalidates only what depends on it. - No
GeometryReaderinsideLazyVStack/LazyHStack— it forces eager layout and defeats laziness. UsecontainerRelativeFrame/onScrollGeometryChangeinstead. .drawingGroup()(Metal) for static, expensive layers (complex gradients/shadows) — but not on frequently-animated content, where it can hurt.- Hoist constants out of
body;bodyruns often. Don't allocate formatters/arrays per render. equatable()/EquatableViewto short-circuit re-renders of pure subviews when inputs are unchanged.- Measure with Instruments and the WWDC23 "Demystify SwiftUI performance" guidance — don't guess.
The MCP tooling map
| Step | Tool (this environment) |
|---|---|
Render a #Preview headlessly | mcp__xcode__RenderPreview |
| Build + run on a simulator | mcp__XcodeBuildMCP__build_run_sim |
| Screenshot the running view | mcp__XcodeBuildMCP__screenshot |
| Inspect the view hierarchy / coordinates | mcp__XcodeBuildMCP__snapshot_ui |
| Look up an HIG rule or API | mcp__xcode__DocumentationSearch, mcp__apple-docs__search_apple_docs |
Tool names are environment-specific; if these MCP servers aren't connected, the loop degrades to: build in Xcode → screenshot manually → evaluate → edit. The methodology is the durable part.
Canonical example: the previewable unit you iterate on
struct StatCard: View {
let title: String
let value: String
var body: some View {
VStack(alignment: .leading, spacing: 6) {
Text(title).font(.subheadline).foregroundStyle(.secondary) // semantic color
Text(value).font(.title2).bold()
}
.padding()
.background(.regularMaterial, in: .rect(cornerRadius: 16)) // material, not faux glass
.frame(minWidth: 44, minHeight: 44) // touch-target floor
}
}
#Preview("Light") { StatCard(title: "Followers", value: "12.4k").preferredColorScheme(.light) }
#Preview("Dark") { StatCard(title: "Followers", value: "12.4k").preferredColorScheme(.dark) }
#Preview("Dynamic Type XXL") { StatCard(title: "Followers", value: "12.4k").dynamicTypeSize(.accessibility3) }
#Preview("Reduce Transparency") {
StatCard(title: "Followers", value: "12.4k").environment(\.accessibilityReduceTransparency, true)
}
Decision aid: when NOT to / trade-offs
- Don't iterate forever. Two or three passes against the checklist is usually enough; perfectionism on a screen nobody's blocked on is waste.
#Previewasync caveat: unstructuredTask {}in a preview can behave oddly — drive async work through.taskwith preview-only model data, not detached tasks.- The loop can echo. If you change code between rendering and evaluating, you're judging stale pixels — keep each pass on one commit hash.
- HIG changes yearly. Re-query the guidelines each major OS cycle; a rule that held last year may have moved.
Related skills
global-skills/apple/swift-liquid-glass-design-system-ios26/SKILL.md— the glass rules this loop checks ("glass on controls, not content"; Reduce Transparency fallback).global-skills/apple/swift-ios26-native-ux-patterns/SKILL.md— the motion/transitions this loop evaluates against Reduce Motion.global-skills/apple/apple-anti-patterns/SKILL.md— the registry this loop feeds when it catches a repeatable mistake.global-skills/meta/skill-pattern-freshness-audit/SKILL.md— re-verify HIG + performance guidance.
Sources
- Preview(_:body:) macro · Human Interface Guidelines · DocC
- WWDC23 Demystify SwiftUI performance · WWDC25 Build a SwiftUI app with the new design
Last verified: 2026-06-03 against Apple Developer docs (#Preview macro confirmed:
Preview(_ name: String? = nil, @ViewBuilder body: @escaping @MainActor () -> any View)) + HIG +
WWDC23 #10160. MCP tool names reflect this environment's Xcode/XcodeBuild/apple-docs servers.
Re-check after: WWDC26 / Xcode 27, or by 2026-12-01. Decay risk: medium (HIG and the MCP tool
surface both evolve; the loop methodology is stable).
Found a drift? Run /skill-pattern-freshness-audit apple.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.