RxSwiftCommunity / RxDataSources

UITableView and UICollectionView Data Sources for RxSwift (sections, animated updates, editing ...)
MIT License
3.07k stars 485 forks source link
animation binding collectionview datasource diff rxswift sections tableview unidirectional

Travis CI

Table and Collection view data sources

Features

Why

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: 1) Turn your data into an Observable sequence 2) Bind the data to the tableView/collectionView using one of:

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)

RxDataSources example app

How

Given the following custom data structure:

struct CustomData {
  var anInt: Int
  var aString: String
  var aCGPoint: CGPoint
}

1) Start by defining your sections with a struct that conforms to the SectionModelType protocol:

struct SectionOfCustomData {
  var header: String    
  var items: [Item]
}
extension SectionOfCustomData: SectionModelType {
  typealias Item = CustomData

   init(original: SectionOfCustomData, items: [Item]) {
    self = original
    self.items = items
  }
}

2) Create a dataSource object and pass it your 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
})

3) Customize closures on the dataSource as needed:

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
}

4) Define the actual data as an Observable sequence of CustomData objects and bind it to the tableView

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)

Animated Data Sources

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:

Requirements

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

Installation

We'll try to keep the API as stable as possible, but breaking API changes can occur.

CocoaPods

Podfile

pod 'RxDataSources', '~> 5.0'

Carthage

Cartfile

github "RxSwiftCommunity/RxDataSources" ~> 5.0

Swift Package Manager

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.