WWDC Quick Look 💓 By SwiftGGTeam
SwiftUI on the Mac: Build the fundamentals

SwiftUI on the Mac: Build the fundamentals

Watch original video

Highlight

Excellent Mac Apps have four common characteristics: flexible (customizable), familiar (following system conventions), vast (taking advantage of the large screen), and precise (high information density).Session demonstrates how to implement these principles with SwiftUI using a gardening management app.

Core Content

SwiftUI improvements in macOS Monterey make it truly usable for building production-grade Mac apps.Session uses a gardening tracking app as a run-through example, built from scratch.

SwiftUI implementation of four core principles:

  • Flexible: use@SceneStorageSave the sidebar expanded state and selected items so that the user’s interface configuration is maintained after restarting
  • Familiar: UseCommandsModifiers build the standard menu bar,ToolbarItemPlace it at the agreed location
  • Broad: useTableDisplay multiple columns of data,DisclosureGroupControl content display
  • Accurate: Table’s compact row height, Sidebar’s minimum width setting

Detailed Content

Sidebar of Mac App is usually usedDisclosureGroupImplement outline view.Cooperate@SceneStorageCan save expanded state (06:09).

@SceneStorage("expansionState") private var expansionState = Set<String>()

var body: some View {
    List {
        DisclosureGroup("Current Year", isExpanded: $isCurrentExpanded) {
            ForEach(currentYearGardens) { garden in
                Label(garden.name, systemImage: "leaf")
                    .tag(garden.id)
            }
        }
        DisclosureGroup("History") {
            GardenHistoryOutline()
        }
    }
    .frame(minWidth: 150)
}

Key points:

  • @SceneStorageAutomatically save the state when the app exits and restore it after restarting
  • DisclosureGroupProvides a collapsible outline view
  • tag()Cooperateselectionbinding supports keyboard navigation and selected state

Table: Multi-column data display

TableIt is a new component of macOS Monterey, similar toNSTableView(09:14)。

Table(plants, selection: $selectedPlant, sortOrder: $sortOrder) {
    TableColumn("Name", value: \.name)
    TableColumn("Variety", value: \.variety)
    TableColumn("Days to Maturity") { plant in
        Text("\(plant.daysToMaturity)")
    }
}

Key points:

  • TableSupports multi-column display, thanListSuitable for more complex data
  • selectionbinding supports single or multiple selection
  • sortOrderbinding automatically supports column sorting
  • Automatically downgrade toList

Toolbar: Toolbar configuration

The toolbar is the standard place for Mac users to discover functionality (11:21).

Table(plants) {
    // Column definitions...
}
.toolbar {
    ToolbarItem(placement: .primaryAction) {
        Button(action: addPlant) {
            Label("Add Plant", systemImage: "plus")
        }
    }
    ToolbarItem(placement: .automatic) {
        DisplayModePicker()
    }
}

Key points:

  • ToolbarItemofplacementDetermine location in toolbar
  • .primaryActionPlace it in a prominent position on the left
  • .automaticLet the system determine the location based on space
  • Buttons can be disabled based on selected state

Searchable: Search function

There’s always a hardware keyboard on your Mac, and searching is an effective way to quickly sift through your data (12:15).

Table(plants.filter { searchText.isEmpty || $0.name.contains(searchText) }) {
    // Column definitions...
}
.searchable(text: $searchText)

Key points:

  • .searchableModifier adds search box to toolbar
  • The search box on Mac is fixed to the right side of the toolbar
  • supportCmd+FShortcut keys focus search box
  • Automatically handle focus and keyboard navigation

Commands: Menu bar configuration

The menu bar is an important way for Mac users to explore functionality (13:17).

@main
struct GardenApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .commands {
            SidebarCommands()
            CommandGroup(before: .newItem) {
                Button("Add Plant") {
                    // Add plant
                }
                .keyboardShortcut("n", modifiers: .command)
            }
            CommandMenu("Plants") {
                Button("Mark as Watered") {
                    // Mark as watered
                }
                .keyboardShortcut("w", modifiers: [.command, .shift])
            }
        }
    }
}

Key points:

  • .commandsModifier configuration menu bar
  • SidebarCommands()Add standard sidebar toggle menu
  • CommandGroup(before:)Insert custom command before system menu
  • CommandMenuCreate a new top-level menu

FocusedSceneValue: Communication between windows

Menu commands for multi-window apps need to act on the foreground window (14:01).

struct GardenDetail: View {
    @Binding var garden: Garden?

    var body: some View {
        Table(plants, selection: $selectedPlants) {
            // Column definitions...
        }
        .focusedSceneValue(\.selectedGarden, $garden)
        .focusedSceneValue(\.selectedPlants, $selectedPlants)
    }
}
struct PlantCommands: Commands {
    @FocusedValue(\.selectedGarden) private var focusedGarden: Binding<Garden>?

    var body: some Commands {
        CommandMenu("Plants") {
            Button("Mark as Watered") {
                focusedGarden?.wrappedValue.markAsWatered()
            }
            .disabled(focusedGarden == nil)
        }
    }
}

Key points:

  • @FocusedValueRead the value of the currently focused window
  • focusedSceneValueExpose binding to menu commands
  • Menu commands automatically act on the front window
  • No need to manually track window focus

Core Takeaways

  1. Use @SceneStorage to save user interface state: Mac App has multiple windows, and the state of each window should be saved independently.@SceneStorageThis is handled automatically, eliminating the need to manually read and write UserDefaults.

  2. Use Table instead of List to display complex data: When you need multi-column, sortable data, useTableinstead ofListTableDesigned for information density, supports sorting and selection, automatically downgrades toList

  3. Menu bar shortcuts for all actions: Mac users expect to access all functions through the menu bar and shortcuts.Even if your app has a toolbar, these actions should be repeated in the menu bar to increase the discoverability of the functionality.

  4. Add search function with Searchable: Mac’s toolbar has enough space..searchableMake the search box appear in the standard position, supportCmd+FFocused and in line with user habits.

  5. Use FocusedSceneValue to achieve collaboration between windows: The menu commands of a multi-window App need to act on the front window.focusedSceneValue + @FocusedValueThis is achieved without the need to manually manage focus tracking.

Comments

GitHub Issues · utterances