Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Reviews, improves, and writes SwiftUI code following state management, view composition, performance, and iOS 26+ Liquid Glass best practices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/macos-scenes.md
1# macOS Scenes Reference23> SwiftUI scene types for macOS apps — `Settings`, `MenuBarExtra`, `WindowGroup`, `Window`, `UtilityWindow`, and `DocumentGroup`. Covers macOS-only scenes and cross-platform scenes with macOS-specific behavior.45## Table of Contents67- [Quick Lookup Table](#quick-lookup-table)8- [Settings (macOS-only)](#settings-macos-only)9- [MenuBarExtra (macOS-only)](#menubarextra-macos-only)10- [WindowGroup (macOS behavior)](#windowgroup-macos-behavior)11- [Window](#window)12- [UtilityWindow (macOS-only)](#utilitywindow-macos-only)13- [DocumentGroup](#documentgroup)14- [Platform Conditionals](#platform-conditionals)15- [Best Practices](#best-practices)1617---1819## Quick Lookup Table2021| API | Availability | macOS-Only? | macOS-Specific Behavior |22|-----|-------------|:-----------:|------------------------|23| `WindowGroup` | macOS 11.0+ | No | Multiple window instances, tabbed interface, automatic Window menu commands |24| `Window` | macOS 13.0+ | No | App quits when sole window closes; adds itself to Windows menu |25| `UtilityWindow` | macOS 15.0+ | Yes | Floating tool palette; receives `FocusedValues` from active main window |26| `Settings` | macOS 11.0+ | Yes | Presents preferences window (Cmd+,) |27| `MenuBarExtra` | macOS 13.0+ | Yes | Persistent icon/menu in the system menu bar |28| `DocumentGroup` | macOS 11.0+ | No | Document-based menu bar commands (File > New/Open/Save); multiple document windows |2930---3132## Settings (macOS-only)3334Presents the app's preferences window, accessible via **Cmd+,** or the app menu. SwiftUI automatically enables the Settings menu item and manages the window lifecycle.3536```swift37Settings {38TabView {39Tab("General", systemImage: "gear") { GeneralSettingsView() }40Tab("Advanced", systemImage: "star") { AdvancedSettingsView() }41}42.scenePadding()43.frame(maxWidth: 350, minHeight: 100)44}45```4647Use `TabView` with `Tab` items for multi-pane preferences. Each tab's content is typically a `Form` with `@AppStorage`-backed controls.4849### SettingsLink (macOS 14.0+)5051A button that opens the Settings scene. Use for in-app navigation to preferences.5253```swift54struct SidebarFooter: View {55var body: some View {56SettingsLink {57Label("Preferences", systemImage: "gear")58}59}60}61```6263### openSettings environment action (macOS 14.0+)6465Programmatically open (or bring to front) the Settings window.6667```swift68struct OpenSettingsButton: View {69@Environment(\.openSettings) private var openSettings7071var body: some View {72Button("Open Settings") {73openSettings()74}75}76}77```7879---8081## MenuBarExtra (macOS-only)8283Renders a persistent control in the system menu bar. Two styles available:84- **`.menu`** (default) — standard dropdown menu85- **`.window`** — popover panel with custom SwiftUI views8687### Menu-style (dropdown)8889```swift90MenuBarExtra("My Utility", systemImage: "hammer") {91Button("Action One") { /* ... */ }92Button("Action Two") { /* ... */ }93Divider()94Button("Quit") { NSApplication.shared.terminate(nil) }95}96```9798### Window-style (popover panel)99100```swift101MenuBarExtra("Status", systemImage: "chart.bar") {102DashboardView()103.frame(width: 240)104}105.menuBarExtraStyle(.window)106```107108**Variations:**109- **Toggleable** — pass `isInserted:` with an `@AppStorage` binding to let users show/hide the extra: `MenuBarExtra("Status", systemImage: "chart.bar", isInserted: $showMenuBarExtra)`110- **Menu-bar-only app** — use `MenuBarExtra` as the sole scene + set `LSUIElement = true` in Info.plist to hide the Dock icon. The app auto-terminates if the user removes the extra from the menu bar.111112---113114## WindowGroup (macOS behavior)115116On macOS, `WindowGroup` supports:117- **Multiple window instances** — users can open many windows from File > New Window118- **Tabbed interface** — users can merge windows into tabs119- **Automatic Window menu** — commands for window management appear automatically120121```swift122@main123struct Mail: App {124var body: some Scene {125// Basic multi-window support126WindowGroup {127MailViewer()128}129130// Data-presenting window opened programmatically131WindowGroup("Message", for: Message.ID.self) { $messageID in132MessageDetail(messageID: messageID)133}134}135}136137// Open a specific window programmatically138struct NewMessageButton: View {139var message: Message140@Environment(\.openWindow) private var openWindow141142var body: some View {143Button("Open Message") {144openWindow(value: message.id)145}146}147}148```149150> **Key difference from `Window`:** `WindowGroup` keeps the app running even after all windows are closed. `Window` (as sole scene) quits the app when closed.151152---153154## Window155156A single, unique window scene. The system ensures only one instance exists.157158```swift159@main160struct Mail: App {161var body: some Scene {162WindowGroup {163MailViewer()164}165166// Supplementary singleton window167Window("Connection Doctor", id: "connection-doctor") {168ConnectionDoctor()169}170}171}172173// Open programmatically — brings to front if already open174struct OpenDoctorButton: View {175@Environment(\.openWindow) private var openWindow176177var body: some View {178Button("Connection Doctor") {179openWindow(id: "connection-doctor")180}181}182}183```184185### Window as sole scene186187If `Window` is the only scene, the app quits when the window closes:188189```swift190@main191struct VideoCall: App {192var body: some Scene {193Window("VideoCall", id: "main") {194CameraView()195}196}197}198```199200> **Recommendation:** In most cases, prefer `WindowGroup` for the primary scene. Use `Window` for supplementary singleton windows.201202---203204## UtilityWindow (macOS-only)205206A specialized floating window for tool palettes and inspector panels. Available since macOS 15.0.207208**Key behaviors:**209- Receives `FocusedValues` from the focused main scene (like menu bar commands)210- Floats above main windows (default level: `.floating`)211- Hides when the app is no longer active212- Only becomes focused when explicitly needed (e.g., clicking the title bar)213- Dismissible with the Escape key214- Not minimizable by default215- Automatically adds a show/hide item to the View menu216217```swift218@main219struct PhotoBrowser: App {220var body: some Scene {221WindowGroup {222PhotoGallery()223}224225UtilityWindow("Photo Info", id: "photo-info") {226PhotoInfoViewer()227}228}229}230231struct PhotoInfoViewer: View {232// Automatically updates based on whichever main window is focused233@FocusedValue(PhotoSelection.self) private var selectedPhotos234235var body: some View {236if let photos = selectedPhotos {237Text("\(photos.count) photos selected")238} else {239Text("No selection")240.foregroundStyle(.secondary)241}242}243}244```245246> **Tip:** Remove the automatic View menu item with `.commandsRemoved()` and place a `WindowVisibilityToggle` elsewhere in your commands.247248---249250## DocumentGroup251252Document-based apps with automatic file management. On macOS, provides:253- **Document-based menu bar commands** (File > New, Open, Save, Revert)254- **Multiple document windows** simultaneously255- On iOS, shows a document browser instead256257```swift258DocumentGroup(newDocument: TextFile()) { config in259ContentView(document: config.$document)260}261```262263The document type must conform to `FileDocument` (value type) or `ReferenceFileDocument` (reference type). Key requirements:264265```swift266struct TextFile: FileDocument {267static var readableContentTypes: [UTType] { [.plainText] }268var text: String = ""269init() {}270init(configuration: ReadConfiguration) throws {271text = String(data: configuration.file.regularFileContents ?? Data(), encoding: .utf8) ?? ""272}273func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {274FileWrapper(regularFileWithContents: Data(text.utf8))275}276}277```278279For multiple document types, add additional `DocumentGroup` scenes — use `DocumentGroup(viewing:)` for read-only formats.280281---282283## Platform Conditionals284285Always wrap macOS-only scenes in `#if os(macOS)`:286287```swift288@main289struct MyApp: App {290var body: some Scene {291WindowGroup {292ContentView()293}294295#if os(macOS)296Settings {297SettingsView()298}299300MenuBarExtra("Status", systemImage: "bolt") {301StatusMenu()302}303#endif304}305}306```307308---309310## Best Practices311312- **Use `Settings`** for preferences — prefer this over a custom preferences window313- **Use `MenuBarExtra`** for menu bar items — prefer this over managing AppKit's `NSStatusItem` directly314- **Use `WindowGroup`** as the primary scene — reserve `Window` for supplementary singletons315- **Use `UtilityWindow`** for inspectors/palettes — it handles floating, focus, and visibility automatically316- **Use `DocumentGroup`** for document-based apps — it provides the full File menu and document lifecycle317- **Gate macOS-only scenes** with `#if os(macOS)` for multiplatform projects318- **Use `openWindow(id:)`** to open windows programmatically — it brings existing windows to front319