swiftui-ui-patterns
SKILL.md
SwiftUI UI Patterns
Lifecycle Position
Phase 3 (Implement — best practices, UI architecture) and Phase 5 (Review — checklist). Core guidelines apply during implementation; review checklist during code review.
When to Use
digraph when {
rankdir=TB;
node [fontsize=11];
Q [label="What are you building?" shape=diamond];
A1 [label="New SwiftUI view" shape=box];
A2 [label="Reviewing code" shape=box];
A3 [label="App shell / navigation" shape=box];
A4 [label="Specific UI component" shape=box];
B1 [label="Read Core Guidelines\nthen Quick Reference table" shape=box style=filled fillcolor=lightblue];
B2 [label="Use Review Checklist\nat bottom of this file" shape=box style=filled fillcolor=lightblue];
B3 [label="app-wiring.md → navigation.md\n→ sheets-and-routing.md" shape=box style=filled fillcolor=lightblue];
B4 [label="Find component in\nQuick Reference table" shape=box style=filled fillcolor=lightblue];
Q -> A1 [label="feature"];
Q -> A2 [label="review"];
Q -> A3 [label="scaffold"];
Q -> A4 [label="component"];
A1 -> B1;
A2 -> B2;
A3 -> B3;
A4 -> B4;
}
Quick Reference — Which Reference to Load
| UI Need | Reference File |
|---|---|
| App root shell, TabRouter, dependency graph | references/app-wiring.md |
| Tab architecture, AppTab enum, TabSection | references/tabview.md |
| NavigationStack routing, RouterPath | references/navigation.md |
| Sheet presentation, SheetDestination enum | references/sheets-and-routing.md |
| Lists, ForEach identity, scroll-to-top | references/lists.md |
| ScrollView, chat anchor, horizontal chips | references/scroll-patterns.md |
| LazyVGrid, adaptive/fixed columns | references/grids.md |
| Forms, themed modals, auto-focus | references/forms-and-input.md |
| .searchable, async debounced search | references/searchable.md |
| Toast overlay, auto-dismiss banners | references/overlay-toast.md |
| Bottom composer bar, .safeAreaInset | references/input-toolbar.md |
| iOS 26+ safeAreaBar, top bar | references/top-bar.md |
| @Observable Theme, semantic colors | references/theming.md |
| AsyncImage, QuickLook media viewer | references/image-and-media.md |
| Haptic feedback, HapticManager | references/haptics.md |
| withAnimation, .animation modifier, CustomAnimation | references/state-based-animation.md |
| Keyframe animations, KeyframeTimeline | references/keyframe-animation.md |
| View transitions, matchedTransitionSource, zoom | references/view-transitions.md |
| Transaction control, TransactionKey | references/transactions.md |
| matchedGeometryEffect, geometryGroup | references/geometry-sync.md |
| Animatable protocol, VectorArithmetic | references/animatable-data.md |
| TimelineView, scheduled updates | references/timeline-view.md |
| PhaseAnimator, multi-step sequences | references/phase-animation.md |
| Spring parameters, UnitCurve timing | references/timing-and-curves.md |
| Deep links, URL dispatch, OpenURLAction | references/deeplinks.md |
| ToolbarTitleMenu, principal title | references/title-menus.md |
| NavigationSplitView vs manual split | references/split-views.md |
| .redacted placeholders, ContentUnavailableView | references/loading-placeholders.md |
| Closure-based client struct, store DI | references/lightweight-clients.md |
| Modern API replacements, deprecations | references/modern-apis.md |
| Property wrappers, @Observable, data flow | references/state-management.md |
| View extraction, composition rules | references/view-structure.md |
| Performance optimization, body purity | references/performance-patterns.md |
| Layout patterns, context-agnostic views | references/layout-best-practices.md |
| Text formatting, localizedStandardContains | references/text-formatting.md |
Templates
Deep linking and App Shortcuts in templates/ — copy and adapt:
DeepLink.swift— URL-to-enum parser for typed deep link handlingDeepLinkRouter.swift—@MainActor @Observablenavigation router with pending link support,NavigationPathintegration,EnvironmentKeyinjectionAppShortcuts.swift— Siri App Intents / App Shortcuts for voice and Spotlight integrationapple-app-site-association— Universal Links JSON template for your domain
Core Guidelines
State Management
- Always prefer
@ObservableoverObservableObjectfor new code - Mark
@Observableclasses with@MainActorunless using default actor isolation - Always mark
@Stateand@StateObjectasprivate(makes dependencies clear) - Never declare passed values as
@Stateor@StateObject @Statefor internal view state, or owned@Observableclass@Bindingonly when child needs to modify parent state@Bindablefor injected@Observableobjects needing bindings- Use
letfor read-only values;var+.onChange()for reactive reads - Nested
ObservableObjectdoesn't work — pass nested objects directly;@Observablehandles nesting
Modern APIs
foregroundStyle()notforegroundColor()clipShape(.rect(cornerRadius:))notcornerRadius()TabAPI nottabItem()ButtonnotonTapGesture()(unless need location/count)NavigationStacknotNavigationViewnavigationDestination(for:)for type-safe navigation- Two-parameter
onChange(of:) { old, new in }or no-parameter variant .sheet(item:)not.sheet(isPresented:)for model-based sheets- Sheets own their actions and call
dismiss()internally containerRelativeFrame()orvisualEffect()overGeometryReader.scrollIndicators(.hidden)notshowsIndicators: false- Avoid
UIScreen.main.boundsfor sizing
Swift Best Practices
- Use modern Text formatting (
.formatparameters, notString(format:)) - Use
localizedStandardContains()for user-input filtering - Use
.taskmodifier for automatic cancellation of async work - Use
.task(id:)for value-dependent async tasks
View Composition
- Prefer modifiers over conditional views for state changes (maintains view identity)
- Extract complex views into separate subviews early
- Keep view
bodysimple and pure (no side effects or complex logic) - Use
@ViewBuilder let content: Contentover closure-based content properties - Separate business logic into testable models
- Action handlers should reference methods, not contain inline logic
- Use relative layout over hard-coded constants
- Views should work in any context (context-agnostic)
Performance
- Pass only needed values to views (not large "config" or "context" objects)
LazyVStack/LazyHStackfor large lists- Stable identity for
ForEach(never.indicesfor dynamic content) - Constant number of views per
ForEachelement - No inline filtering in
ForEach(prefilter and cache) - No
AnyViewin list rows - Check for value changes before assigning state in hot paths
- No object creation in
body
Liquid Glass (iOS 26+)
Only adopt when explicitly requested.
- Use
glassEffect,GlassEffectContainer, glass button styles - Apply
.glassEffect()after layout and visual modifiers - Gate with
#available(iOS 26, *)and provide fallbacks - Use
glassEffectIDwith@Namespacefor morphing transitions - See
apple-liquid-glass-designskill for full glass API reference
Review Checklist
State Management (see references/state-management.md)
- Using
@Observableinstead ofObservableObjectfor new code -
@Observableclasses marked with@MainActor(if needed) - Using
@Statewith@Observableclasses (not@StateObject) -
@Stateand@StateObjectproperties areprivate - Passed values NOT declared as
@Stateor@StateObject -
@Bindingonly where child modifies parent state -
@Bindablefor injected@Observableneeding bindings - Nested
ObservableObjectavoided (or passed directly to child views)
Modern APIs (see references/modern-apis.md)
- Using
foregroundStyle()instead offoregroundColor() - Using
clipShape(.rect(cornerRadius:))instead ofcornerRadius() - Using
TabAPI instead oftabItem() - Using
Buttoninstead ofonTapGesture()(unless need location/count) - Using
NavigationStackinstead ofNavigationView - Avoiding
UIScreen.main.bounds - Using alternatives to
GeometryReaderwhen possible - Button images include text labels for accessibility
Sheets & Navigation (see references/sheets-and-routing.md, references/navigation.md)
- Using
.sheet(item:)for model-based sheets - Sheets own their actions and dismiss internally
- Using
navigationDestination(for:)for type-safe navigation
ScrollView (see references/scroll-patterns.md)
- Using
ScrollViewReaderwith stable IDs for programmatic scrolling - Using
.scrollIndicators(.hidden)instead of initializer parameter
Text & Formatting (see references/text-formatting.md)
- Using modern Text formatting (not
String(format:)) - Using
localizedStandardContains()for search filtering
View Structure (see references/view-structure.md)
- Using modifiers instead of conditionals for state changes
- Complex views extracted to separate subviews
- Views kept small for performance
- Container views use
@ViewBuilder let content: Content
Performance (see references/performance-patterns.md)
- View
bodykept simple and pure (no side effects) - Passing only needed values (not large config objects)
- Eliminating unnecessary dependencies
- State updates check for value changes before assigning
- Hot paths minimize state updates
- No object creation in
body - Heavy computation moved out of
body
List Patterns (see references/lists.md)
- ForEach uses stable identity (not
.indices) - Constant number of views per ForEach element
- No inline filtering in ForEach
- No
AnyViewin list rows
Layout (see references/layout-best-practices.md)
- Avoiding layout thrash (deep hierarchies, excessive GeometryReader)
- Gating frequent geometry updates by thresholds
- Business logic separated into testable models
- Action handlers reference methods (not inline logic)
- Using relative layout (not hard-coded constants)
- Views work in any context (context-agnostic)
Animations (see references/state-based-animation.md, references/view-transitions.md, references/keyframe-animation.md)
- Using
.animation(_:value:)with value parameter - Using
withAnimationfor event-driven animations - Transitions paired with animations outside conditional structure
- Custom
Animatablehas explicitanimatableDataimplementation - Preferring transforms over layout changes for animation performance
- Phase animations for multi-step sequences (iOS 17+)
- Keyframe animations for precise timing (iOS 17+)
- Completion handlers use
.transaction(value:)for reexecution
Liquid Glass (iOS 26+)
-
#available(iOS 26, *)with fallback for Liquid Glass - Multiple glass views wrapped in
GlassEffectContainer -
.glassEffect()applied after layout/appearance modifiers -
.interactive()only on user-interactable elements - Shapes and tints consistent across related elements
Cross-References
apple-liquid-glass-design— Full Liquid Glass API reference, glass button styles, morph transitionsswift-concurrency— Actors,@MainActor,Sendable, SwiftUI concurrency patternsswift-app-lifecycle— Scene structure, app entry points (pairs withapp-wiring.md,deeplinks.md)swift-networking— Async network patterns (pairs withreferences/lightweight-clients.md)swiftui-26-api— Non-glass iOS 26 APIs (WebView, TextEditor rich text, @Animatable)swiftui-input-api— Text fields, pickers, controls, focus management (pairs withreferences/searchable.md,references/forms-and-input.md)swiftui-material-api— Material backgrounds, shapes, stroke patterns
Weekly Installs
2
Repository
kmshdev/claude-…-toolkitFirst Seen
2 days ago
Security Audits
Installed on
mcpjam2
claude-code2
replit2
junie2
windsurf2
zencoder2