Skill v1.0.1
currentTrusted Publisher100/100Refresh hyperframes branding (#186)
version: "1.0.1" name: swiftui-patterns description: Build macOS SwiftUI scenes and components with desktop patterns. Use when shaping windows, commands, toolbars, settings, split views, or inspectors.
SwiftUI Patterns
Quick Start
Choose a track based on your goal:
Existing project
- Identify the feature or scene and the primary interaction model: document, editor, sidebar-detail, utility window, settings, or menu bar extra.
- Read the nearest existing scene or root view before inventing a new desktop structure.
- Choose the relevant reference from
references/components-index.md. - If SwiftUI cannot express the required platform behavior cleanly, use the
appkit-interopskill rather than forcing a shaky workaround.
New app scaffolding
- Choose the scene model first:
WindowGroup,Window,Settings,MenuBarExtra, orDocumentGroup. - If the app combines a normal main window and a
MenuBarExtra, useWindowGroup(..., id:)for the primary window when it should appear at launch. TreatWindow(...)as a better fit for auxiliary/on-demand singleton windows; in menu-bar-heavy apps, aWindow(...)scene may not present the main window automatically at launch. - Before creating the scaffold, check whether the workspace is already inside a git repo with
git rev-parse --is-inside-work-tree. If not, rungit initat the project root so Codex app git-backed features are available from the start. Do not initialize a nested repo inside an existing parent checkout. - For a new app scaffold, also create one project-local
script/build_and_run.shand.codex/environments/environment.tomlso the Codex app Run button works immediately. Use the exact bootstrap contract frombuild-run-debugand itsreferences/run-button-bootstrap.mdfile rather than inventing a second variant here. - Decide which state is app-wide, scene-scoped, or window-scoped before writing views.
- Sketch file and module boundaries before writing the full UI. For any non-trivial app, create the folder structure first and split files by responsibility from the start.
- Use a single Swift file only for tiny throwaway examples or snippets: roughly under 50 lines, one screen, no persistence, no networking/process client, and no reusable models. Anything beyond that should be multi-file immediately.
- Use system-adaptive colors and materials by default (
Color.primary,Color.secondary, semantic foreground styles,.regularMaterial, etc.) so the app follows Light/Dark mode automatically. Do not hardcode white or light backgrounds unless the user explicitly asks for a fixed theme, and do not reach for opaquewindowBackgroundColorfills for root panes by default. - Pick the references for the first feature surface you need: windowing, commands, split layouts, or settings.
New App File Structure
For any non-trivial macOS app, start with this shape instead of putting the app, all views, models, stores, services, and helpers in one Swift file:
App/<AppName>App.swift: the@mainapp type andAppDelegateonly.Views/ContentView.swift: root layout and high-level composition only.Views/SidebarView.swift,Views/DetailView.swift,Views/ComposerView.swift, etc.: feature views named after their primary type.Models/*.swift: value models, identifiers, and selection enums.Stores/*.swift: persistence and state stores.Services/*.swift: app-server, network, process, or platform clients.Support/*.swift: small formatters, resolvers, extensions, and glue helpers.
Keep files small and named after the primary type they contain. If a file starts collecting unrelated views, models, stores, networking clients, and helper extensions, split it before adding more behavior.
Pre-Edit Checklist For New App Scaffolds
Before writing the full UI:
- Choose the scene model.
- Choose state ownership: app-wide, scene-scoped, window-scoped, or view-local.
- Sketch file and module boundaries.
- Create the folder structure before filling in the UI.
- Keep
script/build_and_run.shand.codex/environments/environment.tomlseparate from app source.
General Rules To Follow
- Design for pointer, keyboard, menus, and multiple windows.
- Keep scenes explicit. A separate settings window, utility window, or menu bar extra should be modeled as its own scene, not hidden inside one monolithic
ContentView. - Prefer system desktop affordances:
commands, toolbars, sidebars, inspectors, contextual menus, andsearchable. - For menu bar apps, keep
MenuBarExtraitem titles and action labels short and scannable. Cap visible menu item text at 30 characters; if source content is longer, truncate or summarize it before rendering and open the full content in a dedicated window or detail surface. - If a
MenuBarExtraapp should still behave like a regular Dock app with a visible main window/process, install anNSApplicationDelegatevia@NSApplicationDelegateAdaptor, callNSApp.setActivationPolicy(.regular)during launch, and activate the app withNSApp.activate(ignoringOtherApps: true). If the app is intentionally menu-bar-only, document that.accessory/ no-Dock behavior is a deliberate product choice. - Prefer system-adaptive colors, materials, and semantic foreground styles. Avoid fixed white/light backgrounds in scaffolding and examples unless the requested design explicitly calls for a custom non-adaptive theme.
- Do not paint
NavigationSplitViewsidebars or root window panes with opaque customColor(...)orColor(nsColor: .windowBackgroundColor)fills by default. Prefer native macOS sidebar/window materials and system-provided backgrounds unless the user explicitly asks for a custom opaque surface. In sidebar-detail-inspector layouts, let the sidebar keep the standard source-list/material appearance and reserve custom backgrounds for detail or inspector content cards where needed. - Use
@SceneStoragefor per-window ephemeral state and@AppStoragefor durable user preferences. - Keep selection state explicit and stable. macOS layouts often pivot around sidebar selection rather than push navigation.
- Prefer
NavigationSplitViewor a deliberate manual split layout over iOS-style stacked flows when the app benefits from always-visible structure. - For
List(...).listStyle(.sidebar)andNavigationSplitViewsidebars, prefer flat native rows with standard system selection/highlight behavior. Keep rows visually lightweight and Mail-like: at most one leading icon, one strong title line, and one optional secondary detail line in.secondary. Avoid stacked metadata rows, repeated inline utility icons, or dense multi-column status text in the sidebar. Reserve card-style and metadata-heavy surfaces for detail or inspector panes unless the user explicitly asks for a highly custom sidebar treatment. - Keep primary actions discoverable from both UI chrome and keyboard shortcuts when appropriate.
- Use SwiftUI-native scenes and views first. If you need low-level window, responder-chain, text system, or panel control, switch to
appkit-interop.
For concrete sidebar row and split-view background examples, read references/split-inspectors.md.
State Ownership Summary
Use the narrowest state tool that matches the ownership model:
| Scenario | Preferred pattern | |
|---|---|---|
| Local view or control state | @State | |
| Child mutates parent-owned value state | @Binding | |
| Root-owned reference model on macOS 14+ | @State with an @Observable type | |
Child reads or mutates an injected @Observable model | Pass it explicitly as a stored property | |
| Window-scoped ephemeral selection or expansion state | @SceneStorage when practical, otherwise scene-owned @State | |
| Shared user preference | @AppStorage | |
| Shared app service or configuration | @Environment(Type.self) | |
| Legacy reference model on older targets | @StateObject at the owner and @ObservedObject when injected |
Choose the ownership location first, then the wrapper. Do not turn simple desktop state into a view model by reflex.
Cross-Cutting References
references/components-index.md: entry point for scene and component guidance.references/windowing.md: choosing betweenWindowGroup,Window,DocumentGroup, and window-opening patterns.references/settings.md: dedicated settings scenes,SettingsLink, and preference layouts.references/commands-menus.md: command menus, keyboard shortcuts, focused values, and desktop action routing.references/split-inspectors.md: sidebars, split views, selection-driven layout, and inspectors.references/menu-bar-extra.md: menu bar extra structure and when it fits.
Anti-Patterns
- One huge
ContentViewpretending the whole app is a single screen. - A single Swift file containing the
@mainapp, all views, models, stores, networking/process clients, formatters, and extensions. This is acceptable only for tiny throwaway snippets under the new-app threshold above. - Touch-first interaction models ported directly from iOS without desktop affordances.
- Hiding core actions behind gestures with no menu, toolbar, or keyboard path.
- Building a menu-bar-plus-window app around only a
Window(...)scene and then expecting the main window to appear at launch. UseWindowGroup(..., id:)for the primary launch window and reserveWindow(...)for auxiliary/on-demand windows. - Rendering full unbounded document titles, prompts, or message text directly inside a menu bar extra. Menu item labels should stay at or below 30 characters, with longer content moved into a dedicated window or detail view.
- Treating settings as another navigation destination in the main content window.
- Hardcoding
.background(.white),Color.white, or a fixed light palette in a brand-new scaffold without an explicit design requirement. - Wrapping each sidebar item in large rounded custom cards inside a
.sidebarlist, which fights native source-list density, alignment, and selection behavior unless the user explicitly asked for a bespoke visual sidebar. - Building sidebar rows with multiple repeated icons, three or more text lines, or a dense strip of inline metadata counters/timestamps/models. Keep the sidebar row to one icon and one or two text lines, then move richer metadata into the detail pane.
- Painting
NavigationSplitViewsidebars or root window panes with opaque custom color fills by default, instead of letting the sidebar use native source-list/material appearance and reserving custom backgrounds for actual content cards. - Using push navigation for layouts that want stable sidebar selection and detail panes.
- Reaching for AppKit before the SwiftUI scene and command APIs have been used properly.
Workflow For A New macOS Scene Or View
- Define the scene type and ownership model before writing child views.
- Decide which actions live in content, toolbars, commands, inspectors, or settings.
- Sketch the selection model and layout: sidebar-detail, editor-inspector, document window, or utility window.
- Create the file/folder structure for app entrypoint, root layout, feature views, models, stores, services, and support helpers.
- Build with small, focused subviews and explicit inputs rather than giant computed fragments.
- Add keyboard shortcuts and menu or toolbar exposure for actions that matter on desktop.
- Validate the flow with a build and a quick usability pass: multiwindow assumptions, settings entry points, and selection stability.
Component References
Use references/components-index.md as the entry point. Each component reference should include:
- intent and best-fit scenarios
- minimal usage pattern with desktop conventions
- pitfalls and discoverability notes
- when to fall back to
appkit-interop