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()
}
}
}
# 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
...
}
# 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
...
}
# 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()
}
}
}
# MenuBarExtra
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)
}
}
# 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.
If you have older iOS apps and want to enhance them with modern SwiftUI features, check out my book Integrating SwiftUI into UIKit Apps. It provides detailed guidance on gradually adopting SwiftUI in your UIKit projects. Additionally, if you're eager to enhance your Swift programming skills, my latest book Swift Gems offers over a hundred advanced tips and techniques, including optimizing collections, handling strings, mastering asynchronous programming, and debugging, to take your Swift code to the next level.