# macOS Polish Details that make apps feel native and professional. ```swift import SwiftUI struct AppCommands: Commands { var body: some Commands { // File operations CommandGroup(replacing: .saveItem) { Button("Save") { save() } .keyboardShortcut("s", modifiers: .command) Button("Save As...") { saveAs() } .keyboardShortcut("s", modifiers: [.command, .shift]) } // Edit operations (usually automatic) // ⌘Z Undo, ⌘X Cut, ⌘C Copy, ⌘V Paste, ⌘A Select All // View menu CommandMenu("View") { Button("Zoom In") { zoomIn() } .keyboardShortcut("+", modifiers: .command) Button("Zoom Out") { zoomOut() } .keyboardShortcut("-", modifiers: .command) Button("Actual Size") { resetZoom() } .keyboardShortcut("0", modifiers: .command) Divider() Button("Toggle Sidebar") { toggleSidebar() } .keyboardShortcut("s", modifiers: [.command, .control]) Button("Toggle Inspector") { toggleInspector() } .keyboardShortcut("i", modifiers: [.command, .option]) } // Custom menu CommandMenu("Actions") { Button("Run") { run() } .keyboardShortcut("r", modifiers: .command) Button("Build") { build() } .keyboardShortcut("b", modifiers: .command) } } } ``` ```swift struct ContentView: View { var body: some View { MainContent() .onKeyPress(.space) { togglePlay() return .handled } .onKeyPress(.delete) { deleteSelected() return .handled } .onKeyPress(.escape) { clearSelection() return .handled } .onKeyPress("f", modifiers: .command) { focusSearch() return .handled } } } ``` ```swift @main struct MyApp: App { var body: some Scene { WindowGroup { ContentView() } .commands { // Replace standard items CommandGroup(replacing: .newItem) { Button("New Project") { newProject() } .keyboardShortcut("n", modifiers: .command) Button("New from Template...") { newFromTemplate() } .keyboardShortcut("n", modifiers: [.command, .shift]) } // Add after existing group CommandGroup(after: .importExport) { Button("Import...") { importFile() } .keyboardShortcut("i", modifiers: [.command, .shift]) Button("Export...") { exportFile() } .keyboardShortcut("e", modifiers: [.command, .shift]) } // Add entire menu CommandMenu("Project") { Button("Build") { build() } .keyboardShortcut("b", modifiers: .command) Button("Run") { run() } .keyboardShortcut("r", modifiers: .command) Divider() Button("Clean") { clean() } .keyboardShortcut("k", modifiers: [.command, .shift]) } // Add to Help menu CommandGroup(after: .help) { Button("Keyboard Shortcuts") { showShortcuts() } .keyboardShortcut("/", modifiers: .command) } } } } ``` ```swift struct ItemRow: View { let item: Item var body: some View { Text(item.name) .contextMenu { Button("Open") { open(item) } Button("Open in New Window") { openInNewWindow(item) } Divider() Button("Duplicate") { duplicate(item) } .keyboardShortcut("d", modifiers: .command) Button("Rename") { rename(item) } Divider() Button("Delete", role: .destructive) { delete(item) } } } } ``` ```swift @main struct MyApp: App { var body: some Scene { // Main document window DocumentGroup(newDocument: MyDocument()) { file in DocumentView(document: file.$document) } // Auxiliary windows Window("Inspector", id: "inspector") { InspectorView() } .windowResizability(.contentSize) .defaultPosition(.trailing) .keyboardShortcut("i", modifiers: [.command, .option]) // Floating utility Window("Quick Entry", id: "quick-entry") { QuickEntryView() } .windowStyle(.hiddenTitleBar) .windowResizability(.contentSize) Settings { SettingsView() } } } // Open window from view struct ContentView: View { @Environment(\.openWindow) private var openWindow var body: some View { Button("Show Inspector") { openWindow(id: "inspector") } } } ``` ```swift // Save and restore window state class WindowStateManager { static func save(_ window: NSWindow, key: String) { let frame = window.frame UserDefaults.standard.set(NSStringFromRect(frame), forKey: "window.\(key).frame") } static func restore(_ window: NSWindow, key: String) { guard let frameString = UserDefaults.standard.string(forKey: "window.\(key).frame"), let frame = NSRectFromString(frameString) as NSRect? else { return } window.setFrame(frame, display: true) } } // Window delegate class WindowDelegate: NSObject, NSWindowDelegate { func windowWillClose(_ notification: Notification) { guard let window = notification.object as? NSWindow else { return } WindowStateManager.save(window, key: "main") } } ``` ```swift class AppDelegate: NSObject, NSApplicationDelegate { func applicationDockMenu(_ sender: NSApplication) -> NSMenu? { let menu = NSMenu() menu.addItem(NSMenuItem( title: "New Project", action: #selector(newProject), keyEquivalent: "" )) menu.addItem(NSMenuItem.separator()) // Recent items let recentProjects = RecentProjectsManager.shared.projects for project in recentProjects.prefix(5) { let item = NSMenuItem( title: project.name, action: #selector(openRecent(_:)), keyEquivalent: "" ) item.representedObject = project.url menu.addItem(item) } return menu } @objc private func newProject() { NSDocumentController.shared.newDocument(nil) } @objc private func openRecent(_ sender: NSMenuItem) { guard let url = sender.representedObject as? URL else { return } NSDocumentController.shared.openDocument( withContentsOf: url, display: true ) { _, _, _ in } } } ``` ```swift struct ItemRow: View { let item: Item var body: some View { HStack { Image(systemName: item.icon) VStack(alignment: .leading) { Text(item.name) Text(item.date.formatted()) .font(.caption) } } .accessibilityElement(children: .combine) .accessibilityLabel("\(item.name), \(item.date.formatted())") .accessibilityHint("Double-tap to open") .accessibilityAddTraits(.isButton) } } ``` ```swift struct NoteListView: View { let notes: [Note] @State private var selectedNote: Note? var body: some View { List(notes, selection: $selectedNote) { note in NoteRow(note: note) } .accessibilityRotor("Pinned Notes") { ForEach(notes.filter { $0.isPinned }) { note in AccessibilityRotorEntry(note.title, id: note.id) { selectedNote = note } } } .accessibilityRotor("Recent Notes") { ForEach(notes.sorted { $0.modifiedAt > $1.modifiedAt }.prefix(10)) { note in AccessibilityRotorEntry("\(note.title), modified \(note.modifiedAt.formatted())", id: note.id) { selectedNote = note } } } } } ``` ```swift struct AnimationHelper { static var prefersReducedMotion: Bool { NSWorkspace.shared.accessibilityDisplayShouldReduceMotion } static func animation(_ animation: Animation) -> Animation? { prefersReducedMotion ? nil : animation } } // Usage withAnimation(AnimationHelper.animation(.spring())) { isExpanded.toggle() } ``` ```swift extension UserDefaults { enum Keys { static let theme = "theme" static let fontSize = "fontSize" static let recentFiles = "recentFiles" static let windowFrame = "windowFrame" } var theme: String { get { string(forKey: Keys.theme) ?? "system" } set { set(newValue, forKey: Keys.theme) } } var fontSize: Double { get { double(forKey: Keys.fontSize).nonZero ?? 14.0 } set { set(newValue, forKey: Keys.fontSize) } } var recentFiles: [URL] { get { guard let data = data(forKey: Keys.recentFiles), let urls = try? JSONDecoder().decode([URL].self, from: data) else { return [] } return urls } set { let data = try? JSONEncoder().encode(newValue) set(data, forKey: Keys.recentFiles) } } } extension Double { var nonZero: Double? { self == 0 ? nil : self } } // Register defaults at launch func registerDefaults() { UserDefaults.standard.register(defaults: [ UserDefaults.Keys.theme: "system", UserDefaults.Keys.fontSize: 14.0 ]) } ``` ```swift struct ErrorPresenter: ViewModifier { @Binding var error: AppError? func body(content: Content) -> some View { content .alert( "Error", isPresented: Binding( get: { error != nil }, set: { if !$0 { error = nil } } ), presenting: error ) { _ in Button("OK", role: .cancel) {} } message: { error in Text(error.localizedDescription) } } } extension View { func errorAlert(_ error: Binding) -> some View { modifier(ErrorPresenter(error: error)) } } // Usage ContentView() .errorAlert($appState.error) ``` ```swift struct OnboardingView: View { @AppStorage("hasSeenOnboarding") private var hasSeenOnboarding = false @Environment(\.dismiss) private var dismiss var body: some View { VStack(spacing: 24) { Image(systemName: "star.fill") .font(.system(size: 64)) .foregroundStyle(.accentColor) Text("Welcome to MyApp") .font(.largeTitle) VStack(alignment: .leading, spacing: 16) { FeatureRow(icon: "doc.text", title: "Create Documents", description: "Organize your work in documents") FeatureRow(icon: "folder", title: "Stay Organized", description: "Use folders and tags") FeatureRow(icon: "cloud", title: "Sync Everywhere", description: "Access on all your devices") } Button("Get Started") { hasSeenOnboarding = true dismiss() } .buttonStyle(.borderedProminent) } .padding(40) .frame(width: 500) } } struct FeatureRow: View { let icon: String let title: String let description: String var body: some View { HStack(spacing: 12) { Image(systemName: icon) .font(.title2) .frame(width: 40) .foregroundStyle(.accentColor) VStack(alignment: .leading) { Text(title).fontWeight(.medium) Text(description).foregroundStyle(.secondary) } } } } ``` ```swift // Add Sparkle package for auto-updates // https://github.com/sparkle-project/Sparkle import Sparkle class UpdaterManager { private var updater: SPUUpdater? func setup() { let controller = SPUStandardUpdaterController( startingUpdater: true, updaterDelegate: nil, userDriverDelegate: nil ) updater = controller.updater } func checkForUpdates() { updater?.checkForUpdates() } } // In commands CommandGroup(after: .appInfo) { Button("Check for Updates...") { updaterManager.checkForUpdates() } } ``` ```swift class AppDelegate: NSObject, NSApplicationDelegate { func applicationDidFinishLaunching(_ notification: Notification) { // Register defaults registerDefaults() // Setup services setupServices() // Check for updates checkForUpdates() } func applicationWillTerminate(_ notification: Notification) { // Save state saveApplicationState() } func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { // Return false for document-based or menu bar apps return false } func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { if !flag { // Reopen main window NSDocumentController.shared.newDocument(nil) } return true } } ```