Search code examples
macosswiftuiwindowpersistence

How to close all windows in a WindowGroup?


I am coding in SwiftUI for MacOS and I'm looking for a way to close all open windows.

My app has what Apple calls a data-driven window group: when a user clicks on an on-screen object for more information, a new detail window appears specific to the object they click on.

The problem I'm having is that these windows are persistent by default. When the user quits and runs the app again, all the detail screens that were previously open appear again, even when the items they describe no longer exist. I do not want any of these windows to appear upon restart.

Here's the (simplified) code. My App.swift looks like this:

@main
struct MyApp: App {
    @State var vm = ViewModel()

    var body: some Scene {
        WindowGroup { // main window
            ContentView()
                .environment(vm)
        }
        WindowGroup(for: Item.ID.self) { $itemID in
            ItemDetails(itemID: itemID)
                .environment(vm)
        }
    }
}

And my ContentView.swift:

struct ContentView: View {
    @Environment(ViewModel.self) private var vm
    @Environment(\.openWindow) private var openWindow

    var body: some View {
        ForEach(vm.items) { item in
            Button(item.name) {
                openWindow(value: item.id)
            }
        }
    }
}

Of course the user can close the windows manually, but if they create 15 new ItemDetails windows in a session and then quit. I don't want them to have to start their next session by closing all the "leftover" windows from their previous session.

Alternatively, I could have the app close all its windows upon quit. But I'd like a solution that works even when the app quits unexpectedly. Such as when Xcode halts it to replace it with a new version. :)

Any ideas how I can make the windows created by user button taps not persist? Or, that failing, at least close all the windows in a WindowGroup programmatically?


Solution

  • I know now, why on my Mac the windows were closed: In the system settings, I deactivated "restore windows when quitting and re-opening apps". After enabling it, I had the same behaviour.

    So you have two options: You can disable this setting with:

    defaults write [application identifier] NSQuitAlwaysKeepsWindows -bool false
    

    or you can use this code:

    class AppDelegate: NSObject, NSApplicationDelegate {
        
        func applicationDidFinishLaunching(_ notification: Notification) {
            UserDefaults.standard.setValue(false, forKey: "NSQuitAlwaysKeepsWindows")
        }
    }
    
    @main
    struct StackOverflowTestApp: App {
            
        @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
        ...
    }
    

    You may need to restart the app a second time to see the effect.

    UPDATE:

    You don't need an AppDelegate, you can just use the init in the App:

    @main
    struct StackOverflowTestApp: App {
    
        init() {
            UserDefaults.standard.setValue(false, forKey: "NSQuitAlwaysKeepsWindows")
        }
        ...
    }