NSTableView full reload works, partial reload doesn't

I'm using an NSTableView (and DifferenceKit, but that's likely irrelevant). This is all programmatic, no Interface Builder at all.

I'd previously implemented only tableView(_:objectValueFor:row) in order to get values into my table. At that point I could apply full and partial reloads and everything worked fine.

Now I've added an implementation of tableView(_:viewFor:row:) in order to format some columns differently, and it's affected reloading. A full reloadData() still works, but a call to reloadData(forRowIndexes:columnIndexes) doesn't call either my datasource or delegate methods; the reload seems to simply disappear.

I also tried removing the datasource method and running only with tableView(_:viewFor:row:) but no dice. A partial reload still doesn't call the delegate method.

Has anyone come across this? Is there a nuance of NSTableView I'm missing?

My (truncated) code:

init() {
    ...
    tableView.dataSource = self
    tableView.delegate = self
    myColumnDefinitions.forEach {
        tableView.addTableColumn($0)
    }
}

func tableView(
    _ tableView: NSTableView,
    objectValueFor tableColumn: NSTableColumn?,
    row: Int
) -> Any? {
    ...
    return someStringProvider(column, row)
}

func tableView(
    _ tableView: NSTableView,
    viewFor tableColumn: NSTableColumn?,
    row: Int
) -> NSView? {
    ...
    let identifier = someDerivedValue(column)
    if let existingView = tableView.makeView(withIdentifier: identifier, owner: self) as? NSTableCellView {
        existingView.textField?.stringValue = someStringProvider(column, row)
        return existingView
    }

    let textField = NSTextField()
    ...
    textField.stringValue = someStringProvider(column, row)

    let view = NSTableCellView()
    view.identifier = identifier
    view.addSubview(textField)
    view.textField = textField

    view.addConstraints([
        ... (pin textField to view)
    ])

    textField.bind(
        .value,
        to: view,
        withKeyPath: "objectValue",
        options: nil
    )

    return view
}

Replies

This turns out to be either a misunderstanding of, or possibly a bug in, the way DifferenceKit interacts with a view-based, rather than objectValue-based, NSTableView.

I was crucially not checking the values with which reloadData(forRowIndexes:columnIndexes) was called, and the columnIndexes passed by DifferenceKit's algorithm were always [0], which was not valid for any visible cells as my first column is hidden, so the methods weren't called.

The useful lesson here is that when a tableview's delegate lacks tableView(_:viewFor:row:), a reload to any column reloads the entire row, but once views are specified, the column values become relevant.