ra1028 / DifferenceKit

💻 A fast and flexible O(n) difference algorithm framework for Swift collection.

Home Page:https://ra1028.github.io/DifferenceKit

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Number of minimal stages that can perform batch updates with no crashes.

iwheelbuy opened this issue · comments

Checklist

Description

Decrease the number of minimal stages to 1 when changeset is applied to a view with single section.

Motivation and Context

When a stage is applied it makes the UICollectionViewLayout to be recalculated. So, we have multiple stages, multiple batch updates and multiple layout recalculations.

It's not an issue when you have regular cells with regular behavior. But, when it comes to layout invalidation on bounds change, the number of stages matter.

I have a layout with some levitating headers and footers. They are hidden/shown whenever you scroll the opposite direction. Also they have their own behavior range.

Whenever layout is recalculated - the content size changes. And since we have deletes before insertions, the content size shrinks first and behavior range of some view also shrinks. But it won't be restored when the last stage inserts are applied, the layout isn't aware of the number of stages. And I believe it shouldn't be...

However, in performBatchUpdates of UITableView, UICollectionView, etc, there are combinations of diffs that cause crash when applied simultaneously.

Since some other frameworks provide a single changeset for 1-dimensional collection, I assumed that it is possible to live without crashes in a single UIKit section. I decided to move from RxDataSources to some other diffing framework because RxDataSources is a 3-staged solution. Also, I made the layout to have a single UIKit section but with multiple virtual sections.

So, I've made a generic solution to test on real data in my app. I've wrapped DifferenceKit, IGListDiffKit, DeepDiff and Swift.CollectionDifference solutions and run them one by one for each view update.

The most common scenario is scrolling down the infinite collection and append more and more data. Usually DifferenceKit is 2x-3x time slower than Swift.CollectionDifference in this scenario. IGListDiffKit and DeepDiff are 5x-6x slower than Swift.CollectionDifference. But there are cases when Swift.CollectionDifference is 10x-20x slower than DifferenceKit and calculation time might be 1-2 seconds while DifferenceKit usually consumes not more than 100ms.

DifferenceKit looks like the most stable solution and I'd love to move forward with it. But it doesn't provide a 1-staged solution for 1-dimensional collection.

Are there any known combinations to crash a view with single section?
Is it possible to provide a 1-staged solution for 1-dimensional collection?

Proposed Solution

I have nothing to propose...

I will also be very happy to see a method to return a guaranteed 1-staged changeset, since if I call performBatchUpdates multi times the animation looks broken.

@iwheelbuy @ruixingchen

Sorry for my late reply.
Regardless of the number of sections, reload with a 1-staged changeset will cause a crash in several cases.
I think crashes are inevitable in many other libraries.
As far as I know, the libraries that can provide stable behavior in every case are DifferenceKit, RxDataSources, and IGListKit.
IGListKit avoids crashes by decomposing reloadItems into deletes and inserts. DifferenceKit and RxDataSources are using the same approach.
This means that it's impossible to prevent crashes and glitches in complex diff animations at the same time, due to the specification of UIKit.

@ra1028 Thank you for your reply. Are there any known combinations to crash a view with single section?

UIKit is a black-boxed framework, so I don't know all the specs, but the most typical case is when it takes move and update at the same time.
Even similar combinations may or may not crash, depending on the other conditions.