ReactiveCollectionsKit
Data-driven, declarative, reactive, diffable collections (and lists!) for iOS. A modern, fast, and flexible library for UICollectionView done right.
Install / Use
/learn @jessesquires/ReactiveCollectionsKitREADME
ReactiveCollectionsKit 
Data-driven, declarative, reactive, diffable collections (and lists!) for iOS. A modern, fast, and flexible library for UICollectionView done right.
About
This library is the culmination of everything I learned from building and maintaining [IGListKit][0], [ReactiveLists][1], and [JSQDataSourcesKit][2]. The 4th time's a charm! 🍀
This library contains a number of improvements, optimizations, and refinements over the aforementioned libraries. I have incorporated what I think are the best ideas and architecture design elements from each of these libraries, while eliminating or improving upon the shortcomings. Importantly, this library uses modern UICollectionView APIs — namely, UICollectionViewDiffableDataSource and UICollectionViewCompositionalLayout, both of which were unavailable when the previous libraries were written. This library has no third-party dependencies and is written in Swift.
What about SwiftUI?
SwiftUI performance is still a significant issue, not to mention all the bugs, missing APIs, and lack of back-porting APIs to older OS versions. SwiftUI still does not provide a proper UICollectionView replacement. Yes, Grid exists but it is nowhere close to a replacement for UICollectionView and UICollectionViewLayout. While SwiftUI's List is pretty good much of the time, both LazyVStack and LazyHStack suffer from severe performance issues when you have large amounts of data.
Main Features
| | Main Features |
---|----------------
🏛️ | Declarative, data-driven architecture with reusable components
🔐 | Immutable, uni-directional data flow
🔀 | Safe from data races with Swift 6 strict concurrency checking
🤖 | Automatic diffing for cells, sections, and supplementary views
🎟️ | Automatic registration and dequeuing for cells and supplementary views
📐 | Automatic self-sizing cells and supplementary views
🔠 | Create collections with mixed data types, powered by protocols and generics
🔎 | Fine-grained control over diffing behavior for your models
🚀 | Sensible defaults via protocol extensions
🛠️ | Extendable API, customizable via protocols
📱 | Simply UICollectionView and UICollectionViewDiffableDataSource at its core
🙅 | Never call apply(_ snapshot:), reloadData(), or performBatchUpdates() again
🙅 | Never call register(_:forCellWithReuseIdentifier:) or dequeueReusableCell(withReuseIdentifier:for:) again
🙅 | Never implement DataSource and Delegate methods again
🏎️ | All Swift and zero third-party dependencies
✅ | Fully unit tested
Notably, this library consolidates and centers on UICollectionView. There is no UITableView support because UICollectionView now has a List Layout that obviates the need for UITableView entirely.
Getting Started
[!TIP]
Check out the extensive example project included in this repo.
Here's a brief example of building a simple, static list from an array of data models.
let models = [/* array of some data models */]
// create cell view models from the data models
let cellViewModels = models.map {
MyCellViewModel($0)
}
// create the sections with cells
let section = SectionViewModel(id: "my_section", cells: cellViewModels)
// create the collection with sections
let collectionViewModel = CollectionViewModel(sections: [section])
// initialize the driver with the view model and other components
let driver = CollectionViewDriver(
view: collectionView,
viewModel: collectionViewModel,
emptyViewProvider: provider,
cellEventCoordinator: coordinator
)
// the collection view is updated and animated automatically
// when the models change, generate a new view model (like above)
let updated = CollectionViewModel(sections: [/* updated items and sections */])
driver.update(viewModel: updated)
[!IMPORTANT]
When using this library, you should avoid calling the following
UICollectionViewAPIs:
reloadData()reconfigureItems(at:)reloadSections(_:)reloadItems(at:)performBatchUpdates(_:completion:)- All
UICollectionViewDataSourcemethods- All
UICollectionViewDelegatemethods
Requirements
- iOS 16.0+
- Swift 6.0+
- Xcode 26.0+
- SwiftLint
Installation
Swift Package Manager
dependencies: [
.package(url: "https://github.com/jessesquires/ReactiveCollectionsKit.git", from: "0.1.0")
]
Alternatively, you can add the package directly via Xcode.
Documentation
You can read the documentation here. Generated with jazzy. Hosted by GitHub Pages.
Documentation is also available on the Swift Package Index.
Notes on library architecture
Below are some high-level notes on architecture and core concepts in this library, along with comparisons to the other libraries I have worked on — [IGListKit][0], [ReactiveLists][1], and [JSQDataSourcesKit][2].
Overview
The main shortcomings of [IGListKit][0] are the lack of expressivity in Objective-C's type system, some boilerplate set up, mutability, and using sections as the base/fundamental component. While it is general-purpose, much of the design is informed by what we needed specifically at Instagram. What IGListKit got right was diffing — in fact, we pioneered that entire idea. The APIs in UIKit came after we released IGListKit and were heavily influenced by what we did.
The main shortcomings of [ReactiveLists][1] are that it uses older UIKit APIs and a custom, third-party diffing library. It maintains entirely separate infrastructure for tables and collections, which duplicates a lot of functionality. There's a TableViewModel and a CollectionViewModel, etc. for use with UITableView and UICollectionView. It is also a bit incomplete as we only implemented what we needed at PlanGrid. It pre-dates the modern collection view APIs for diffing and list layouts. What ReactiveLists got right was a declarative API, using a cell as the base/fundamental component, and uni-directional data flow.
[JSQDataSourcesKit][2] in some sense was always kind of experimental and academic. It doesn't do any diffing and also has separate infrastructure for tables and collections, as it pre-dated those modern collection view APIs. It was primarily concerned with constructing type-safe data sources that eliminated the boilerplate associated with UITableViewDataSource and UICollectionViewDataSource. Ultimately, the generics were too unwieldy. See my post, Deprecating JSQDataSourcesKit, for more details. What JSQDataSourcesKit got right was the idea of using generics to provide type-safety, though it was not executed well.
All of this experience and knowledge has culminated in me writing this library, ReactiveCollectionsKit, which aims to keep all the good ideas and designs from the libraries above, while also addressing their shortcomings. I wrote or maintained all of them, so hopefully I'll get it right this time! :)
Immutability and uni-directional data flow
Details that [ReactiveLists][1] got right are immutability, a declarative API, and uni-directional data flow. With ReactiveLists, you declaratively define your entire collection view model and regenerate it whenever your underlying data model changes.
Meanwhile, [IGListKit][0] is very imperative and mutable. With IGListKit, after you hook-up your IGListAdapter and IGListSectionController objects, you update sections in-place. IGListKit encourages immutable data models but this is not enforceable in Objective-C, nor is it enforced in the API. IGListKit does have uni-directional data flow in some sense, but you provide your data imperatively via IGListAdapterDataSource which also requires you to manually manage a mapping of your data model objects to their corresponding IGListSectionController objects.
ReactiveCollectionsKit improves upon the approach taken by both ReactiveLists and IGListKit, and removes or consolidates the boilerplate required by IGListKit.
The CellViewModel is the fundamental or "atomic" component in the library. It encapsulates all data, configuration, interaction, and registration for a single cell. This is similar to ReactiveLists. In IGListKit, this component corresponds to IGListSectionController. A shortcoming of IGListKit is that the "atomic" component is an entire section of multiple items — a sect
