WWDC24 deal: 30% off our Swift and SwiftUI books! Learn more ...WWDC24 deal:30% off our Swift and SwiftUI books >>

Scenes types in a SwiftUI Mac app

Creating versatile and efficient macOS applications with SwiftUI involves understanding how to use various scene types. Scenes in SwiftUI manage and display their contents through windows, with different scene types offering distinct behaviors and capabilities. In this post, we'll explore the different scene types available in SwiftUI for macOS, including WindowGroup, DocumentGroup, Settings, Window, and MenuBarExtra.

# WindowGroup

The WindowGroup scene is a key tool for developing data-driven applications in SwiftUI. It allows for the creation of multiple instances of a scene, with each instance represented by its own window. This capability is especially useful for applications that need to present different views independently.

On macOS, WindowGroup offers several unique advantages. Users can organize open windows into a tabbed interface, enhancing usability by allowing better organization and quick access to multiple windows within the same application. Additionally, WindowGroup on macOS includes built-in commands for standard window management tasks, such as minimizing and closing windows.

Each window in a WindowGroup maintains its own state. The system allocates separate storage for any State variables instantiated by the scene’s view hierarchy for each window. This ensures that each window operates with its own unique data and behavior, allowing for highly dynamic and interactive applications.

Here’s an example of how to use WindowGroup in a SwiftUI macOS app:

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
Screenshot of a Mac app that shows two windows with a different navigation state

# DocumentGroup

The DocumentGroup scene in SwiftUI is specifically designed for applications that work with document-based data. It manages the lifecycle of documents, including their creation, opening, and saving, making it well-suited for file-centric apps.

To set up a DocumentGroup scene, we need to provide a document model and a view that can display and edit the document. SwiftUI leverages this model to integrate document handling features into our app. On macOS, this integration includes menu support for managing documents, such as opening recent files, creating new ones, and handling multiple documents at the same time.

The DocumentGroup scene dynamically updates its contents whenever the document's configuration changes. This behavior ensures that the app remains consistent with the current document data.

Here’s an example of how to implement DocumentGroup in SwiftUI:

@main
struct MyDocumentApp: App {
    var body: some Scene {
        DocumentGroup(newDocument: MyDocument()) { file in
            DocumentView(document: file.$document)
        }
    }
}

struct MyDocument: FileDocument {
    ...
}

struct DocumentView: View {
    @Binding var document: MyDocument
    
    ...
}
Screenshot of a Mac app that shows two text documents

# Settings

The Settings scene provides an interface for representing settings values on macOS. This scene is ideal for creating a dedicated settings window that users can access to configure the application’s preferences.

When we pass a view to the Settings scene in the App declaration, SwiftUI automatically enables the app’s "Settings" menu item. This integration ensures that SwiftUI manages the display and removal of the settings view when the user selects the "Settings" option from the application menu or uses the corresponding keyboard shortcut.

Here's an example usage of the Settings scene:

@main
struct SettingsExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        Settings {
            SettingsView()
        }
    }
}

struct SettingsView: View {
    @AppStorage("username")
    private var username: String = "User"
    
    @AppStorage("notificationsEnabled")
    private var notificationsEnabled: Bool = true

    ...
}
Screenshot of a Mac app that shows an example Settings window

# Window

The Window scene type creates a single unique window. This is particularly useful when we need a specific window that doesn’t follow the typical grouping behavior of WindowGroup.

Unlike WindowGroup, which manages multiple windows of the same type and automatically handles their creation and presentation, Window is intended for scenarios where a single, distinct window is required. This is ideal for tools or utility panels that need to be accessible from multiple places within our application but should only have one instance open at any given time. If an app uses a single window as its main scene, the app quits when the window is closed.

Here’s an example of how to define a unique window in a macOS SwiftUI app using the Window scene type:

@main
struct SingleWindowExample: App {
    var body: some Scene {
        Window("My Unique Window", id: "uniqueWindow") {
            UniqueWindowView()
        }
    }
}
Screenshot of a Mac app that shows a single window with sample text

The MenuBarExtra scene renders as a persistent control in the macOS system menu bar, making it ideal for utilities or controls that need to be readily available. It provides users with quick and easy access to essential app functionality without opening the main app window.

MenuBarExtra offers a seamless way to integrate additional features directly into the menu bar, perfect for apps that deliver system-level utilities, frequently used functions, or status monitoring.

In addition to its basic functionality, MenuBarExtra offers customization through styles, allowing developers to tailor the behavior and appearance of the menu bar item to better fit their application's needs.

Here’s an example of how to define a MenuBarExtra scene with the window style:

@main
struct MenuBarExampleApp: App {
    var body: some Scene {
        MenuBarExtra("My Menu Bar Extra", systemImage: "gear") {
            MenuBarView()
        }
        .menuBarExtraStyle(.window)
    }
}
Screenshot of a Mac app that shows a menu bar extra window

# Composing scene types

One of the powerful features of SwiftUI is the ability to compose different scene types together within an app. This allows for creating complex and feature-rich applications by leveraging multiple scenes.

@main
struct MyCompositeApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        Settings {
            SettingsView()
        }
        MenuBarExtra("My Menu Bar Extra", systemImage: "gear") {
            MenuBarView()
        }
    }
}

In this example, we combine WindowGroup, Settings, and MenuBarExtra to build a comprehensive application with multiple windows, a settings panel, and a menu bar utility.

SwiftUI provides a variety of scene types to suit different application needs on macOS. Whether we are building a data-driven app, a document-based app, or a utility accessible from the menu bar, understanding and utilizing these scene types effectively can help us create powerful and flexible applications.

Books by Natalia PanferovaBooks by Natalia Panferova
WWDC24: 30% off all books!
  • Swift Gems

    100+ tips to take your Swift code to the next level

  • Integrating SwiftUI into UIKit Apps

    A detailed guide on gradually adopting SwiftUI in UIKit projects

The offer is active until the 16th of June.