NSHostingView Not Working With Swift 6.0

I recently compiled my macOS App with Swift 6 in Xcode 16 (was using Swift 5 previously) and noticed that AppKit Integration doesn't appear to be working as before. All my instances of NSHostingView which allow me to add a SwiftUI View to an AppKit NSWindow view controller no longer respond to mouse input anymore. All my NSHostingView instances display but refuse to accept any user interaction. Has anyone else noticed this and is there a workaround to get NSHostingView to once again be able to accept user/mouse events with Swift 6?

Answered by GreenChess in 796354022

Here is the solution I found. When using NSHostingView to wrap a SwiftUI View to use with AppKit, don't set the SwiftUI View x and y position parameters within the SwiftUI View itself. If you do, the NSHostingView will visually render properly but the NSEvent handling area for the NSHostingView will be offset and it won't respond to mouse events (it did work fine back in macOS 14). Only set the width and height parameters in the SwiftUI View and then when you have the resulting NSHostingView of that, you can set the NSHostingView frame parameter to properly place it on an NSWindow. Presumably this is an API bug. NSHostingView is supposed to do the correct coordinate transform on the SwiftUI View to make it into a usable NSHostingView but this is no longer the case with macOS 15. Use this solution if you have the same error because it took me days to find this solution.

I did some more testing by adding a regular NSView to the NSViewController for an NSWindow along with an NSHostingView (treated by AppKit Integration as an NSView). What I found is that the regular instances of NSView() accept user interaction but the NSHostingView gets ignored even though it is a return type of NSView. Somehow between Swift 5 and 6, NSHostingView is only partially usable now. You can use it to render images to an NSWindow but can't interact with it any longer. Is there something different in the NSHostingView return type of NSView that somehow got reconfigured in the background so as to prevent user interaction in Swift 6? NSHostingView functionally in Swift 5 somehow got degraded when switching to Swift 6.

I just updated to macOS 15 Beta 3 and found something interesting. My App (pre macOS 15) from the AppStore worked fine yesterday with macOS 15 Beta 2 but now no longer does with macOS 15 Beta 3. Somehow going from Beta 2 to Beta 3 breaks NSHostingView for at least pre macOS 15 compiled apps. So I suspect it may be something with the OS and switched back to Swift 5. Essentially it looks like the NSHostingView breakage may not have anything to do with Swift 6 but something new in the OS that doesn't allow NSHostingView instances to accept user interaction anymore. Anyone know what might have possibly caused this? NSHostingView certainly was useful to those of us slowly transitioning to SwiftUI from AppKit and it would be unfortunate to have done all that transition work only to find out that NSHostingView is only partially usable in macOS 15. So I am wondering if someone has a workaround to nudge NSHostingView to work in the new macOS 15. In the meantime, I will keep trying to get NSHostingView to work in macOS 15 and if I find a viable workaround, I will post the solution here.

Update: I spent the entire weekend trying to get NSHostingView on macOS 15 and Xcode 16 to accept user interaction but nothing worked. The only thing I found is that if you put an NSHostingView into the NSTitlebarAccessoryViewController for an NSWindow, the NSHostingView will accept user interaction in the title bar but the NSViewController for the NSWindow won't accept any user interaction for an NSHostingView instance. There must be some difference between the NSTitlebarAccessoryViewController and the NSViewController for the NSWindow that I haven't deciphered yet. Anyone know why NSWindow won't allow an NSHostingView instance to receive user interaction in the main view controller? There must be something inherent in NSTitlebarAccessoryViewController that allows it to fully function with NSHostingView but only in there and I am trying to find out why.

I was reading more on AppKit Integration and for an NSHostingView it says "The hosting view also coordinates event delivery." But that doesn't appear to be the case in macOS 15 Beta 3. Adding an NSView to a NSViewController View does event handling just fine but somehow an NSHostingView derived NSView isn't coordinating event delivery correctly. If I can intercept those user events somehow, I could probably try to find a workaround for an NSHostingView derived NSView so I could pipe those events into it and have it behave like NSHostingView used to behave in macOS 14.

Update: I still haven't had any luck trying to get NSHostingView to respond to mouse events. I am still trying to find out what mechanism is preventing NSHostingView on macOS 15 from accepting any mouse input. I have acceptsFirstResponder set to true and that doesn't work either. Is there any other parameter or function I need to insert to get NSHostingView to do event handling or even to respond to a simple mouse click?

Accepted Answer

Here is the solution I found. When using NSHostingView to wrap a SwiftUI View to use with AppKit, don't set the SwiftUI View x and y position parameters within the SwiftUI View itself. If you do, the NSHostingView will visually render properly but the NSEvent handling area for the NSHostingView will be offset and it won't respond to mouse events (it did work fine back in macOS 14). Only set the width and height parameters in the SwiftUI View and then when you have the resulting NSHostingView of that, you can set the NSHostingView frame parameter to properly place it on an NSWindow. Presumably this is an API bug. NSHostingView is supposed to do the correct coordinate transform on the SwiftUI View to make it into a usable NSHostingView but this is no longer the case with macOS 15. Use this solution if you have the same error because it took me days to find this solution.

having the same issue on swift 6 macos 15.1 xcode 16.0 beta4

macOS 15.1. In an example app I wrap a SwiftUI Button and an NSButton in a NSView container. When showing the container from a simple NSViewRepresentable, both work. But when I show the container in an overlay of a PDFView using PDFPageOverlayViewProvider, just the SwiftUI button does not work. I set no offsets. Both Buttons work on iOS (using UI... variants). A possible cause might be that is that text selection mode is active on Mac by default. But why would this just stop the SwiftUI button and not the NSButton from working?

NSHostingView Not Working With Swift 6.0
 
 
Q