ReactiveCollectionsKit
Data-driven, declarative, reactive, diffable collections (and lists!) for iOS. A thoughtful and flexible wrapper for UICollectionView done right.
Project Status: WIP ⚠️
⚠️ This is still a Work-In-Progress, but it is close to finished. I started this a few years back, then got busy with other things. I am now returning to the project to get the initial release complete. Hopefully. 🤞
In any case, what’s here now is worth sharing.
About
This library is the culmination of everything I learned from building and maintaining IGListKit, ReactiveLists, and JSQDataSourcesKit. The 4th time’s a charm! 🍀
Improvements over the libraries above include:
- All Swift and zero third-party dependencies
- Generic view models to represent and configure cells
- Mix multiple data types
- Automatic registration for cells and supplementary views
- Automatic diffing for items and sections
- Simply
UICollectionView
andUICollectionViewCompositionalLayout
at its core. - No
UITableView
.UICollectionView
now has a List Layout.
What about SwiftUI?
SwiftUI performance is still a significant issue, not to mention all the bugs and missing APIs. 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, both LazyVStack
and LazyHStack
suffer from severe performance issues when you have large amounts of data.
Main Features
TODO
⚠️ Work-In-Progress ⚠️
Usage
[!TIP]
Check out the extensive example project included in this repo.
Here’s an example of buliding a simple, static list from an array of data models.
class MyViewController: UICollectionViewController, CellEventCoordinator {
var driver: CollectionViewDriver!
override func viewDidLoad() {
super.viewDidLoad()
let models = [/* array of some data models */]
// create cell view models from the data models
let cellViewModels = models.map {
MyCellViewModel($0)
}
// create your sections, and add cells
let section = SectionViewModel(id: "my_section", cells: cellViewModels)
// create the collection with all the sections
let collectionViewModel = CollectionViewModel(sections: [section])
// create your collection view layout
let layout = UICollectionViewCompositionalLayout.list(
using: .init(appearance: .insetGrouped)
)
// initialize the driver will all of the above
self.driver = CollectionViewDriver(
view: self.collectionView,
layout: layout,
viewModel: collectionViewModel,
cellEventCoordinator: self
)
// the collection is updated and animated automatically
// later, you can update the model like so:
let updatedCollectionViewModel = CollectionViewModel(sections: [/* updated items and sections */])
self.driver.viewModel = updatedCollectionViewModel
}
// MARK: CellEventCoordinator
func didSelectCell(viewModel: any CellViewModel) {
// TODO: handle cell selection events
}
}
Requirements
- iOS 15.0+
- Swift 5.9+
- Xcode 15.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.
Additional Resources
- Advances in diffable data sources, WWDC20
- Advances in UICollectionView, WWDC20
- Building High-Performance Lists and Collection Views, Apple Sample Code
- Creating Lists with Collection View, Use Your Loaf
- Getting Started with
UICollectionViewCompositionalLayout
, Lickability - Implementing Modern Collection Views, Apple Sample Code
- Lists in UICollectionView, WWDC20
- Make blazing fast lists and collection views, WWDC21
- Modern cell configuration, WWDC20
- The Case for Lists in UICollectionView, PSPDFKit Blog
Contributing
Interested in making contributions to this project? Please review the guides below.
Also, consider sponsoring this project or buying my apps! ✌️
Credits
Created and maintained by Jesse Squires.
License
Released under the MIT License. See LICENSE
for details.
Copyright © 2019-present Jesse Squires.