KelaKing
KelaKing

Reputation: 146

UITableViewDiffableDataSource Invalid sections

I try to use UITableViewDiffableDataSource

class SecondViewController: UIViewController {

    enum Section {
        case main
    }

    struct Model: Hashable {
        let title: String
    }

    var tableView: UITableView!
    var dataSource: UITableViewDiffableDataSource<Section, Model>!

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView = UITableView(frame: view.bounds)
        view.addSubview(tableView)
        tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")

        view.layoutIfNeeded()

        dataSource = UITableViewDiffableDataSource<Section, Model>(tableView: tableView, cellProvider: { (tableView, indexPath, item) -> UITableViewCell? in
            let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
            cell.textLabel?.text = item.title
            return cell
        })

        var snapshot = NSDiffableDataSourceSnapshot<Section, Model>()
        snapshot.appendSections([.main])
        snapshot.appendItems([Model(title: "1")], toSection: .main)
        dataSource.apply(snapshot)
    }
}

if I use view.layoutIfNeeded() before create dataSource, it will crash:

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of sections. The number of sections contained in the table view after the update (1) must be equal to the number of sections contained in the table view before the update (1), plus or minus the number of sections inserted or deleted (1 inserted, 0 deleted).'

Upvotes: 7

Views: 2380

Answers (3)

wlixcc
wlixcc

Reputation: 1492

Same problem, I found that even without animations, some users in production will still crash。

When I setupUI in viewDidLoad, DispatchQueue.main.async help me fix this problem

    DispatchQueue.main.async { [weak self] in
        guard let self = self else { return }
        var snapshot = NSDiffableDataSourceSnapshot<Section, Row>()
        snapshot.appendSections([...])
        snapshot.appendItems([...], toSection: .main)
        
        dataSource.apply(snapshot, animatingDifferences: true, completion: nil)            
    }

More specifically, just setting animatingDifferences to false will still crash. This crash only happened on iOS 15.0.0 and iOS15.0.1

enter image description here

Upvotes: 0

Steve Landey
Steve Landey

Reputation: 2609

I ran into the same problem. In general, on iOS 14, causing a layout before the table view's data source is set can cause crashes.

You can fix these crashes by setting the data source as soon as the table view has been created. If you want to animate your initial snapshot, then first set an empty snapshot followed by a populated one with animatingDifferences: true.

Upvotes: 0

Michael Tigas
Michael Tigas

Reputation: 199

I encountered the same problem, when populating a UITableView with results from a PassthroughSubject, as soon as the UIViewController was displayed.

Animating updates was very important to me, so my workaround was to first "prime" the DiffableDataSource by creating a snapshot immediately after initialisation, appending the sections I wanted, appending the items with an empty array, and applying the snapshot with animatingDifferences: false.

Afterwards I was able to apply animated snapshots with data with no issue.

Upvotes: 6

Related Questions