<< All versions

Skill v1.0.0

currentAutomated scan100/100
dbmrq/agent-skills/native-swiftui
──Details
PublishedOctober 3, 2026 at 03:48 PM
Content Hashsha256:afe10a864f3733ac...
Git SHA
──Files
Files (1 file, 10.4 KB)
SKILL.md10.4 KBactive
SKILL.md · 213 lines · 10.4 KB

version: "1.0.0" name: native-swiftui description: >- Build iOS apps that look and behave like native Apple software by preferring highest-level SwiftUI components, system styles, semantic colors, and standard navigation structures. Use when creating or reviewing SwiftUI UI, choosing between custom vs built-in controls, styling buttons and cards, or when the user wants a native iOS look, HIG-aligned layouts, or out-of-the-box SwiftUI APIs instead of custom implementations.


Native SwiftUI

Produce iOS interfaces that feel native by defaulting to Apple's ready-made SwiftUI components and styles before writing custom views. Custom UI is the exception, not the starting point.

Skill split:

  • `native-swiftui` (this skill) — what Apple components and system styles to use
  • `swiftui-view-composition` — how to structure and refactor large views into reusable pieces
  • `swiftui-project-structure` — repo folders, packages, targets, MV vs Store layers
  • `swiftui-expert-skill` — state, performance, concurrency (avdlee/swiftui-agent-skill; install via ./scripts/install-all.sh)

Agent workflow

  1. Check deployment target — use the newest APIs the minimum iOS version supports.
  2. Pick structure first — NavigationStack / NavigationSplitView + .inspector before inventing custom chrome.
  3. Pick components second — scan built-in-components.md for a system control that fits.
  4. Style at the root — .tint, .buttonStyle, .groupBoxStyle on WindowGroup or screen containers (see styling.md).
  5. Verify native feel — system colors, SF Symbols, Dynamic Type, Dark Mode, accessibility labels.
  6. Reject custom reimplementations — if Apple ships it, use it.
  7. Structure large screens — if body is hard to scan, apply swiftui-view-composition (extract View structs before custom modifiers).

Golden rules

PreferAvoid
GroupBox, Form, DisclosureGroup for grouped contentCustom RoundedRectangle + shadow "cards"
.buttonStyle(.borderedProminent) / .bordered + ButtonRoleHand-rolled button backgrounds and borders
Semantic colors (.primary, .secondary, .tint, .teal, .mint)Hard-coded hex/RGB/Color(red:green:blue:)
Label, LabeledContent for icon+text rowsManual HStack of Image + Text
ContentUnavailableView for empty statesCustom empty-state illustrations
NavigationStack, NavigationSplitView, .inspectorCustom nav bars, sidebars, detail panes
ShareLink, ColorPicker, PasteButton, RenameButtonUIKit bridges or bespoke controls
SF SymbolsCustom icon assets (unless branding requires)
@Observable + @StateObservableObject / @Published in new code
.task / .task(id:)onAppear { Task { } } without cancellation

App structure and navigation

iPhone

  • `NavigationStack` for drill-down flows; navigationDestination(for:) for type-safe pushes.
  • `TabView` with the Tab API (not deprecated tabItem).
  • `.sheet(item:)` for model-driven modals; sheet content owns its actions and calls dismiss().

iPad / macOS

  • `NavigationSplitView` for sidebar + detail; add `.inspector` for supplementary panels (settings, metadata, tools) instead of a third custom column.
  • Use size classes and horizontalSizeClass to adapt compact vs regular layouts.
  • `ViewThatFits` when a row may need to collapse to a column on narrow widths.
swift
NavigationSplitView {
List(selection: $selection) { /* sidebar */ }
} detail: {
DetailView(item: selection)
.inspector(isPresented: $showInspector) {
InspectorPanel()
}
}

Settings and forms

  • `Form` for settings screens and data entry.
  • Nest `GroupBox` inside Form or other GroupBox views for logical sections — the system alternates backgrounds per nesting level automatically.
  • `DisclosureGroup` for expandable settings sections.
  • `LabeledContent` for label/value rows (settings detail, read-only info).

Visual grouping

Use Apple's grouping primitives — they carry correct spacing, materials, and accessibility:

swift
GroupBox("Account") {
LabeledContent("Username") { Text(user.name) }
LabeledContent("Plan") { Text(user.plan) }
}
DisclosureGroup("Advanced") {
Toggle("Analytics", isOn: $analytics)
}
  • `ControlGroup` for related actions (media transport, toolbar-like button clusters).
  • `OutlineGroup` for hierarchical tree data in lists.
  • `Label` everywhere icons accompany text (lists, buttons, menus).

Buttons and controls

Apply styles once at a container or app root; do not wrap Button in custom MyButton types.

swift
// App root
ContentView()
.tint(.teal)
.buttonStyle(.borderedProminent)
// Destructive actions
Button(role: .destructive) { delete() } label: {
Label("Delete", systemImage: "trash")
}
// Secondary actions
Button("Cancel", role: .cancel) { dismiss() }
.buttonStyle(.bordered)
  • Roles: .destructive for delete/remove, .cancel for dismissive actions.
  • Prominence: .borderedProminent for primary CTA; .bordered or .borderless for secondary.
  • `Stepper`, `Gauge`, `Picker`, `Toggle`, `Slider` — use as-is; apply .pickerStyle(.segmented) etc. at the group level.
  • `ShareLink` for sharing URLs, text, or images — not UIActivityViewController wrappers.

Colors, materials, and typography

  • Use semantic styles: .foregroundStyle(.primary), .foregroundStyle(.secondary), .tint(.mint).
  • Use system palette names (.teal, .mint, .indigo, .orange) for accents — they adapt to light/dark and accessibility settings.
  • Use `foregroundStyle()` instead of deprecated foregroundColor().
  • Support Dynamic Type — avoid fixed font sizes for body text; use .font(.body), .headline, etc.
  • `MeshGradient` for decorative backgrounds when a multi-point gradient is needed (iOS 18+); prefer materials (.regularMaterial) for functional surfaces.

State, concurrency, and data

From project-wide Swift guidelines:

  • `@Observable` for shared model state; @State to own it in a view; @Bindable for bindings to injected observables.
  • `@MainActor` on types that drive UI; keep non-UI work off the main actor.
  • `async/await` with strict concurrency; actors for shared mutable state.
  • Storage: UserDefaults (simple prefs), Keychain (secrets), SwiftData (models), CloudKit (sync) — match the problem, don't invent file formats.
  • `Logger` instead of print(); no forced unwraps (!).

Animation and live content

  • `TimelineView` for clocks, countdowns, or periodic refresh (weather, timers) — not Timer + @State polling.
  • `PhaseAnimator` for repeating multi-phase animations (pulse, shimmer) — not manual animation loops.
  • `ScenePhase` via @Environment(\.scenePhase) for foreground/background lifecycle in views.

Accessibility and platform

  • VoiceOver labels and hints on all interactive elements from the start.
  • Dark Mode must work without separate color definitions when using semantic colors.
  • Follow Human Interface Guidelines and App Store Review expectations.
  • Add Previews to every view; use #Preview with varied size classes when layout adapts.

Custom UI — only when necessary

Reach for custom views only after confirming no built-in fits:

NeedBuilt-in first
Card / panelGroupBox
Empty listContentUnavailableView
Expandable sectionDisclosureGroup
Icon + title rowLabel / LabeledContent
Tree listOutlineGroup
Adaptive H/V layoutViewThatFits
Custom arrangementLayout protocol (not nested stacks with magic numbers)
Drawing / chartsCanvas (not UIViewRepresentable unless required)
MapSwiftUI Map + MapKit
Multi-date selectionMultiDatePicker
Clipboard pastePasteButton
Inline renameRenameButton
Per-corner radiusUnevenRoundedRectangle

Full catalog with usage notes: built-in-components.md.

Styling system components

  • Set `.buttonStyle`, `.tint`, `.toggleStyle`, `.pickerStyle` on WindowGroup or screen root — styles propagate like environment values.
  • Extend built-in styles with Button(configuration) / style-configuration initializers instead of per-button modifier stacks.
  • Nested `GroupBox` does not inherit a custom `.groupBoxStyle` from its parent — reapply inside the style's makeBody or on each nested box. Prefer the default automatic style unless branding requires custom.
  • Sheets may not inherit styles from the presenter — reapply styles on sheet content when needed.

Details: styling.md.

Code quality checklist

Before finishing UI work:

  • [ ] No custom card/button when GroupBox / .borderedProminent suffices
  • [ ] No hard-coded RGB/hex colors for standard UI
  • [ ] Navigation uses NavigationStack or NavigationSplitView (not legacy NavigationView)
  • [ ] Empty states use ContentUnavailableView
  • [ ] Icons are SF Symbols with appropriate rendering mode
  • [ ] Forms and settings use Form + LabeledContent / DisclosureGroup
  • [ ] Primary actions use .borderedProminent; destructive use role: .destructive
  • [ ] Styles applied at root, not duplicated on every control
  • [ ] Previews present; Dynamic Type and Dark Mode spot-checked
  • [ ] Accessibility labels set on custom-labeled controls

View structure

This skill does not cover refactoring large view bodies. When a screen grows beyond a short, scannable body:

  • Extract rows, sections, and states into dedicated View structs — not extension computed properties
  • Prefer List / GroupBox for extracted pieces instead of custom card modifiers
  • See `swiftui-view-composition` for the full extraction workflow, decision guide, and refactor patterns

Swift and project conventions

  • One type per file; feature/domain folders (not global Views/ / ViewModels/ splits). For repo layout, packages, and architecture layers, see `swiftui-project-structure`.
  • // MARK: - sections; protocol conformance in extensions.
  • Swift Testing over XCTest for new tests; meaningful business-logic coverage.
  • Remove stale code after refactors; match patterns already in the project.
  • Do not add documentation files unless the user asks.
All versions