Open SwiftUI window from inside a Swift class

I have a macOS SwiftUI app with a secondary window that should be opened/displayed if a certain condition occurs. The logic that detects the condition is inside a Swift class.

I see lots of examples where opening a window is done inside another SwiftUI View using something like

struct ContentView: View {
    @Environment(\.openWindow) var openWindow

    var body: some View {
        ...
        Button("Open window") {
            openWindow(id: "secondWindowId")
        }
    }

In my case, the logic that detects the condition that would prompt the opening of secondWindowId is in a Swift class (not a SwiftUI View).

Is there a standard way to open the window identified with "secondWindowId" from inside a Swift class?

Answered by Todd_at_Ennetix in 771808022

I found a solution to programmatically opening a window, but I don't know if it the most elegant. It is also a bit complicated. (I'm sorry, but I forgot the original author on the Internet to give them credit.)

(1) I changed the Window to a WindowGroup.

(2) That allowed me to add the .handlesExternalEvents() modifier. The scene now looks like:

struct NetworkAgentInstallScene: Scene {
    var body: some Scene {
        WindowGroup("Initial Install Agent", id: "installagentwindow") {
            InstallAgentView()
        }
        .defaultSize(width: 400, height: 300)
        .commandsRemoved()
        .handlesExternalEvents(matching: Set(arrayLiteral: Wnd.InstallAgentView.rawValue))
    }
}

(3) Wnd is an enum with the InstallAgentView as one of the types. This approach gives you the opportunity to open several different window types.

enum Wnd: String, CaseIterable {
    case InstallAgentView   = "InstallAgentView"
    case OtherView          = "OtherView"
    
    func open(){
        if let url = URL(string: "MyCompanyMyApp://\(self.rawValue)") {
            os_log("opening \(self.rawValue)")
            NSWorkspace.shared.open(url)
        }
    }
}

(4) I had to register the MyCompanyMyApp as a URL Type in the target's Info tab

(5) Then, in the Swift class, I could call open() on the enum type:

Wnd.InstallAgentView.open()

This allows me to open an appropriate window when my regular Swift code notices that the user needs to handle something that is controlled through the auxiliary window.

The basic way to do this to have the Swift class an ObservableObject, and have a @Published property there. When that property changes, you can observe that change event to do something in the SwiftUI view.

Accepted Answer

I found a solution to programmatically opening a window, but I don't know if it the most elegant. It is also a bit complicated. (I'm sorry, but I forgot the original author on the Internet to give them credit.)

(1) I changed the Window to a WindowGroup.

(2) That allowed me to add the .handlesExternalEvents() modifier. The scene now looks like:

struct NetworkAgentInstallScene: Scene {
    var body: some Scene {
        WindowGroup("Initial Install Agent", id: "installagentwindow") {
            InstallAgentView()
        }
        .defaultSize(width: 400, height: 300)
        .commandsRemoved()
        .handlesExternalEvents(matching: Set(arrayLiteral: Wnd.InstallAgentView.rawValue))
    }
}

(3) Wnd is an enum with the InstallAgentView as one of the types. This approach gives you the opportunity to open several different window types.

enum Wnd: String, CaseIterable {
    case InstallAgentView   = "InstallAgentView"
    case OtherView          = "OtherView"
    
    func open(){
        if let url = URL(string: "MyCompanyMyApp://\(self.rawValue)") {
            os_log("opening \(self.rawValue)")
            NSWorkspace.shared.open(url)
        }
    }
}

(4) I had to register the MyCompanyMyApp as a URL Type in the target's Info tab

(5) Then, in the Swift class, I could call open() on the enum type:

Wnd.InstallAgentView.open()

This allows me to open an appropriate window when my regular Swift code notices that the user needs to handle something that is controlled through the auxiliary window.

I tried this, but I could't figure out how to get the code to open an auxiliary window. I feel this would be a cleaner approach than my solution, but I couldn't figure it out.

Open SwiftUI window from inside a Swift class
 
 
Q