4

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

marc-medley
  • 8,931
  • 5
  • 60
  • 66
  • I'm afraid it's not supported as of today. I didn't find working workaround either – Marcin Jan 04 '23 at 22:14
  • @Marcin see my work-around posted below. – Dad Feb 08 '23 at 20:56
  • Does this answer your question? [How do you restrict the macOS windowing management from restoring a specific window?](https://stackoverflow.com/questions/72850662/how-do-you-restrict-the-macos-windowing-management-from-restoring-a-specific-win) – tobygriffin May 12 '23 at 02:09

2 Answers2

0

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)")
    }
  }
}
Dad
  • 6,388
  • 2
  • 28
  • 34
0

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.

Tweety
  • 11
  • 1
  • 2
  • If I am correct this will also prevent window from remembering its size and position. If so you should definitely mention that. – Ivan Ičin Jan 23 '23 at 18:33