Windows and commands

Add a Settings scene

Store small preferences with AppStorage and let SwiftUI connect a normal settings window to the application menu.

Every Mac app has a Settings item in its application menu, with ⌘, as the shortcut. Users don’t think about this. They expect it, the way they expect Quit at the bottom of the same menu.

SwiftUI wires all of it from one scene declaration. Add it beside your other scenes:

Settings {
  SettingsView()
}

That’s the whole integration. The menu item appears, ⌘, works, and macOS presents a standard settings window. You never write “open the settings window” code.

Store preferences with @AppStorage

Inside, build a form. For small preferences, @AppStorage connects a property straight to UserDefaults:

struct SettingsView: View {
  @AppStorage("showLineNumbers") private var showLineNumbers = true
  @AppStorage("fontSize") private var fontSize = 14.0

  var body: some View {
    Form {
      Toggle("Show line numbers", isOn: $showLineNumbers)
      Slider(value: $fontSize, in: 10...24) {
        Text("Font size")
      }
    }
    .padding()
    .frame(width: 350)
  }
}

Reading the same key somewhere else gives you a live value. Declare @AppStorage("showLineNumbers") in the note detail view and the UI updates the moment the toggle changes. No notification code, no manual refresh.

Two rules for keys

First, key names are permanent. Rename "showLineNumbers" in a later version and every user silently loses their preference. Define keys once and never touch them again.

Second, every key needs a sensible default, because the first launch has no stored value. The = true and = 14.0 in the code above are those defaults.

Split into tabs when settings grow

System apps organize settings into tabs. You can do the same:

TabView {
  GeneralSettings()
    .tabItem { Label("General", systemImage: "gearshape") }
  EditorSettings()
    .tabItem { Label("Editor", systemImage: "textformat") }
}

What does not belong here

UserDefaults stores plain text in a plist anyone can read with one Terminal command. API tokens and passwords go in the Keychain, and we’ll do exactly that in a later lesson.

Primary documents don’t belong here either. The notes themselves need real files with atomic saves, not preference storage.

Verify

Run the app. The application menu shows Settings…, ⌘, opens the window, toggling the switch updates the editor immediately, and the value survives a relaunch.

If the menu item is missing, your Settings scene is probably declared inside another scene’s body instead of beside it. Scenes are siblings in body, never nested.

Lesson completed