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?

  • 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.

  • 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.

Add a Comment

Accepted Reply

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.

Replies

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.