SwiftUI WindowGroup disable window persistence

1k Views Asked by At

Apple added new functionality to SwiftUI this year, bringing persistence and multiple windows to our SwiftUI apps. How can we disable window persistence. I'm looking for a windowing system very similar to Xcode, where there's a Welcome window on start, users can open new windows with the content they're looking for, then on the next start of the app only the Welcome window is shown.

The below code achieves all of these goals except the unwanted windows remain

import SwiftUI

@main
struct StackApp: App {
    @Environment(\.openWindow) var openWindow
    
    var body: some Scene {
        Window("Welcome to App", id: "welcome-to-app") {
            VStack {
                Text("Welcome")
                Button(action: {
                    openWindow(id: "app-content")
                }) {
                    Text("Open Content")
                }
            }
        }
        .defaultSize(CGSize(width: 200, height: 200))
        
        WindowGroup(id: "app-content") {
            VStack {
                Text("App Content")
            }
        }
        .defaultSize(CGSize(width: 200, height: 200))
    }
}

Help is much appreciated

2

There are 2 best solutions below

0
On

Here's a quick hack proof-of-concept workaround. Can definitely be cleaned up, but it seems to work in macOS 12.6.1.

Not pretty but if you adopt the SwiftUI app lifecycle there just aren't as many ways too hook in and override the system default behavior (can't override the default NSDocumentController etc).

import SwiftUI

@main
struct TestWindowPersistenceApp: App {
  @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
  
    var body: some Scene {
        DocumentGroup(newDocument: TestWindowPersistenceDocument()) { file in
            ContentView(document: file.$document)
        }
    }
}

class AppDelegate: NSObject, NSApplicationDelegate {

  func applicationWillFinishLaunching(_ notification: Notification) {
    print("did finish launching")
    flushSavedWindowState()
    // trigger open new file or Welcome flow here
  }
  
  func flushSavedWindowState() {
    do {
      let libURL = try FileManager.default.url(for: .libraryDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
      guard let appPersistentStateDirName = Bundle.main.bundleIdentifier?.appending(".savedState") else { print("get bundleID failed"); return }
      let windowsPlistFilePath = libURL.appendingPathComponent("Saved Application State", isDirectory: true)
        .appendingPathComponent(appPersistentStateDirName, isDirectory: true)
        .appendingPathComponent("windows.plist", isDirectory: false)
        .path
      
      print("path to remove: ", windowsPlistFilePath)
      try FileManager.default.removeItem(atPath: windowsPlistFilePath)
    } catch {
      print("exception: \(error)")
    }
  }
}
1
On

Check out my Swift package which should solve this problem. You could use it like this:

@main
struct MyApp: App {

    var body: some Scene {
        WindowGroup(id: "MyWindow") {
            ContentView()
        }
        .register("MyWindow")
        .disableRestoreOnLaunch()
    }

}

It seems to work fine and achieved this by setting the isRestorable property of NSWindow to false. This should disable the default behavior.