It's a bit rubbish and not very SwiftUI-ish, but I seem to have got something working. I've had to code:
.onReceive(NotificationCenter.default.publisher(for: NSApplication.willTerminateNotification), perform: { _ in }
and
.onReceive(NotificationCenter.default.publisher(for: NSWindow.willCloseNotification)) { output in
guard let window = output.object as? NSWindow else { return }}
The first gets sent to me when the app quits and the second is sent every time a window closes (any window). In the windowWillCloseNotification, I need to check that window.windowNumber matches my current window. Luckily, I have the window number using HostingWindowFinder.
Once in my code, I can code an NSAlert to ask whether the user wants to save their data. Since that's shown modally, the app waits for a response.
For the record, HostingWindowFinder is a useful bit of code I found somewhere in StackOverflow. It returns me a reference to the window that the SwiftUI view is hosted in.
struct HostingWindowFinder: NSViewRepresentable {
var callback: (NSWindow?) -> Void
func makeNSView(context: Self.Context) -> NSView {
let view = NSView()
DispatchQueue.main.async { [weak view] in
self.callback(view?.window)
}
return view
}
func updateNSView(_ nsView: NSView, context: Context) {}
}