macos-development

macOS Development Patterns

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "macos-development" with this command: npx skills add bluewaves-creations/bluewaves-skills/bluewaves-creations-bluewaves-skills-macos-development

macOS Development Patterns

Comprehensive guide to macOS Tahoe development, window management, menu bars, document-based apps, and macOS-specific SwiftUI patterns.

Prerequisites

  • macOS Tahoe (macOS 26) or later

  • Xcode 26+

Window Management

Window Styles

import SwiftUI

@main struct MyMacApp: App { var body: some Scene { // Standard window WindowGroup { ContentView() } .windowStyle(.automatic) // Default

    // Hideable title bar (content extends to top)
    WindowGroup("Editor", id: "editor") {
        EditorView()
    }
    .windowStyle(.hiddenTitleBar)

    // Plain window (no chrome)
    WindowGroup("Floating", id: "floating") {
        FloatingView()
    }
    .windowStyle(.plain)
}

}

Window Size and Position

WindowGroup { ContentView() } // Default size .defaultSize(width: 800, height: 600)

// Size constraints .windowResizability(.contentSize) // Fit content .windowResizability(.contentMinSize) // Min = content, resizable larger .windowResizability(.automatic) // System decides

// Fixed size window .windowResizability(.contentSize) .frame(width: 400, height: 300)

// Position .defaultPosition(.center) .defaultPosition(.topLeading) .defaultPosition(UnitPoint(x: 0.75, y: 0.25))

Multiple Windows

@main struct MultiWindowApp: App { @Environment(.openWindow) private var openWindow

var body: some Scene {
    // Main window
    WindowGroup {
        MainView()
            .toolbar {
                Button("New Editor") {
                    openWindow(id: "editor")
                }
            }
    }
    .commands {
        CommandGroup(after: .newItem) {
            Button("New Editor Window") {
                openWindow(id: "editor")
            }
            .keyboardShortcut("e", modifiers: [.command, .shift])
        }
    }

    // Secondary window type
    WindowGroup("Editor", id: "editor") {
        EditorView()
    }
    .defaultSize(width: 600, height: 400)

    // Single instance window
    Window("Settings", id: "settings") {
        SettingsView()
    }
    .keyboardShortcut(",", modifiers: .command)
    .defaultSize(width: 500, height: 400)
}

}

Window with Data

// Define window value type struct DocumentInfo: Codable, Hashable { let id: UUID let title: String }

@main struct DocumentApp: App { var body: some Scene { WindowGroup(for: DocumentInfo.self) { $document in if let document { DocumentView(info: document) } } } }

// Open with specific data struct ContentView: View { @Environment(.openWindow) private var openWindow

var body: some View {
    Button("Open Document") {
        openWindow(value: DocumentInfo(id: UUID(), title: "New Doc"))
    }
}

}

NSWindow Integration

import AppKit import SwiftUI

struct WindowAccessor: NSViewRepresentable { let callback: (NSWindow?) -> Void

func makeNSView(context: Context) -> NSView {
    let view = NSView()
    DispatchQueue.main.async {
        callback(view.window)
    }
    return view
}

func updateNSView(_ nsView: NSView, context: Context) {}

}

// Usage struct ContentView: View { @State private var window: NSWindow?

var body: some View {
    Text("Hello")
        .background(WindowAccessor { window in
            self.window = window

            // Customize window
            window?.titlebarAppearsTransparent = true
            window?.titleVisibility = .hidden
            window?.styleMask.insert(.fullSizeContentView)
            window?.isMovableByWindowBackground = true
        })
}

}

Menu Bar

App Menu Customization

@main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } .commands { // Replace existing menu group CommandGroup(replacing: .newItem) { Button("New Document") { // Create new document } .keyboardShortcut("n")

            Button("New from Template...") {
                // Show template picker
            }
            .keyboardShortcut("n", modifiers: [.command, .shift])
        }

        // Add to existing menu
        CommandGroup(after: .sidebar) {
            Divider()
            Button("Toggle Inspector") {
                // Toggle inspector
            }
            .keyboardShortcut("i", modifiers: [.command, .option])
        }

        // Custom menu
        CommandMenu("Canvas") {
            Button("Zoom In") { }
                .keyboardShortcut("+")
            Button("Zoom Out") { }
                .keyboardShortcut("-")
            Divider()
            Button("Fit to Window") { }
                .keyboardShortcut("0")
        }
    }
}

}

Context Menus (Right-Click)

struct ItemView: View { let item: Item @State private var isRenaming = false

var body: some View {
    Text(item.title)
        .contextMenu {
            Button("Open") {
                // Open action
            }

            Button("Open in New Window") {
                // Open in new window
            }

            Divider()

            Button("Rename") {
                isRenaming = true
            }

            Button("Duplicate") {
                // Duplicate action
            }

            Divider()

            Button("Delete", role: .destructive) {
                // Delete action
            }
        }
}

}

Menu Bar Extra (Status Bar Items)

@main struct StatusBarApp: App { var body: some Scene { // Optional main window WindowGroup { ContentView() }

    // Status bar item
    MenuBarExtra("My App", systemImage: "star.fill") {
        Button("Show Dashboard") {
            // Open main window
        }
        .keyboardShortcut("d")

        Divider()

        Menu("Recent Items") {
            ForEach(recentItems) { item in
                Button(item.name) {
                    // Open item
                }
            }
        }

        Divider()

        Button("Preferences...") {
            // Open preferences
        }
        .keyboardShortcut(",")

        Button("Quit") {
            NSApplication.shared.terminate(nil)
        }
        .keyboardShortcut("q")
    }
}

@State private var recentItems: [RecentItem] = []

}

// Custom status bar view struct StatusBarApp2: App { var body: some Scene { MenuBarExtra { StatusBarPopover() } label: { HStack(spacing: 4) { Image(systemName: "cpu") Text("45%") .font(.caption) } } .menuBarExtraStyle(.window) // Popover style } }

struct StatusBarPopover: View { var body: some View { VStack(spacing: 16) { Text("System Status") .font(.headline)

        // Status content
        StatusRow(title: "CPU", value: "45%")
        StatusRow(title: "Memory", value: "8.2 GB")
        StatusRow(title: "Storage", value: "234 GB")

        Divider()

        Button("Open Activity Monitor") {
            // Open Activity Monitor
        }
    }
    .padding()
    .frame(width: 200)
}

}

Document-Based Apps

Document Type Definition

import SwiftUI import UniformTypeIdentifiers

// Define document type extension UTType { static var myDocument: UTType { UTType(exportedAs: "com.mycompany.mydocument") } }

// Document model struct MyDocument: FileDocument { static var readableContentTypes: [UTType] { [.myDocument, .plainText] } static var writableContentTypes: [UTType] { [.myDocument] }

var content: String

init(content: String = "") {
    self.content = content
}

init(configuration: ReadConfiguration) throws {
    guard let data = configuration.file.regularFileContents,
          let string = String(data: data, encoding: .utf8) else {
        throw CocoaError(.fileReadCorruptFile)
    }
    content = string
}

func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
    let data = content.data(using: .utf8)!
    return FileWrapper(regularFileWithContents: data)
}

}

// Document-based app @main struct DocumentApp: App { var body: some Scene { DocumentGroup(newDocument: MyDocument()) { file in DocumentView(document: file.$document) } .commands { // Document-specific commands CommandGroup(after: .saveItem) { Button("Export as PDF...") { // Export } .keyboardShortcut("e", modifiers: [.command, .shift]) } } } }

struct DocumentView: View { @Binding var document: MyDocument @FocusedValue(.document) private var focusedDocument

var body: some View {
    TextEditor(text: $document.content)
        .font(.body.monospaced())
        .focusedValue(\.document, $document)
}

}

Reference File Documents (Large Files)

import SwiftUI import UniformTypeIdentifiers

// For large files, use ReferenceFileDocument @Observable class ImageDocument: ReferenceFileDocument { static var readableContentTypes: [UTType] { [.png, .jpeg] } static var writableContentTypes: [UTType] { [.png] }

var image: NSImage?
var annotations: [Annotation] = []

init() {}

required init(configuration: ReadConfiguration) throws {
    guard let data = configuration.file.regularFileContents else {
        throw CocoaError(.fileReadCorruptFile)
    }
    image = NSImage(data: data)
}

func snapshot(contentType: UTType) throws -> Data {
    guard let image, let data = image.tiffRepresentation else {
        throw CocoaError(.fileWriteUnknown)
    }
    return data
}

func fileWrapper(snapshot: Data, configuration: WriteConfiguration) throws -> FileWrapper {
    FileWrapper(regularFileWithContents: snapshot)
}

}

@main struct ImageEditorApp: App { var body: some Scene { DocumentGroup(newDocument: { ImageDocument() }) { file in ImageEditorView(document: file.document) } } }

Mac Catalyst

Enabling Mac Catalyst

In Xcode: Target → General → Deployment Info → Mac (Mac Catalyst)

Platform-Specific Code

struct ContentView: View { var body: some View { VStack { #if targetEnvironment(macCatalyst) // Mac Catalyst specific UI MacToolbar() #else // iOS specific UI iOSToolbar() #endif

        MainContent()
    }
}

}

// Check at runtime struct PlatformAwareView: View { var body: some View { Group { if ProcessInfo.processInfo.isMacCatalystApp { MacLayout() } else { iOSLayout() } } } }

Catalyst-Specific Features

#if targetEnvironment(macCatalyst) import AppKit

extension View { func configureMacWindow() -> some View { self.onAppear { guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return }

        // Window size
        windowScene.sizeRestrictions?.minimumSize = CGSize(width: 800, height: 600)
        windowScene.sizeRestrictions?.maximumSize = CGSize(width: 1920, height: 1080)

        // Title bar
        if let titlebar = windowScene.titlebar {
            titlebar.titleVisibility = .hidden
            titlebar.toolbar = nil
        }
    }
}

} #endif

Optimizing for Mac

struct CatalystOptimizedApp: App { var body: some Scene { WindowGroup { ContentView() #if targetEnvironment(macCatalyst) .frame(minWidth: 800, minHeight: 600) .onAppear(perform: setupMacEnvironment) #endif } #if targetEnvironment(macCatalyst) .commands { // Mac-specific menu commands CommandGroup(replacing: .help) { Button("MyApp Help") { // Open help } } } #endif }

#if targetEnvironment(macCatalyst)
private func setupMacEnvironment() {
    // Enable hover effects
    // Configure pointer interactions
    // Set up keyboard shortcuts
}
#endif

}

// Pointer/hover support struct HoverButton: View { @State private var isHovered = false

var body: some View {
    Button("Click Me") { }
        .buttonStyle(.borderedProminent)
        .scaleEffect(isHovered ? 1.05 : 1.0)
        .onHover { hovering in
            withAnimation(.easeInOut(duration: 0.15)) {
                isHovered = hovering
            }
        }
}

}

macOS-Specific Modifiers

Toolbar Customization

struct MacToolbarView: View { @State private var searchText = ""

var body: some View {
    NavigationSplitView {
        Sidebar()
    } detail: {
        DetailView()
    }
    .toolbar {
        // Leading items
        ToolbarItem(placement: .navigation) {
            Button(action: {}) {
                Image(systemName: "sidebar.left")
            }
        }

        // Principal (center)
        ToolbarItem(placement: .principal) {
            Picker("View", selection: .constant(0)) {
                Text("Grid").tag(0)
                Text("List").tag(1)
            }
            .pickerStyle(.segmented)
            .frame(width: 150)
        }

        // Trailing items
        ToolbarItemGroup(placement: .primaryAction) {
            Button(action: {}) {
                Image(systemName: "plus")
            }

            Button(action: {}) {
                Image(systemName: "square.and.arrow.up")
            }
        }

        // Search field (trailing)
        ToolbarItem(placement: .automatic) {
            TextField("Search", text: $searchText)
                .textFieldStyle(.roundedBorder)
                .frame(width: 200)
        }
    }
    .toolbarBackground(.visible, for: .windowToolbar)
}

}

Sidebar and Split View

struct ThreeColumnLayout: View { @State private var selectedFolder: Folder? @State private var selectedItem: Item? @State private var columnVisibility: NavigationSplitViewVisibility = .all

var body: some View {
    NavigationSplitView(columnVisibility: $columnVisibility) {
        // Sidebar (first column)
        List(folders, selection: $selectedFolder) { folder in
            NavigationLink(value: folder) {
                Label(folder.name, systemImage: folder.icon)
            }
        }
        .navigationSplitViewColumnWidth(min: 180, ideal: 200, max: 250)

    } content: {
        // Content (second column)
        if let folder = selectedFolder {
            List(folder.items, selection: $selectedItem) { item in
                NavigationLink(value: item) {
                    ItemRow(item: item)
                }
            }
        } else {
            ContentUnavailableView("Select a Folder", systemImage: "folder")
        }

    } detail: {
        // Detail (third column)
        if let item = selectedItem {
            ItemDetailView(item: item)
        } else {
            ContentUnavailableView("Select an Item", systemImage: "doc")
        }
    }
    .navigationSplitViewStyle(.balanced)
}

@State private var folders: [Folder] = []

}

Settings/Preferences Window

@main struct PreferencesApp: App { var body: some Scene { WindowGroup { ContentView() }

    #if os(macOS)
    Settings {
        SettingsView()
    }
    #endif
}

}

struct SettingsView: View { var body: some View { TabView { GeneralSettings() .tabItem { Label("General", systemImage: "gear") }

        AppearanceSettings()
            .tabItem {
                Label("Appearance", systemImage: "paintbrush")
            }

        AccountSettings()
            .tabItem {
                Label("Account", systemImage: "person.crop.circle")
            }

        AdvancedSettings()
            .tabItem {
                Label("Advanced", systemImage: "gearshape.2")
            }
    }
    .frame(width: 450, height: 300)
}

}

struct GeneralSettings: View { @AppStorage("launchAtLogin") private var launchAtLogin = false @AppStorage("checkForUpdates") private var checkForUpdates = true

var body: some View {
    Form {
        Toggle("Launch at Login", isOn: $launchAtLogin)
        Toggle("Check for Updates Automatically", isOn: $checkForUpdates)
    }
    .padding()
}

}

Inspector Panel

struct InspectorView: View { @State private var showInspector = true @State private var selectedItem: Item?

var body: some View {
    NavigationStack {
        ContentList(selection: $selectedItem)
    }
    .inspector(isPresented: $showInspector) {
        if let item = selectedItem {
            ItemInspector(item: item)
        } else {
            ContentUnavailableView("No Selection", systemImage: "sidebar.right")
        }
    }
    .inspectorColumnWidth(min: 250, ideal: 300, max: 400)
    .toolbar {
        ToolbarItem {
            Button {
                showInspector.toggle()
            } label: {
                Image(systemName: "sidebar.right")
            }
        }
    }
}

}

Keyboard Shortcuts

Custom Keyboard Shortcuts

struct KeyboardShortcutDemo: View { @State private var text = ""

var body: some View {
    TextEditor(text: $text)
        .toolbar {
            // Toolbar button with shortcut
            Button("Bold") {
                makeBold()
            }
            .keyboardShortcut("b", modifiers: .command)

            Button("Italic") {
                makeItalic()
            }
            .keyboardShortcut("i", modifiers: .command)
        }
        // Background keyboard shortcuts
        .background {
            Group {
                Button("") { duplicateLine() }
                    .keyboardShortcut("d", modifiers: [.command, .shift])

                Button("") { deleteLine() }
                    .keyboardShortcut(.delete, modifiers: [.command, .shift])
            }
            .frame(width: 0, height: 0)
            .opacity(0)
        }
}

private func makeBold() {}
private func makeItalic() {}
private func duplicateLine() {}
private func deleteLine() {}

}

Focus-Based Commands

struct FocusedDocument: FocusedValueKey { typealias Value = Binding<MyDocument> }

extension FocusedValues { var document: Binding<MyDocument>? { get { self[FocusedDocument.self] } set { self[FocusedDocument.self] = newValue } } }

struct DocumentCommands: Commands { @FocusedValue(.document) var document

var body: some Commands {
    CommandGroup(after: .textEditing) {
        Button("Word Count") {
            if let doc = document?.wrappedValue {
                let count = doc.content.split(separator: " ").count
                print("Words: \(count)")
            }
        }
        .keyboardShortcut("w", modifiers: [.command, .shift])
        .disabled(document == nil)
    }
}

}

Best Practices

DO

// ✓ Use native macOS controls NavigationSplitView { ... }

// ✓ Support keyboard navigation .focusable() .onKeyPress(.tab) { ... }

// ✓ Respect system appearance @Environment(.colorScheme) var colorScheme

// ✓ Use Settings scene for preferences Settings { SettingsView() }

// ✓ Support window restoration .handlesExternalEvents(matching: ["main"])

DON'T

// ✗ Don't use iOS-only patterns .navigationBarHidden(true) // Use .toolbar instead

// ✗ Don't ignore keyboard shortcuts // Always add for common actions

// ✗ Don't hardcode window sizes .frame(width: 800, height: 600) // Use defaultSize instead

// ✗ Don't forget right-click menus // Add contextMenu to interactive elements

Official Resources

  • Mac App Documentation

  • Human Interface Guidelines - macOS

  • Mac Catalyst Documentation

  • WWDC23: Build programmatic UI with Xcode Previews

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

photographer-testino

No summary provided by upstream source.

Repository SourceNeeds Review
General

photographer-lindbergh

No summary provided by upstream source.

Repository SourceNeeds Review
General

photographer-lachapelle

No summary provided by upstream source.

Repository SourceNeeds Review
General

photographer-vonunwerth

No summary provided by upstream source.

Repository SourceNeeds Review