Prevent main AppKit app window from opening when loading a SwiftUI preview

425 views Asked by At

I have a Mac App that's mostly written in AppKit. Over time, I'm adding new views in SwiftUI, and all the while I've been using SwiftUI previews to preview both my new SwiftUI views, and my old AppKit views/view controllers (see Previews are not just for SwiftUI views).

One nuisance I've been running into is that starting a SwiftUI preview opens my app's main window. This is a consequence of how previews go through the regular startup flow, e.g. calling applicationDidFinishLaunching(_:) on my App Delegate, which is where I instantiate and present my app's main window on start. Naturally, I don't want that to happen if I'm working on a view in a preview.

Here's what my AppDelegate looks like:

@NSApplicationMain
public class AppDelegate: NSObject, NSApplicationDelegate {
    lazy var mainWindowController = MainWindowController()

    public func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Nasty hack :(
        let isInSwiftUIPreview = ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"

        if !isRunningInTest() && !isInSwiftUIPreview {
            mainWindowController.showWindow(self)
        }
    }

    // ...
}

As you can see, I've added special cases for not showing the main window if this code is running in a test or SwiftUI preview. This smells, and suggests to me that I'm doing it wrong.

If fact, Apple mentions this exact issue in Mastering Xcode Previews, at 41:41. They suggest to make your app scene-aware and use a UISceneDelegate to do this work instead. However, that's an iOS/catalyst-only API, and I don't see an obvious alternative for AppKit.

With all that said: What's the right way to open the main window of an AppKit app, so that it doesn't open during tests or SwiftUI previews?

0

There are 0 answers