SwiftUI sidebar within NSHostedView on macOS doesn't show selections

I have an AppKit app written mostly in Objective-C. I'm trying to convert my NSOutlineView to a SwiftUI OutlineGroup. Right now I'm just using List{} because I can't get OutlineGroup to work with CoreData.

But when I display my list of items in the sidebar, I can't select any items. I can click on them of course, but the row selection highlighting doesn't work at all.

I think maybe this might have something to do with the fact that my SwiftUI view is embedded within an NSHostingView.

I've setup my NSHostingView like this:

Code Block
import Cocoa
import SwiftUI
@objc class FormsListHostingController: NSViewController {
public var viewModel : FormsListUIView.ViewModel?
var formsListView : FormsListUIView?
@objc public var databaseDocument : TFDatabaseDocument?
override func viewDidLoad() {
        super.viewDidLoad()
var hostingView : NSHostingView<FormsListUIView>?
if let context = self.databaseDocument?.dataAccessManager?.persistentContainer.viewContext {
let viewModel = FormsListUIView.ViewModel(managedObjectContext: context)
let contentView : FormsListUIView = FormsListUIView(viewModel: viewModel)
hostingView = NSHostingView(rootView: contentView)
self.view = hostingView!
}
}
}


and my SwiftUI view is setup like this:

Code Block
struct FormsListUIView: View {
@Environment(\.managedObjectContext) var context
@ObservedObject var viewModel: ViewModel
@State private var selection: CDForm? = nil
@State private var isSelected : Bool = false
var body: some View {
List(selection: self.$selection) {
ForEach(viewModel.categories, id: \.uuid) { (category : CDCategory) in
Section(header: Text(category.name ?? "").font(.headline)) {
ForEach(Array(category.forms as! Set<CDForm>), id: \.uuid) { (form : CDForm) in
FormRow(form: form).onTapGesture {
self.selection = form
isSelected.toggle()
}
}
}
}
}
.listStyle(SidebarListStyle())
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}


Any ideas why the row selection doesn't show? I'm suspecting it's a problem with showing a SwiftUI view within an NSHostingView.

Replies

This is due to a mismatch in the tags of the rows compared to your selection binding. The (implicit) tags are the uuids of your CDForms, while the selection is to an entire CDForm, and because those don't match the List doesn't know which row is associated with that selection.

You could follow two different approaches:

  1. Add an explicit tag to your FormRow to describe it should be the entire CDForm: .tag(form)
  2. Change selection to be relative to the identity of the form: @State private var selection: UUID? = nil

I've confirmed in a test project based on the above that either of those do work as expected. The latter may be preferable in the general case to avoid making extra copies of the form structures and instead reference the minimal id incase of mutations to the forms.

Also note that the onTapGesture is not necessary since the List itself will update its selection on click of the entire row, again based on the tag of the row and selection matching.