jflinter / Dwifft

Swift Diff

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Call to `beginUpdates` should occur before modifying the table's source data

elliottwilliams opened this issue · comments

I've been getting errors from within some unit test code that uses TableViewDiffCalculator. From view controller code that's invoked whenever backing data has changed...

vehicles.producer.startWithNext { vehicles in
    self.diffCalculator.rows = vehicles.sort()
}

...an exception like this is thrown when diffCalculator.rows is set:

failed: caught "NSInternalInconsistencyException", "Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (5) must be equal to the number of rows contained in that section before the update (5), plus or minus the number of rows inserted or deleted from that section (5 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out)."

After looking into this, my understanding is that tableView.beginUpdates() and tableView.endUpdates() require the table's dataSource to be modified in between the two calls, or at least in the same block. This is what Apple's documentation seems to suggest:

At the conclusion of a block—that is, after endUpdates returns—the table view queries its data source and delegate as usual for row and section data. Thus the collection objects backing the table view should be updated to reflect the new or removed rows or sections.

Sure enough, if I surround my self.diffCalculator.rows = ... call with update methods...

vehicles.producer.startWithNext { vehicles in
    self.tableView.beginUpdates()
    self.diffCalculator.rows = vehicles.sort()
    self.tableView.endUpdates()
}

...no exception is thrown, since update methods can be nested, and only the outer set triggers the actual insertions/deletions to the table.

My proposal to fix this would be to move Dwifft's beginUpdates call to a willSet observer, so that the initial number of rows is calculated before rows is changed, or to encourage wrapping rows modifications with update methods, like I did above.

I think this bug is being missed in the Dwifft tests because insertRowsAtIndexPaths and deleteRowsAtIndexPaths are being overridden. What's confusing to me is why I'm noticing this in testing, but not when my application is being run, but regardless, let me know what you think!

This might have something to do with #21 as well, since it too relates to crashes when updating row data.

@elliottwilliams - thanks for the research; would you be interested in fixing it in a PR?