I'm writing a multi-platform app in SwiftUI.
It handles 3 different kinds of windows, and as many instances of each as the user wants to open.
I need to do some cleanup when the user closes a window, so I added the following to the top-level View in my View hierarchy (a NavigationStack in this case)
.onReceive(NotificationCenter.default.publisher(for: NSWindow.willCloseNotification)) { newValue in
// Do cleanup
}
My expectation is that the closure for my .onReceive handler would be called when the specific window that contains this specific View object is closed.
If you look up the NSWindow.willCloseNotification in AppKit, it says that it sends the notification to the specific window object that is about to be closed.
However, it seems that in SwiftUI, it gets called on every window when any window is about to be closed.
Why is that, and is there some trick to getting a notification when a specific instance of NSWindow is about to be closed?
I believe that there might be a misunderstanding here of what the documentation is trying to express. From the docs:
"The notification object" here refers to
Notification.object, which is a generic property (general, not in the<T>sense) that describes an object which the notification is relevant to. Some notifications are global and not related to a specific object (in which case it can benil), while others are indicating a change relevant to a specific object, in which case this will be set.In this specific case, the
objectproperty of aNotificationreceived forNSWindow.willCloseNotificationis documented to be the window which will be closing, so that you can distinguish between windows which can close.Notification.objectisn't just relevant information when receiving notifications, though: you can use a specific object as a filter for notifications to receive when subscribing to notifications. BothNotificationCenter.addObserver(forName:object:queue:using:)andNotificationCenter.addObserver(_:selector:name:object:)allow you to pass anobjectparameter in such that only notifications whose.objectmatches the given object are delivered — i.e., in case you only care about receivingNSWindow.willCloseNotificationfor a specific window object, you can pass it in up-front and notifications will only be delivered when.objectmatches.Crucially,
NotificationCenter.publisher(for:object:)also allows you to pass in anobjectto filter for — it just happens to default tonil(which means that no filter is applied). The net effect of subscribing for notifications with no object is that each window which subscribes will receive notifications for any window that closes; i.e., exactly what you're seeing.If you want each view hierarchy to be informed only when its containing window is about to close, you'll need to provide a reference to the containing
NSWindowtopublisher(for:object:):NSHostingController/NSHostingView, the easiest way to do this would be to grab a reference to the presenting window atNSHosting*creation time, then pass that in to your SwiftUI hierarchyApplifecycle then accessing the AppKit world is likely easiest throughNSViewRepresentable— it's possible to inject an emptyNSViewinto your hierarchy whose sole purpose is to provide a reference to itsNSWindow: https://stackoverflow.com/a/63439982/169394