
Swiftui Patterns
- 23 installs
- 4.9k repo stars
- Updated July 14, 2026
- openai/plugins
swiftui-patterns implements macOS SwiftUI desktop UI with system-native patterns.
About
The swiftui-patterns skill builds macOS SwiftUI scenes and components using desktop-native patterns for windows, commands, toolbars, settings, split views, and inspectors. It prefers system affordances such as commands, toolbars, sidebars, contextual menus, and searchable over custom chrome. Workflow chooses ownership location first, then wrappers, avoiding reflex view models for simple desktop state. Guidance covers NavigationSplitView column visibility, inspector placement, settings scenes, menu command organization, and toolbar item grouping with shared backgrounds. It pairs with view-refactor and window-management skills for large view splits and chrome tuning. Validation checks keyboard navigation, selection persistence, and scene restoration behavior. Use when shaping new macOS UI surfaces or aligning existing views with current desktop HIG patterns. Identify the feature or scene and the primary interaction model: document, editor, sidebar-detail, utility window, settings, or menu bar extra.
- Applies macOS desktop patterns for scenes and toolbars.
- Prefers system commands, sidebars, and searchable affordances.
- Chooses state ownership before adding view model layers.
- Covers split views, inspectors, and settings scenes.
- Validates keyboard navigation and restoration behavior.
Swiftui Patterns by the numbers
- 23 all-time installs (skills.sh)
- +1 installs in the week ending Jul 26, 2026 (Skillselion tracking)
- Ranked #694 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
swiftui-patterns capabilities & compatibility
- Capabilities
- desktop affordance preference rules · scene and toolbar pattern guidance · ownership location workflow
- Use cases
- frontend · ui design
- Platforms
- macOS
- IDEs
- vscode
What swiftui-patterns says it does
Choose the ownership location first, then the wrapper
npx skills add https://github.com/openai/plugins --skill swiftui-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 23 |
|---|---|
| repo stars | ★ 4.9k |
| Last updated | July 14, 2026 |
| Repository | openai/plugins ↗ |
How do I structure a macOS SwiftUI split view with inspectors?
Build macOS SwiftUI scenes with desktop patterns for windows, commands, and inspectors.
Who is it for?
macOS SwiftUI developers building windows, toolbars, or settings.
Skip if: Skip for iOS-only SwiftUI without desktop scene APIs.
When should I use this skill?
User shapes windows, commands, toolbars, settings, or split views.
What you get
SwiftUI scenes using desktop toolbars, commands, and sidebars.
Files
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:
1. Choose the scene model. 2. Choose state ownership: app-wide, scene-scoped, window-scoped, or view-local. 3. Sketch file and module boundaries. 4. Create the folder structure before filling in the UI. 5. Keep script/build_and_run.sh and .codex/environments/environment.toml separate 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
1. Define the scene type and ownership model before writing child views. 2. Decide which actions live in content, toolbars, commands, inspectors, or settings. 3. Sketch the selection model and layout: sidebar-detail, editor-inspector, document window, or utility window. 4. Create the file/folder structure for app entrypoint, root layout, feature views, models, stores, services, and support helpers. 5. Build with small, focused subviews and explicit inputs rather than giant computed fragments. 6. Add keyboard shortcuts and menu or toolbar exposure for actions that matter on desktop. 7. 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
interface:
display_name: "SwiftUI Patterns"
short_description: "Build native macOS SwiftUI scenes, menus, settings, and windows"
default_prompt: "Use $swiftui-patterns to choose the right macOS SwiftUI scene structure, implement desktop-native layouts and commands, and avoid iOS-centric UI patterns."
Commands and Menus
Intent
Use this when mapping desktop actions into menu items, keyboard shortcuts, and focused scene behavior.
Core patterns
- Add
commandsat the scene level. - Use
CommandMenufor app-specific actions. - Use
CommandGroupto insert, replace, or remove menu sections. - Use
FocusedValueor scene state to make commands context-sensitive. - Pair important commands with keyboard shortcuts and visible toolbar or content affordances when appropriate.
Example
@main
struct SampleApp: App {
var body: some Scene {
WindowGroup {
EditorRootView()
}
.commands {
CommandMenu("Document") {
Button("New Note") { /* create */ }
.keyboardShortcut("n")
Button("Toggle Inspector") { /* toggle */ }
.keyboardShortcut("i", modifiers: [.command, .option])
}
}
}
}Pitfalls
- Do not register the same shortcut in multiple places.
- Do not make commands the only discoverable path for a critical action.
- If you need responder-chain validation, custom menu item state, or AppKit-specific command behavior, use
appkit-interop.
Components Index
Use this file to find scene and component guidance. Each entry lists when to use it.
Available references
- Windowing:
references/windowing.md— Use when choosing betweenWindowGroup,Window,DocumentGroup, or window-opening APIs. - Settings:
references/settings.md— Use for dedicated settings scenes, preference storage, and settings entry points. - Commands and menus:
references/commands-menus.md— Use for menu items, keyboard shortcuts, focused actions, and command routing. - Split views and inspectors:
references/split-inspectors.md— Use for sidebar-detail apps, inspectors, and selection-driven desktop layouts. - Menu bar extras:
references/menu-bar-extra.md— Use when the app belongs primarily in the menu bar.
Adding entries
- Add a new reference file when a macOS-specific pattern comes up repeatedly.
- Keep each reference short, actionable, and explicit about when SwiftUI is enough versus when AppKit interop is warranted.
Menu Bar Extra
Intent
Use this when the app primarily lives in the macOS menu bar instead of a traditional always-open window.
Core patterns
- Use
MenuBarExtrafor lightweight utilities, status indicators, and quick actions. - If the app also has a primary main window that should appear at launch, define that scene with
WindowGroup(..., id:)and useWindow(...)only for auxiliary/on-demand windows. - If the menu bar app should still show in the Dock and activate like a normal app, install an app delegate with
@NSApplicationDelegateAdaptor, callNSApp.setActivationPolicy(.regular)during launch, and thenNSApp.activate(ignoringOtherApps: true). - If the app is intentionally menu-bar-only, explicitly document that
.accessory/ no-Dock behavior is expected product behavior rather than a launch bug. - Keep the menu content concise and action-oriented.
- Keep each visible menu item label to 30 characters or fewer. If the backing content can be longer than that, derive a short display title and open the full text in a separate window or detail pane.
- If the app has deeper workflows, open a dedicated window from the menu bar extra rather than cramming everything into the menu.
Example
This snippet shows scene wiring only. In a real non-trivial app, keep the @main app and AppDelegate in App/<AppName>App.swift, and put the menu bar, root content, and supporting models/services in separate files named after their primary types.
import AppKit
private func shortMenuTitle(_ title: String) -> String {
if title.count <= 30 {
return title
}
return String(title.prefix(27)) + "..."
}
final class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.setActivationPolicy(.regular)
NSApp.activate(ignoringOtherApps: true)
}
}
@main
struct SampleApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
var body: some Scene {
WindowGroup("Sample", id: "main") {
ContentView()
}
MenuBarExtra("Sample", systemImage: "bolt.circle") {
Button(shortMenuTitle("Open Dashboard")) { /* open window */ }
Divider()
Button("Quit") {
NSApplication.shared.terminate(nil)
}
}
}
}Pitfalls
- Do not rely on a
Window(...)scene alone for the main launch window in a menu-bar-plus-window app when the product expects a regular window at startup. - Do not silently ship a no-Dock menu-bar-only app if the user expects a normal app process. Either install the app delegate and switch to
.regular, or clearly document that.accessorybehavior is intentional. - Do not turn the menu bar extra into a tiny, overloaded substitute for a full app window.
- Do not render raw unbounded titles or message bodies as menu items. Long labels quickly blow out the menu width and should be capped to 30 characters with a short display title.
- If the extra needs advanced status item customization or AppKit menu control, use
appkit-interop.
Settings
Intent
Use this when building a native macOS settings window with SwiftUI.
Core patterns
- Declare a dedicated
Settingsscene in the app. - Keep settings content in a separate root view.
- Use
@AppStoragefor user preferences that should persist. - Prefer tabs, sections, or a split settings layout over deep push navigation.
- Use
SettingsLinkorOpenSettingsActionfor in-app entry points.
Example
This snippet shows scene wiring only. In a real non-trivial app, keep the @main app in App/<AppName>App.swift and put settings content in a dedicated view file such as Views/SettingsView.swift.
@main
struct SampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
Settings {
SettingsView()
}
}
}
struct SettingsView: View {
@AppStorage("showSidebarIcons") private var showSidebarIcons = true
var body: some View {
TabView {
Form {
Toggle("Show Sidebar Icons", isOn: $showSidebarIcons)
}
.tabItem { Label("General", systemImage: "gearshape") }
}
.frame(width: 460, height: 260)
.scenePadding()
}
}Pitfalls
- Do not reuse an iOS full-screen settings screen unless the app really is a direct Catalyst-style port.
- Keep settings rows simple and accessible.
- If settings require custom panels, responders, or first-responder integration, use
appkit-interop.
Split Views and Inspectors
Intent
Use this when the app benefits from a stable sidebar-detail layout, optional supplementary content, or an inspector panel.
Core patterns
- Prefer explicit selection state over push-only navigation.
- Start with
NavigationSplitViewwhen the layout matches the system mental model. - Use a manual split only when you need unusual sizing or an always-visible custom column.
- Use
inspector(isPresented:)for lightweight detail controls that complement the main content.
Example: sidebar + detail
struct LibraryRootView: View {
@State private var selection: Item.ID?
@State private var showInspector = false
var body: some View {
NavigationSplitView {
SidebarList(selection: $selection)
} detail: {
DetailView(selection: selection)
.inspector(isPresented: $showInspector) {
InspectorView(selection: selection)
}
}
}
}Example: native sidebar row
Prefer a native source-list row shape:
List(selection: $selection) {
ForEach(items) { item in
HStack(spacing: 10) {
Image(systemName: item.systemImage)
.foregroundStyle(.secondary)
.frame(width: 16)
VStack(alignment: .leading, spacing: 2) {
Text(item.title)
.lineLimit(1)
if let detail = item.detail {
Text(detail)
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(1)
}
}
}
.tag(item.id)
}
}
.listStyle(.sidebar)Keep each row to one icon and one or two text lines. Put richer metadata in the detail or inspector content instead of every sidebar row.
Example: split-view backgrounds
Let the sidebar and split container keep system backgrounds while detail content owns custom surfaces:
NavigationSplitView {
List(selection: $selection) {
ForEach(items) { item in
Label(item.title, systemImage: item.systemImage)
.tag(item.id)
}
}
.listStyle(.sidebar)
} detail: {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
DetailSummaryCard(item: selectedItem)
DetailMetricsCard(item: selectedItem)
}
.padding()
}
}Avoid opaque sidebar and root split-pane fills by default:
NavigationSplitView {
List(items) { item in
SidebarCardRow(item: item)
}
.listStyle(.sidebar)
.background(Color(nsColor: .windowBackgroundColor))
} detail: {
DetailView(item: selectedItem)
.background(Color(.white))
}Pitfalls
- Avoid swapping the whole root layout with top-level conditionals when selection changes.
- Avoid hiding too much detail behind modal sheets when an inspector or secondary column would fit better.
- If the layout requires AppKit split view delegation or advanced window coordination, use
appkit-interop.
Windowing
Intent
Use this when choosing the top-level scene model for a native macOS app.
Choose the scene type deliberately
- Use
WindowGroup(..., id:)for the primary app window when it should appear at launch, especially in apps that also have aMenuBarExtra. - Use
WindowGroupfor any scene that can have multiple independent instances. - Use
Windowfor singleton utility windows or focused secondary surfaces. In menu-bar-heavy apps,Window(...)is better for auxiliary/on-demand windows and may not present the main window automatically at launch. - Use
Settingsfor preferences. Do not bury settings inside the main content flow. - Use
DocumentGroupwhen the app is fundamentally document-driven.
Example: main app plus utility window
This snippet shows scene wiring only. In a real non-trivial app, keep the @main app in App/<AppName>App.swift and put LibraryRootView, InspectorRootView, and SettingsView in dedicated Views/ files.
@main
struct SampleApp: App {
var body: some Scene {
WindowGroup("Library", id: "library") {
LibraryRootView()
}
Window("Inspector", id: "inspector") {
InspectorRootView()
}
Settings {
SettingsView()
}
}
}Opening windows
- Use
openWindow(id:)when a command, toolbar item, or button should open another scene. - Keep per-window state in the scene or
@SceneStorage, not in a single global pile.
Pitfalls
- Avoid modeling every feature as a pushed destination inside one window.
- Do not use only
Window(...)for the main launch window in a menu-bar-plus-window app unless you have verified the launch behavior and intentionally want an on-demand auxiliary window. - Avoid singleton state for window-specific selections or drafts.
- If you need lower-level titlebar, tabbing, or window lifecycle control, switch to
appkit-interop.
Related skills
FAQ
What does swiftui-patterns do?
swiftui-patterns implements macOS SwiftUI desktop UI with system-native patterns.
When should I use swiftui-patterns?
User shapes windows, commands, toolbars, settings, or split views.
Is this skill safe to install?
Review the Security Audits panel on this page before installing in production.