swiftship/internal/skills/data/always-macos/macos-patterns/SKILL.md
macOS platform patterns: window management, menu bar, keyboard shortcuts, Settings, drag-and-drop, App Sandbox. Use when building macOS-specific features, handling desktop input, or implementing Mac app patterns. Triggers: macOS, Mac, desktop, menu bar, keyboard shortcut, Settings scene, window management.
npx skillsauth add abdullah4ai/apple-developer-toolkit macos-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Three scene types for Mac apps:
@main
struct MyApp: App {
var body: some Scene {
// Main window — users can open multiple instances
WindowGroup {
ContentView()
}
.defaultSize(width: 900, height: 600)
// Single utility window
Window("Inspector", id: "inspector") {
InspectorView()
}
.defaultSize(width: 300, height: 400)
// Document-based app
DocumentGroup(newDocument: MyDocument()) { file in
DocumentEditor(document: file.$document)
}
}
}
CommandMenu closures run at the App scene level — outside the view hierarchy. They CANNOT access view @State directly. Use @FocusedValue to bridge view state to menu actions.
// 1. Define a FocusedValue key using @Entry
extension FocusedValues {
@Entry var activeDocument: DocumentViewModel?
}
// 2. ViewModel — @Observable only (NOT ObservableObject)
@Observable @MainActor
class DocumentViewModel {
var content = ""
func save() { /* persist */ }
func togglePreview() { /* toggle */ }
}
// 3. Publish from active view using .focusedValue()
struct DocumentView: View {
@State var viewModel = DocumentViewModel()
var body: some View {
EditorContent(viewModel: viewModel)
.focusedValue(\.activeDocument, viewModel) // REQUIRED — publish to menu bar
}
}
// 4. Consume in App via @FocusedValue and wire ALL menu actions
@main
struct MyApp: App {
@FocusedValue(\.activeDocument) private var document
var body: some Scene {
WindowGroup { DocumentView() }
CommandMenu("Document") {
Button("Save") { document?.save() }
.keyboardShortcut("s", modifiers: .command)
.disabled(document == nil)
Button("Toggle Preview") { document?.togglePreview() }
.keyboardShortcut("p", modifiers: [.command, .option])
.disabled(document == nil)
}
CommandGroup(replacing: .newItem) {
Button("New Document") { document?.createNew() }
.keyboardShortcut("n", modifiers: .command)
.disabled(document == nil)
}
}
}
Rules:
@Entry macro.focusedValue(\.key, value) on the active view to publish state@FocusedValue(\.key) private var name in the App struct.disabled(object == nil) on every menu item — menus are active even when no view is focused{} on CommandMenu buttons is unacceptable — every action must call through to the FocusedValueEvery menu item and primary action needs .keyboardShortcut():
// Standard shortcuts
Button("Save") { save() }
.keyboardShortcut("s", modifiers: .command)
Button("Undo") { undo() }
.keyboardShortcut("z", modifiers: .command)
Button("Find") { showSearch() }
.keyboardShortcut("f", modifiers: .command)
// Custom shortcuts
Button("Toggle Sidebar") { toggleSidebar() }
.keyboardShortcut("s", modifiers: [.command, .control])
@main
struct MyApp: App {
var body: some Scene {
WindowGroup { ContentView() }
// Auto-creates "Settings..." in app menu (Cmd+,)
Settings {
SettingsView()
}
}
}
struct SettingsView: View {
var body: some View {
TabView {
GeneralSettingsView()
.tabItem { Label("General", systemImage: "gear") }
AppearanceSettingsView()
.tabItem { Label("Appearance", systemImage: "paintbrush") }
}
.frame(width: 450, height: 300)
}
}
@main
struct StatusApp: App {
var body: some Scene {
MenuBarExtra("Status", systemImage: "circle.fill") {
StatusMenuView()
}
.menuBarExtraStyle(.window)
}
}
// Draggable item
ItemRow(item: item)
.draggable(item)
// Drop destination
FolderView(folder: folder)
.dropDestination(for: Item.self) { items, location in
moveItems(items, to: folder)
return true
}
macOS apps need entitlements for system access:
com.apple.security.files.user-selected.read-writecom.apple.security.network.clientcom.apple.security.device.cameracom.apple.security.device.audio-inputUse for platform-specific code in shared modules:
#if os(macOS)
import AppKit
// macOS-specific code
#elseif os(iOS)
import UIKit
// iOS-specific code
#endif
// Translucent glass backgrounds
VStack { content }
.glassEffect()
// Glass button styles
Button("Action") { }
.buttonStyle(.glass)
Button("Primary") { }
.buttonStyle(.glassProminent)
// Open panel
.fileImporter(
isPresented: $showOpen,
allowedContentTypes: [.json]
) { result in
// handle
}
// Save panel
.fileExporter(
isPresented: $showSave,
document: doc,
contentType: .json
) { result in
// handle
}
.foregroundStyle() not .foregroundColor().clipShape(.rect(cornerRadius:)) not .cornerRadius()@Observable not ObservableObject — never combine both on the same classUIColor / UIImage — use SwiftUI Color / Image insteadWindowGroup, Window, or DocumentGroup for scene typesCommandMenu / CommandGroup for menu bar customization.keyboardShortcut()Settings { } scene for preferences (auto-wires Cmd+,)MenuBarExtra for menu bar apps with .menuBarExtraStyle(.window).draggable() / .dropDestination() for drag-and-drop#if os(macOS) for platform-specific code in shared modulesColor/Image not NSColor/NSImage.foregroundStyle() not .foregroundColor().clipShape(.rect(cornerRadius:)) not .cornerRadius()tools
Apple platform skill for docs, WWDC lookup, App Store Connect work, and SwiftUI app generation. Use repo-local `node cli.js` for Apple docs and WWDC search, `appledev store` for App Store Connect workflows, and `appledev build` for app scaffolding or fix loops on macOS. USE WHEN: Apple APIs, WWDC sessions, TestFlight/App Store tasks, or building/fixing Apple-platform apps. DON'T USE WHEN: non-Apple platforms, generic backend work, or general web research. EDGE CASES: docs-only queries use `node cli.js` in this repo, not `appledev`; release workflows use `appledev store`; app scaffolding uses `appledev build`; rules-only requests can read `references/ios-rules/` or `references/swiftui-guides/` progressively without invoking binaries.
tools
All-in-one Apple developer skill with three integrated tools shipped as a single unified binary. (1) Documentation search across Apple frameworks, symbols, and 1,267 WWDC sessions from 2014-2025. No credentials needed. (2) App Store Connect CLI with 120+ commands covering builds (find/wait/upload), TestFlight, pre-submission validate, submissions, signing, subscriptions (family-sharable), IAP, analytics, Xcode Cloud, metadata workflows, release pipeline dashboard, insights, win-back offers, promoted purchases, product pages, nominations, accessibility declarations, pre-orders, pricing filters, localizations update, diff, webhooks with local receiver, workflow automation, and more. Requires App Store Connect API key. (3) Multi-platform app builder (iOS/watchOS/tvOS/iPad/macOS/visionOS) that generates complete Swift/SwiftUI apps from natural language with auto-fix, simulator launch, interactive chat mode, and open-in-Xcode. Requires an LLM API key and Xcode. Includes 38 iOS development rules and 12 SwiftUI best practice guides for Liquid Glass, navigation, state management, and modern APIs. All three tools ship as one binary (appledev). USE WHEN: Apple API docs, App Store Connect management, WWDC lookup, or building iOS/watchOS/tvOS/macOS/visionOS apps from scratch. DON'T USE WHEN: non-Apple platforms or general coding.
testing
watchOS complications: WidgetKit complication families, accessory sizes, timeline providers for watch face. Use when implementing watchOS-specific patterns related to widgets.
development
watchOS haptic feedback: WKInterfaceDevice preset haptic types for wrist-based feedback. Use when implementing watchOS-specific patterns related to haptics.