Open Document-like Windows Using SwiftUI WindowGroup
Why would you need to open document-like windows without using SwiftUI’s document architecture?
Suppose you’re planning to use SwiftUI to make a Mac git client app like SourceTree or Fork. The app should open a window for each open git repo. SwiftUI’s document architecture supports opening a window for each open document, but a git repo can’t use most of the document architecture. You’re not going to open a blank repo window by pressing Cmd-N, save a repo by pressing Cmd-S, or mark a git repo as being edited. How do you open windows for each repo without using SwiftUI’s document architecture?
Create a SwiftUI app project and use the WindowGroup
scene type to open additional windows. You must set up the window group to support creating additional windows and write code to open the window.
Setting up the Window Group
The body for the App
struct in a new SwiftUI project has the following code:
var body: some Scene {
WindowGroup {
ContentView()
}
}
To support creating additional windows, add a for
argument to the WindowGroup
and supply a data type. The data type should be one of your app model’s structs or classes that holds the data to show in the window.
Add an argument to the closure (after the opening brace) so that each window shows a unique instance of the window group’s data type.
The following code creates a new window for each open folder in the app and sets the window’s title to the folder’s display name:
struct Folder: Hashable, Codable {
var url: URL
var displayName: String {
url.path.components(separatedBy: "/").filter{ !$0.isEmpty }.last ?? ""
}
}
WindowGroup(for: Folder.self) { $folder in
ContentView()
.navigationTitle(folder?.displayName ?? "Window")
}
In a real app you would have a @Binding
property in the content view and pass the equivalent of $folder
as an argument to the content view.
Opening the Window
SwiftUI provides an openWindow
environment property for opening windows in SwiftUI Mac apps. Add an @Environment
property to your SwiftUI view.
@Environment(\.openWindow) private var openWindow
Create a new instance of the data structure you used as the for
argument to the window group and pass that instance as the value to the openWindow
function. The following code opens a new window for a user-selected folder:
// Get the URL from a file importer.
let folder = Folder(url: url)
openWindow(value: folder)
Add an Open menu item to the File menu or add a toolbar button to open new windows in your app.
Sample Project
I have a sample project on GitHub for you to view and download. Choose File > Open and select a folder on your Mac. The app creates a window and sets its title to the name of the selected folder.