# macos-app-structure
> macOS application architecture patterns covering App protocol (@main), Scene types (WindowGroup, Window, Settings, MenuBarExtra), multi-window management, NSApplicationDelegateAdaptor for AppKit lifecycle hooks, Info.plist configuration (LSUIElement for menu bar apps, NSAccessibilityUsageDescription), entitlements for sandbox/hardened runtime, and project structure conventions. Use when scaffolding a new macOS app, configuring scenes and windows, setting up menu bar apps, or resolving macOS-specific lifecycle issues. Corrects the common LLM mistake of generating iOS-only app structures.
- Author: Mehmet
- Repository: makgunay/claude-swift-skills
- Version: 20260206203503
- Stars: 0
- Forks: 0
- Last Updated: 2026-02-07
- Source: https://github.com/makgunay/claude-swift-skills
- Web: https://mule.run/skillshub/@@makgunay/claude-swift-skills~macos-app-structure:20260206203503
---
---
name: macos-app-structure
description: macOS application architecture patterns covering App protocol (@main), Scene types (WindowGroup, Window, Settings, MenuBarExtra), multi-window management, NSApplicationDelegateAdaptor for AppKit lifecycle hooks, Info.plist configuration (LSUIElement for menu bar apps, NSAccessibilityUsageDescription), entitlements for sandbox/hardened runtime, and project structure conventions. Use when scaffolding a new macOS app, configuring scenes and windows, setting up menu bar apps, or resolving macOS-specific lifecycle issues. Corrects the common LLM mistake of generating iOS-only app structures.
---
# macOS App Structure
## Critical Constraints
- ❌ DO NOT use iOS-only scenes (`TabView` as root scene) → ✅ Use `WindowGroup`, `Window`, or `NavigationSplitView`
- ❌ DO NOT use `UIApplicationDelegate` → ✅ Use `NSApplicationDelegateAdaptor` for AppKit hooks
- ❌ DO NOT forget `Settings` scene for Preferences → ✅ macOS apps should have a Settings scene
- ❌ DO NOT assume single-window → ✅ macOS apps can have multiple windows; design for it
- ❌ DO NOT use iOS navigation patterns → ✅ Use `NavigationSplitView` (sidebar + detail) for macOS
## Standard macOS App
```swift
import SwiftUI
@main
struct MyApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
.defaultSize(width: 900, height: 600)
Settings {
SettingsView()
}
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
// AppKit lifecycle hooks
}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true // Quit when last window closes
}
}
```
## Menu Bar App
```swift
@main
struct MenuBarApp: App {
var body: some Scene {
MenuBarExtra("My App", systemImage: "command") {
MenuBarView()
}
.menuBarExtraStyle(.window) // Full window popover (not just menu items)
Settings {
SettingsView()
}
}
}
```
To hide dock icon, add to Info.plist:
```xml
LSUIElement
```
## Multiple Named Windows
```swift
@main
struct MultiWindowApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
Window("Inspector", id: "inspector") {
InspectorView()
}
.defaultSize(width: 300, height: 400)
.defaultPosition(.trailing)
Settings {
SettingsView()
}
}
}
// Open a named window from code
@Environment(\.openWindow) private var openWindow
Button("Open Inspector") { openWindow(id: "inspector") }
```
## Content View with Sidebar
```swift
struct ContentView: View {
@State private var selection: SidebarItem? = .library
var body: some View {
NavigationSplitView {
List(selection: $selection) {
Section("Library") {
Label("All Items", systemImage: "square.grid.2x2")
.tag(SidebarItem.library)
Label("Favorites", systemImage: "heart")
.tag(SidebarItem.favorites)
}
}
.navigationSplitViewColumnWidth(min: 180, ideal: 220)
} detail: {
switch selection {
case .library: LibraryView()
case .favorites: FavoritesView()
case nil: ContentUnavailableView("Select an item", systemImage: "sidebar.left")
}
}
.navigationTitle("My App")
}
}
```
## Project Structure Convention
```
MyApp/
├── MyApp.swift # @main App struct
├── AppDelegate.swift # NSApplicationDelegateAdaptor (if needed)
├── Models/ # SwiftData @Model classes
├── Views/
│ ├── ContentView.swift # Main navigation structure
│ ├── Components/ # Reusable view components
│ └── Settings/ # Settings/Preferences views
├── ViewModels/ # @Observable view models
├── Services/ # Business logic, networking, persistence
├── Utilities/ # Extensions, helpers
├── Resources/
│ ├── Assets.xcassets
│ └── Localizable.xcstrings
├── Info.plist
└── MyApp.entitlements
```
## Key Info.plist Entries (macOS)
```xml
LSUIElement
NSAccessibilityUsageDescription
NSAppleEventsUsageDescription
```
## Key Entitlements
```xml
com.apple.security.app-sandbox
com.apple.security.network.client
com.apple.security.files.user-selected.read-write
com.apple.developer.icloud-container-identifiers
```
## Common Mistakes & Fixes
| Mistake | Fix |
|---------|-----|
| No `Settings` scene | Add `Settings { SettingsView() }` — expected on macOS |
| App doesn't quit when last window closes | Implement `applicationShouldTerminateAfterLastWindowClosed` |
| Dock icon showing for menu bar app | Add `LSUIElement = true` to Info.plist |
| Window too small on macOS | Add `.defaultSize(width:height:)` to WindowGroup |
| Using `TabView` as main navigation | Use `NavigationSplitView` with sidebar on macOS |
## References
- [SwiftUI App Structure](https://developer.apple.com/documentation/SwiftUI/App)
- [MenuBarExtra](https://developer.apple.com/documentation/SwiftUI/MenuBarExtra)
- [App Sandbox Design Guide](https://developer.apple.com/documentation/security/app_sandbox)