How to use a custom NSWindow instance on a WindowGroup in SwiftUI

Viewed 204

I am currently developing a SwiftUI app that I want to be cross-platform across majority of Apple devices (iPhone, watch, iPad, macOS). The application will share a lot of views and logic between iOS, iPadOS, watchOS and macOS.

What I want to do is make the app on macOS a menu bar application. In order to make the experience best for the user, apart from registering a menubar item I wanted to use a custom NSWindow instance that will be attached to a menubar item similarly to a NSPopover but will be able to detach when dragged outside and still maintain all the looks and feels of a normal NSWindow, like traffic light controls etc. It will be similar functionality to Cardhop, Fantastical and Harvest menubar apps.

What I find hard to do is using a custom NSWindow instance in combination with SwiftUI. I am using the latest SwiftUI and Xcode versions and probably can't downgrade, since I am on the new MacBook.

My Xcode version is 13.2.1 and macOS 12.1

What I tried doing to have a complete control over what is happening with the window, is resigning from @main tag on the SwiftUI App struct and moving that to NSApplicationDelegate instance I coded. I removed the SwiftUI App struct from the macOS target and tried adding the @main and @NSApplicationMain to my AppDelegate implementation, but it resulted in the AppDelegate not being run at all. App launched but nothing happened and nothing printed in the console.

I read on the internet that in the previous version it was possible to use the UIKit legacy lifecycle, which in my case could be helping me solve my case. Because I was unable to do it I tried hacking my way out of this situation.

My current solution is to still have the SwiftUI App struct instance as @main and adding @NSApplicationDelegateAdaptor with my AppDelegate implementation where I simply find the window that was started and run close() on it. This is a very hacky solution that I don't like but I can accept it if it is the only solution that will work at this time.

My question is two-fold:

Is it possible to completely resign from using the App struct and going with old-fashioned AppDelegate in the latest SwiftUI version? If it is not, how can I make WindowGroup use a custom extended NSWindow class that will add my functionality of attaching, detaching, arrow pointing to menubar item etc.

Thanks for help in advance!

1 Answers

Actually it is possible to resign from SwiftUI lifecycle still, but it's harder. Since I am not a seasoned Apple developer I didn't know how to do it.

https://sarunw.com/posts/how-to-create-macos-app-without-storyboard/

I found this, moving from Storyboards tutorial helpful. Instead of moving on from Storyboards one has to remove the SwiftUI App from macOS target, skip the things with removing storyboards and just do the rest. The app worked well for me. The key was the main.swift file apparently.

Related