SkillAgentSearch skills...

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/ReactiveCollectionsKit

README

ReactiveCollectionsKit CI

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 UICollectionView APIs:

  • reloadData()
  • reconfigureItems(at:)
  • reloadSections(_:)
  • reloadItems(at:)
  • performBatchUpdates(_:completion:)
  • All UICollectionViewDataSource methods
  • All UICollectionViewDelegate methods

Requirements

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

View on GitHub
GitHub Stars190
CategoryDevelopment
Updated15d ago
Forks12

Languages

Swift

Security Score

100/100

Audited on Mar 14, 2026

No findings