I put together a quick little Swift playground demonstrating how I solved this problem. It involves a helper function which creates the variable for the window, does some setup, then sets the content to a SwiftUI view and passes the variable to that SwiftUI view. This gives the view a reference to the window which contains it, letting it call close() on that window.
import SwiftUI
func showWindow() {
var windowRef:NSWindow
windowRef = NSWindow(
contentRect: NSRect(x: 0, y: 0, width: 100, height: 100),
styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView],
backing: .buffered, defer: false)
windowRef.contentView = NSHostingView(rootView: MyView(myWindow: windowRef))
windowRef.makeKeyAndOrderFront(nil)
}
struct MyView: View {
let myWindow:NSWindow?
var body: some View {
VStack{
Text("This is in a separate window.")
HStack{
Button(action:{
showWindow()
}) {
Text("Open another window")
}
Button(action:{
self.myWindow!.close()
}) {
Text("Close this window")
}
}
}
.padding()
}
}
showWindow()
I make the myWindow variable an optional so you can pass nil in Xcode's previews, like so:
struct MyView_Previews: PreviewProvider {
static var previews: some View {
MyView(myWindow: nil)
}
}
Edited to add: I realized I didn't directly answer the questions posed by the OP: What should I write into the button action? How and where do I call the login function and how will this function change the windows, if the login is successful?
I have an application with a similar pattern (login, then show a different window with data from the server), which is why I had to figure out the code for the playground above. I built a class to represent the connection to the service I am using. That object's initializer can throw if it encounters an error, leaving you with a thrown error and no object.
For the button action, I use code like this (I haven't actually run this exact code, so there may be errors):
Button(action: {
let loginResult = Result {try connectToServer(self.address, username: self.username, password: self.password)}
switch loginResult {
case .failure(let error):
print(error.localizedDescription)
let loginAlert = NSAlert(error: error)
loginAlert.beginSheetModal(for: self.myWindow, completionHandler: {...})
case .success(let serverConnection):
showContentWindow(serverConnection)
self.myWindow.close()
}
}) {
Text("Login")
}
showContentWindow is a helper function like the one in the playground above. It accepts the object representing the API connection, then passes that to the SwiftUI view it uses as the contents of the window just like the one above passes the window to the view inside the window.
You can obviously handle errors in a lot of ways. The code above is close to what I use (though I don't have it sitting in front of me right now), and gives me free localized descriptions of errors like network timeouts. Error handling specifics are well beyond the scope of this, though.
The important thing is on success, it calls the function to open the new window, then calls close on its own window. Nice and simple.