| name | swiftui-ui |
| description | Build native-feeling SwiftUI screens for the iOS 26 Liquid Glass era with Swift 6 — view composition, native-component selection, state-driven navigation, animations, @Observable view models, anti-AI-slop design rules, and a self-audit checklist. Use when asked to build, structure, improve, or review SwiftUI views or screens — "build a SwiftUI view", "make a screen", "my UI looks off", "NavigationStack", "sheet / detents", "TabView", "glassEffect / Liquid Glass", "List vs LazyVStack", "WidgetKit / Live Activity", "rich text editor", "drag and drop" — even when the user does not say "SwiftUI". For pure HIG audits defer to apple-hig; for Figma→code defer to figma-to-swiftui. |
swiftui-ui
Write SwiftUI that looks and feels like it shipped with iOS — native components,
state-driven navigation, GPU-cheap animation, and the spacing/typography/color
restraint that separates a polished app from AI slop. This is your pre-flight
checklist and reference set for any view, navigation flow, animation, or UI audit
in the iOS 26 Liquid Glass / Swift 6 era.
Scope vs sibling skills. This skill is about building the SwiftUI UI:
view structure, component choice, navigation, animation, design quality, audit.
For the canonical Apple HIG spec/audit reference use apple-hig. For turning a
Figma comp into code use figma-to-swiftui. They compose — cite HIG values
from apple-hig, build the views with this one.
The two failure modes this skill prevents
Almost every weak AI-built SwiftUI screen fails in one of two ways. Read this before
writing anything.
-
Over-customization — fighting the system instead of letting it work. Applying
.glassEffect() to content rows, stacking glass on glass, hand-rolling nav bars
and tab bars, custom-styled text inputs, custom dividers, bespoke gradients on
ordinary cards, LazyVStack where List belongs, animating the network request
instead of the result. iOS 26 gives you enormous polish for free — but only if
you reach for native components first.
-
Visual incoherence (slop) — arbitrary spacing (.padding(26), spacing: 18),
seven font sizes with no system, hardcoded Color.white.opacity(0.44) everywhere,
mismatched stroke widths, 22pt corner radii. The eye reads the inconsistency as
"off" even when it can't name why. Restraint over decoration — fewer colors,
fewer sizes, fewer spacing values, used consistently.
Pre-flight checklist (read before every change)
- Am I writing code inside the current project directory? All UI code belongs in
the working tree. For a brand-new project, scaffold a buildable
.xcodeproj first
(see references/project-setup.md) — never hand off a project that doesn't compile.
And if the request is vague about style ("make it look better", "what style?")
and no design direction is set, run the design-direction advisor first
(references/design-direction.md) — offer 2-3 directions, don't guess one.
- Is there a native component for this? Navigation, tabs, search, toolbars, sheets,
alerts, menus, toggles, sliders, pickers, forms, paywalls all have first-party
solutions. Check the catalog in references/native-components.md. Use them.
- Is my glass only on the navigation layer?
.glassEffect() belongs on floating
controls above content (FABs, floating toolbars, mini-players) — never on list rows,
cards, or backgrounds, never glass-on-glass. (references/liquid-glass.md)
- Is my view model
@MainActor @Observable? Not ObservableObject, not
@StateObject, not @Published. (references/state-and-architecture.md)
- Is my spacing on the grid and my type scale ≤ 5 sizes? 4/8/12/16/20/24/32/40/48
only; hierarchy through weight, not a zoo of sizes; semantic system colors, not
hardcoded opacities. (references/design-quality.md)
- Am I using
List for long homogeneous content? Not ScrollView + LazyVStack
unless the layout genuinely can't be a list.
- Am I animating the result, not the request?
withAnimation wraps the state
change when data lands, never the await that fetches it. (references/animations.md)
- Is navigation state-driven?
NavigationStack + value-based destinations, not a
bare ZStack pretending to be a screen, not deprecated NavigationView.
(references/navigation.md)
View architecture — one pattern, no bikeshedding
Views/ → ViewModels/ (@MainActor @Observable, one per screen) → Services/
(actors) → Models/ (@Model + Sendable DTOs); the full layout tree is in
state-and-architecture.md.
The rule: Views own state via @State on an @Observable view model. View models
call actor services. Services never touch UI. Dependencies flow via initializer
injection (screen-local) or @Environment (cross-cutting: auth, router, current user).
Split views aggressively. Each View struct does ONE thing. @State lives at the
lowest owner that needs it. No business logic inside body — delegate to the view
model. A subtree over ~15 lines or used more than once becomes its own view.
Full pattern with the ViewState<T> loading/error/loaded enum, @Observable migration
table, and DI setup: references/state-and-architecture.md.
Native component priority (reach here first)
Every common need has a first-party answer — NavigationStack/TabView,
List, .sheet, .contextMenu/.swipeActions, Form+TextField,
.refreshable, .searchable, ContentUnavailableView, glass button styles,
SubscriptionStoreView, SignInWithAppleButton, Gauge, PhotosPicker. The
need→use→NOT priority table and the full catalog (each with the one-liner that
gives you Liquid Glass for free) are in
native-components.md.
Design quality — the anti-slop rules
These are what reviewers catch. They are the difference between "ships with iOS" and
"obviously AI-generated." Apply them to every view: spacing only on the
4/8/12/16/20/24/32/40/48 grid; ≤ 5 font sizes with hierarchy through weight
(Dynamic Type styles, never .system(size:)); semantic system colors, never
Color.white.opacity(…) stacks; cards on .secondarySystemBackground at 10pt
radius; matching stroke widths; ≤ 2 tracking values; shorter copy; no
minimumScaleFactor hacks. The at-a-glance rule list (§12) and the full
bad/good code pairs are in design-quality.md.
Navigation — state-driven, not imperative
NavigationStack + value-based destinations replaces the push/present/pop model
with a NavigationPath-as-state model: lazy NavigationLink(value:) +
.navigationDestination(for:) (never eager destination:), .sheet(item:)
over .sheet(isPresented:), one NavigationStack per tab, and a @MainActor @Observable Router when flows outgrow view-local @State. The core-pattern
code, routing strategies, deep links, UIKit interop, and the 10 most common
navigation mistakes are in navigation.md.
Animations — transforms and opacity, not layout
Good iOS animation is GPU-cheap (transforms + opacity). Bad animation forces
layout recalculation inside scroll views. Core rules: .animation(_:value:)
declarative / withAnimation {} imperative; animate when the value lands,
never wrap the await; matchedGeometryEffect before .frame(); inside
lists animate scaleEffect/opacity, never frame/padding; completion
handlers over DispatchQueue delays. The at-a-glance list (§6), toolbox table,
Liquid Glass animation APIs, and the jank anti-patterns are in
animations.md.
iOS 26 Liquid Glass — one rule that matters most
Liquid Glass is a navigation-layer material, not a content material (Apple says this
verbatim). Recompiling against the iOS 26 SDK gives you glass on nav bars, tab bars,
toolbars, sheets, popovers, menus, alerts, search fields, toggles, sliders, and pickers
for free. Custom .glassEffect() is the exception, reserved for floating
controls that hover above content (the canonical FAB example, container
grouping, tinting rules, Regular-vs-Clear, tab-bar/toolbar APIs, and the
automatic accessibility behavior — never override it — are in
liquid-glass.md). Group adjacent glass in ONE
GlassEffectContainer; tint only the primary CTA; never mix Regular and Clear.
Accessibility — non-negotiable minimums
Labels + hints on icon-only buttons, .accessibilityHidden(true) on decorative
images, combined elements, header traits; tap targets ≥ 44×44 pt; color never
carries meaning alone; guard motion via accessibilityReduceMotion; Dynamic
Type text styles, never fixed sizes. Code block in
ui-audit.md ("Accessibility minimums").
UX patterns that apply to every screen
Six recurring patterns — full detail in
native-components.md ("UX patterns" section):
inline button loading (spinner inside the tapped button, no layout shift,
track which button via @State var loadingButton: ButtonID?); consistent
button sizing within a group; native Form for data entry; PhotosPicker for
user images; sheets with detents + drag indicator + NavigationStack toolbar;
onboarding as a conditional root-view flow, not a modal.
Self-audit — run before declaring a view done
Quick pass (full 13-domain audit framework with severity ratings is in
references/ui-audit.md):
Hard stops — never ship these
The full 19-row anti-pattern/why/fix table is in
ui-audit.md ("Hard stops"). Headlines: no
.glassEffect() on content or glass-on-glass; no ObservableObject in new
code; List over LazyVStack for >50 rows; withAnimation after the await,
never around it; no NavigationView or eager NavigationLink(destination:);
no .system(size:) or opacity-zoo colors; .task {} over .onAppear { Task };
@MainActor over DispatchQueue.main.async; 10pt card radius; tint only the
primary CTA.
Beyond the basics — when to reach for which reference
Capability areas that have their own deep reference (all iOS 26 / current-SDK):
- Embed web content → references/webkit.md — SwiftUI
WebView + WebPage
(not a WKWebView wrapper); the callJavaScript(arguments:)-not-interpolation
rule is load-bearing. OAuth still uses ASWebAuthenticationSession (ios-security).
- Rich text editing → references/rich-text-editor.md —
TextEditor bound to
AttributedString; the fontResolutionContext gotcha for toolbar state. (The
text-editing skill defers this here.)
- Widgets / Live Activities / Dynamic Island / Control Center →
references/widgetkit-live-activities.md (functional); widget visual design is
in
design-quality.md §8; Liquid-Glass widget rendering in the liquid-glass skill.
- 3D charts → references/charts-3d.md (
Chart3D/SurfacePlot; the
read-only-.chart3DPose mistake).
- Drag and drop → references/drag-and-drop.md (
.draggable/.dropDestination,
DropDelegate live-reorder, iOS 26 multi-item drag).
- Canvas / Shape stroke geometry → references/canvas-stroke-geometry.md
(selection-border insets, hairlines, marching-ants flicker).
- watchOS / visionOS / iPad input → references/platform-inputs.md (Digital
Crown, spatial hover-privacy, double-tap primary action).
- Pre-release whole-app audit → references/workflow-audit.md — the outside-in
flow audit that finds unreachable views and unwired data (run per-app; run
ui-audit.md per-view).
Reference files
| File | Covers |
|---|
| references/liquid-glass.md | iOS 26 Liquid Glass: free-on-recompile list, custom .glassEffect() rules, GlassEffectContainer, tab-bar/toolbar APIs, tinting, Regular/Clear/.identity variants, accessibility, background extension |
| references/native-components.md | Native component catalog; iOS 17/26 ScrollView modifiers; iOS 26 Slider ticks; hover/pointer; settings-row alternative; anchored popovers; iOS 26 gotchas; search/toast/Siri components; per-domain SF Symbol seed |
| references/design-quality.md | Anti-slop rules with bad/good code: spacing, typography (SF-Pro axes, smallCaps, reading width), color, cards (accent bar, pills, gradient-divider exception), strokes, editors, nested-safeAreaInset trap, AI-slop visual tropes, the one signature detail |
| references/navigation.md | NavigationStack/Path, routing, sheets/detents, TabView + Tab(role) gotcha, Router, @Entry/callAsFunction env actions, toolbar customization, UIKit interop (make-once/update-many), gesture bridging, in-app Safari, testing, mistakes |
| references/animations.md | Animation toolbox, SF Symbol effects catalog, .scrollTransition, transaction keys, @Animatable/KeyframeTimeline, MeshGradient/TextRenderer, Liquid Glass animation APIs, jank anti-patterns |
| references/state-and-architecture.md | @MainActor @Observable view models, @ObservationIgnored+debounce, optimistic updates/rollback/high-stakes, progressive rendering, @Observable migration + traps, @Previewable, environment scoping, Swift 6 concurrency |
| references/ui-audit.md | 5-dimension triage score + 13-domain audit (space, hierarchy, density, interaction, a11y, HIG, dark mode + same-as-background bug, performance + geometry-loop, design-system, color, iPad sheets, hit region) |
| references/project-setup.md | XcodeGen project.yml (app target, SPM packages:, unit-test target wiring, free-team error + mock-auth) + minimal Assets.xcassets |
| references/webkit.md | iOS 26 SwiftUI WebView + WebPage: load/navigation events, injection-safe callJavaScript(arguments:), WKContentWorld isolation, custom URL schemes, snapshot/PDF/archive, private browsing |
| references/widgetkit-live-activities.md | Functional widget half: timeline + reload budget, App Groups, interactive widgets, Live Activities (4 KB, push, lifecycle), Dynamic Island, Control Center controls, Lock Screen/StandBy, Activity Rings |
| references/rich-text-editor.md | iOS 26 TextEditor(text: $AttributedString), selection-aware toolbar + fontResolutionContext, formatting constraints, inline images, custom attribute keys + cross-boundary round-trip, Markdown traps, text-color traps |
| references/charts-3d.md | Chart3D + SurfacePlot, the chart3DPose($binding) read-only mistake, projection, auto-rotation |
| references/drag-and-drop.md | .draggable/.dropDestination/Transferable multi-fidelity, move/copy + spring-loading, DropDelegate live-reorder, iOS 26 selection-driven multi-item drag |
| references/canvas-stroke-geometry.md | Stroke centering, inset/outset for selection borders, 1/displayScale hairlines, pixel alignment, marching-ants flicker, pixel-art AA, optical centering |
| references/workflow-audit.md | Outside-in cross-app flow audit (complements per-view ui-audit): enumerate-then-verify, 5-layer pipeline, entry-point grep recipes + zero-match discipline, 32-category flow-bug taxonomy |
| references/platform-inputs.md | Digital Crown, visionOS spatial input (hover-privacy contract, comfort constants), watchOS haptics + double-tap primary action, combined Magnify+Rotate gestures |
| references/design-direction.md | Pre-build design-direction advisor: 5-school taxonomy with reference-app anchors; offer 2-3 directions, never one final |