UITableView and UICollectionView Data Sources for RxSwift (sections, animated updates, editing ...)
IdentifiableType
and Equatable
, and your section with AnimatableSectionModelType
Insert
, Reload
and Delete
(Automatic, Fade, …)UITableView
and UICollectionView
Writing table and collection view data sources is tedious. There is a large number of delegate methods that need to be implemented for the simplest case possible.
RxSwift helps alleviate some of the burden with a simple data binding mechanism:
rx.items(dataSource:protocol<RxTableViewDataSourceType, UITableViewDataSource>)
rx.items(cellIdentifier:String)
rx.items(cellIdentifier:String:Cell.Type:_:)
rx.items(_:_:)
let data = Observable<[String]>.just(["first element", "second element", "third element"])
data.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { index, model, cell in
cell.textLabel?.text = model
}
.disposed(by: disposeBag)
This works well with simple data sets but does not handle cases where you need to bind complex data sets with multiples sections, or when you need to perform animations when adding/modifying/deleting items.
These are precisely the use cases that RxDataSources helps solve.
With RxDataSources, it is super easy to just write
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(configureCell: configureCell)
Observable.just([SectionModel(model: "title", items: [1, 2, 3])])
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
Given the following custom data structure:
struct CustomData {
var anInt: Int
var aString: String
var aCGPoint: CGPoint
}
SectionModelType
protocol:Item
typealias: equal to the type of items that the section will containitems
property: of type array of Item
struct SectionOfCustomData {
var header: String
var items: [Item]
}
extension SectionOfCustomData: SectionModelType {
typealias Item = CustomData
init(original: SectionOfCustomData, items: [Item]) {
self = original
self.items = items
}
}
SectionOfCustomData
type:let dataSource = RxTableViewSectionedReloadDataSource<SectionOfCustomData>(
configureCell: { dataSource, tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = "Item \(item.anInt): \(item.aString) - \(item.aCGPoint.x):\(item.aCGPoint.y)"
return cell
})
titleForHeaderInSection
titleForFooterInSection
dataSource.titleForHeaderInSection = { dataSource, index in
return dataSource.sectionModels[index].header
}
dataSource.titleForFooterInSection = { dataSource, index in
return dataSource.sectionModels[index].footer
}
dataSource.canEditRowAtIndexPath = { dataSource, indexPath in
return true
}
dataSource.canMoveRowAtIndexPath = { dataSource, indexPath in
return true
}
let sections = [
SectionOfCustomData(header: "First section", items: [CustomData(anInt: 0, aString: "zero", aCGPoint: CGPoint.zero), CustomData(anInt: 1, aString: "one", aCGPoint: CGPoint(x: 1, y: 1)) ]),
SectionOfCustomData(header: "Second section", items: [CustomData(anInt: 2, aString: "two", aCGPoint: CGPoint(x: 2, y: 2)), CustomData(anInt: 3, aString: "three", aCGPoint: CGPoint(x: 3, y: 3)) ])
]
Observable.just(sections)
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
RxDataSources provides two special data source types that automatically take care of animating changes in the bound data source: RxTableViewSectionedAnimatedDataSource
and RxCollectionViewSectionedAnimatedDataSource
.
To use one of the two animated data sources, you must take a few extra steps on top of those outlined above:
AnimatableSectionModelType
IdentifiableType
: The identity
provided by the IdentifiableType
protocol must be an immutable identifier representing an instance of the model. For example, in case of a Car
model, you might want to use the car’s plateNumber
as its identity.Equatable
: Conforming to Equatable
helps RxDataSources
determine which cells have changed so it can animate only these specific cells. Meaning, changing any of the Car
model’s properties will trigger an animated reload of that cell.Xcode 10.2
Swift 5.0
For Swift 4.x version please use versions 3.0.0 ... 3.1.0
For Swift 3.x version please use versions 1.0 ... 2.0.2
For Swift 2.3 version please use versions 0.1 ... 0.9
We’ll try to keep the API as stable as possible, but breaking API changes can occur.
Podfile
pod 'RxDataSources', '~> 5.0'
Cartfile
github "RxSwiftCommunity/RxDataSources" ~> 5.0
Create a Package.swift
file.
import PackageDescription
let package = Package(
name: "SampleProject",
dependencies: [
.package(url: "https://github.com/RxSwiftCommunity/RxDataSources.git", from: "5.0.0")
]
)
If you are using Xcode 11 or higher, go to File / Swift Packages / Add Package Dependency… and enter package repository URL https://github.com/RxSwiftCommunity/RxDataSources.git, then follow the instructions.