The new (iOS 13+) diffable datasources are great step forward making UITableView and UICollectionView much easier to use. Except one operation: item reloads.
Modern app architectures store data in some kind of ViewModel or ViewStore (The Composable Architecture, CombineFeedback, and many others), while the UI just observes the data.
Very likely, the ViewModel/ViewStore:
- stores data that should be displayed in table/collection view, often transformed or enriched (this data is usually private to the ViewModel/ViewStore)
- there is an array of section identifiers
- and a dictionary of item identifiers (section identifier is the key)
Thanks to the diffable datasources UITableViewDiffableDataSource and UICollectionViewDiffableDataSource, animated updating the table/collection view is very easy:
- create new snapshot
- set sectionIdentifiers
- set itemIdentifiers
- call reloadItems(_:) on the snapshot
The only troubled step is the last one: Which items have to be reloaded?
DiffableWithReload automates identification of items that require reload, so you do not need to care about reloading. Cells needing reload (and only these) are automatically reloaded (when snapshot is applied).
DiffableWithReload subclasses UITableViewDiffableDataSource and UICollectionViewDiffableDataSource (still as generic classes) so you can use them with your data types.
For basic use, there are available:
TableViewDiffableReloadingDataSource<SectionIdentifierType: Hashable, ItemIdentifierType: Hashable, EquatableCellContent: Equatable>
CollectionViewDiffableReloadingDataSource<SectionIdentifierType: Hashable, ItemIdentifierType: Hashable, EquatableCellContent: Equatable>
For advanced use, when you need (for example) data locking, there are available these subclasses:
TableViewDiffableDelegatingDataSource<SectionIdentifierType: Hashable, ItemIdentifierType: Hashable, Delegate: ReloadingDataSourceDelegate, EquatableCellContent: Equatable>
CollectionViewDiffableDelegatingDataSource<SectionIdentifierType: Hashable, ItemIdentifierType: Hashable, Delegate: ReloadingDataSourceDelegate, EquatableCellContent: Equatable>
// change data in the cars array
cars.changeColorOfAllCars(brand: .volkswagen)
// apply the current snapshot
let snapshot = diffableDataSource.snapshot()
// items for reload are automatically created
diffableDataSource.applyWithItemsReloadIfNeeded(snapshot, animatingDifferences: true)
It is highly recommended to review the example code. There are 3 tabs in the example iOS application:
- First tab (Cars): Elementary example where you can see on 100 lines of code, how it works
- Second tab (Cars and Motorcycles): Close to real-world example, with view model and Combine used for observing the changes.
- Third tab (Motorcycles): Demonstrating use with collection view and more cell reuse identifiers.
- for each cell used in the table/collection view is stored the displayed content
- displayed content is generic type
EquatableCellContent
(conforming toEquatable
) - when snapshot is being applied, the currently displayed cell content is compared to the current data source content, and if these are not equal, cell will be reloaded
- DiffableWithReload temporarily stores the displayed cell content (
EquatableCellContent
) for each used cell in the table/collection view. Again, it can be any type conforming toEquatable
, but there are two handy structs creating equatable content: EncodableContent
creatingData?
value from the specified properties:Data?
is unique identifier of the displayed content and can be easily created from anyEncodable
property of your underlaying data. The provideddata
value is an Optional, becauseencode(to:)
may throw. In such a case the cell is always reloaded.HashableContent
creatingInt
(the hash) value from the specified properties:hashValue
is not so unique identifier of the displayed content, however, may be good enough. Default choice should beEncodableContent
.
To integrate Toast-Swift into your Xcode project using CocoaPods, specify it in your Podfile
:
pod 'DiffableWithReload', '~> 1.0.0'
and in your code use import DiffableWithReload
.
When using Xcode 11 or later, you can install DiffableWithReload
by going to your Project settings > Swift Packages
and add the repository by providing the GitHub URL. Alternatively, you can go to File
> Swift Packages
> Add Package Dependencies...
- Version
1.0.0
requires Swift 5 - Example project uses multiple trailing closures and requires Swift 5.3