onmyway133 / DeepDiff

🦀Amazingly incredible extraordinary lightning fast diffing in Swift
https://onmyway133.com/apps/
Other
2.05k stars 145 forks source link

'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 (0), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).' #40

Closed sereden closed 4 years ago

sereden commented 5 years ago

Hey @onmyway133

I've faced a crash when using sections in UITableView. I have a regular controller with some array of data

var data: Array<ParticipantData> = []

By design I want to use each item of this array as a section and inner array (items) as rows in this section. Here is ParticipantData:

class ParticipantData {
 let id: Int
 let items: Array<Item>

    class func compareContent(_ a: ParticipantData, _ b: ParticipantData) -> Bool {
       // Compare ids and items
        return a.id == b.id && Comparator.compare(a.items, b.items)
    }

    public var diffId: Int {
        return self.id
    }
}

For calculating difference between collections I'm calling following code:

if (!newData.isEmpty) {
               // Calculate diff between old and new data
                let changes = diff(old: self.data, new: newData)

               // deliver results
              // Neither section: self.data.count nor newData.count works
                self.tableView.reload(changes: changes, section: self.data.count, replacementAnimation: UITableView.RowAnimation.none, updateData: {
                    self.data = newData
                })
} else {
               // Tried a fix with reloading data when it's empty
                self.tableView.reloadData()
}

And code which is responsible for displaying sections and rows:

extension MainController: UITableViewDataSource, UITableViewDelegate {
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
       // Each section has as many rows as it has items 
       return data[section].items.count
    }

    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        cell.textLabel?.text = "\(indexPath.section): \(indexPath.row)"
        return cell
    }

    public func numberOfSections(in tableView: UITableView) -> Int {
        // We have as many sections as items in array
        return data.count
    }

    public func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "Title"
    }
}

As result I have a crash '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 (0), plus or minus the number of sections inserted or deleted (0 inserted, 0 deleted).'

Interesting thing: it's working when there is no numberOfSections method

simonhau commented 5 years ago

@sereden: Have you found a solution by any chance? I'm having a very similar issue.

sereden commented 5 years ago

@simonhau Unfortunately, I have found not elegant solution. I don't think that other developers respect this solution but due to lack of time and a fact that I have 2 months experience in iOS I had to create a work around. I could share my code maybe you can find something interesting. The main idea is do not use section header as it's generally used in UITableView and instead represent your header as an element of the list you would pass to DeepDiff. In addition I've created several abstractions which help inflate views

snoozemoose commented 5 years ago

I think you are using the DeepDiff API wrong. The reload(:) function takes a “section” argument which is the section index you want to reload. But by glancing at your code, it looks like you are setting the number of sections as parameter. You need to reload each section separately. There other threads here that touch this subject, have a look at them.

sereden commented 5 years ago

Yes, you are right. I thought that this is a number of section so library would remove or add them with a proper animation. So what should I do if number of sections change each time?

snoozemoose commented 5 years ago

Handling section changes is one the future improvements but it's not yet in the API.

onmyway133 commented 5 years ago

@snoozemoose thanks for helping out, the section change is your call 😉

snoozemoose commented 5 years ago

Absolutely, I haven't forgotten it and I'm using my own implementation of it (in production). But I'm just swamped at work right now so I haven't cleaned it enough for a PR. But, it will come eventually :-)