Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access NSWindow from @main App using only SwiftUI?

At this answer the solution work for Scene plus swiftUI.

However using @main like:

@main
struct MyApp: App {
    @StateObject private var model = MyModel()
    
    var body: some Scene {
        WindowGroup {
            Router {
                AppContent()
            }.environmentObject(self.model)
        }
    }
}

I also tried to get the main window by using

var window: NSWindow? {
        let window = NSApplication.shared.mainWindow
        return window
    }

Nevertheless, the mainWindow always return nil

Update:

I need the NSWindow due to the need of conforming with ASWebAuthenticationPresentationContextProviding which obligates to return a NSWindow. Basically, I'm trying to do something like:

LoginView(store: AuthStore(window: window))

Where AuthStore uses the AuthenticationServices to perform an authentication.

like image 314
Amadeu Cavalcante Filho Avatar asked Aug 16 '20 02:08

Amadeu Cavalcante Filho


Video Answer


2 Answers

Basically, I'm trying to do something like:

LoginView(store: AuthStore(window: window))

Here is a demo of possible approach (with some replicated entities)

demo

class AuthStore {
    var window: NSWindow

    init(window: NSWindow) {
        self.window = window
    }
}

struct DemoWindowAccessor: View {
    @State private var window: NSWindow?   // << detected in run-time so optional
    var body: some View {
        VStack {
            if nil != window {
                LoginView(store: AuthStore(window: window!))    // << usage
            }
        }.background(WindowAccessor(window: $window))
    }
}

struct WindowAccessor: NSViewRepresentable {
    @Binding var window: NSWindow?

    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        DispatchQueue.main.async {
            self.window = view.window   // << right after inserted in window
        }
        return view
    }

    func updateNSView(_ nsView: NSView, context: Context) {}
}

struct LoginView: View {
    let store: AuthStore

    var body: some View {
        Text("LoginView with Window: \(store.window)")
    }
}

backup

like image 151
Asperi Avatar answered Sep 22 '22 07:09

Asperi


Many ways but there is a gotcha read complete answer

NSApplication.shared.keyWindow, NSApp.keyWindow, 
NSApp.mainWindow, 

Sometimes, these can return nil especially during launch or if app is inactive

I think it is because setting these properties may not be instantaneous from the Appkit side of things

Best is to access all windows

     NSApp.windows.first

This will return all the windows but in an unpredictable order

If you have multiple windows you can do further filtering to find the required window but this method avoids that crazy behaviour where the other methods return nil

like image 38
arthas Avatar answered Sep 22 '22 07:09

arthas