NSTableViewDiffableDataSource bug? table view delegate method not called

Summary

NSTableViewDiffableDataSource does not trigger the tableView(_:rowViewForRow:) delegate method when used, unlike traditional data source implementations. This behavior is due to the diffable data source’s internal handling of row views.

Root Cause

  • Diffable data source manages row views internally, bypassing the need to call tableView(_:rowViewForRow:).
  • The method is not invoked because the diffable data source assumes default row view behavior.

Why This Happens in Real Systems

  • Design choice: Diffable data sources prioritize simplicity and performance by managing row views internally.
  • Assumption of default behavior: It assumes no custom row view configuration is needed unless explicitly overridden.

Real-World Impact

  • Custom row views break: Developers relying on tableView(_:rowViewForRow:) for customization cannot use diffable data sources without a workaround.
  • Code migration issues: Projects transitioning to diffable data sources may encounter unexpected behavior in table view rendering.

Example or Code

class ViewController: NSViewController, NSTableViewDelegate {
    @IBOutlet var tableView: NSTableView!
    var datasource: NSTableViewDiffableDataSource!

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        datasource = NSTableViewDiffableDataSource(tableView: tableView) { tableView, _, row, _ in
            // Cell configuration
        }
        var snapshot = NSDiffableDataSourceSnapshot()
        snapshot.appendSections(["dummy"])
        snapshot.appendItems(["manny", "moe", "jack"])
        datasource.apply(snapshot)
    }

    func tableView(_ tableView: NSTableView, rowViewForRow row: Int) -> NSTableRowView? {
        print("here") // NOT CALLED
        return NSTableRowView()
    }
}

How Senior Engineers Fix It

  • Subclass NSTableRowView: Create a custom row view subclass and register it with the table view.
  • Use tableView(_:viewFor:): Leverage the diffable data source’s cell configuration closure to customize row views indirectly.
  • Report and workaround: File a bug report to Apple and use traditional data sources if custom row views are essential.

Why Juniors Miss It

  • Lack of understanding: Juniors may not realize diffable data sources handle row views differently.
  • Overlooking documentation: Apple’s documentation does not explicitly state that tableView(_:rowViewForRow:) is unused with diffable data sources.
  • Assumption of parity: Assuming diffable data sources behave identically to traditional data sources leads to unexpected behavior.

Leave a Comment