RxDataSources
UITableView and UICollectionView Data Sources for RxSwift (sections, animated updates, editing ...)
Top Related Projects
Reactive Programming in Swift
Elegant HTTP Networking in Swift
Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.
A Swift binding framework
The better way to deal with JSON data in Swift.
Network abstraction layer written in Swift.
Quick Overview
RxDataSources is a Swift library that extends RxSwift to provide a set of reactive data sources for UITableView and UICollectionView. It simplifies the process of binding observable sequences to table and collection views, handling animations, and managing complex data structures.
Pros
- Seamless integration with RxSwift, allowing for reactive programming paradigms in UI development
- Supports automatic diffing and animations for data changes
- Handles complex data structures with nested sections and items
- Provides a clean, declarative API for managing table and collection views
Cons
- Steep learning curve for developers new to reactive programming
- Requires understanding of RxSwift concepts and operators
- May introduce unnecessary complexity for simple use cases
- Performance overhead for very large datasets or frequent updates
Code Examples
- Setting up a basic table view data source:
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(
configureCell: { dataSource, tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = "Item \(item)"
return cell
}
)
Observable.just([
SectionModel(model: "First section", items: [1, 2, 3]),
SectionModel(model: "Second section", items: [4, 5, 6])
])
.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
- Handling item selection:
tableView.rx.itemSelected
.map { indexPath in
dataSource[indexPath]
}
.subscribe(onNext: { item in
print("Selected item: \(item)")
})
.disposed(by: disposeBag)
- Using animated data source for collection view:
let dataSource = RxCollectionViewSectionedAnimatedDataSource<AnimatableSectionModel<String, Int>>(
configureCell: { dataSource, collectionView, indexPath, item in
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = "Item \(item)"
return cell
}
)
Observable.of([
AnimatableSectionModel(model: "First", items: [1, 2, 3]),
AnimatableSectionModel(model: "Second", items: [4, 5, 6])
])
.bind(to: collectionView.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
Getting Started
- Install RxDataSources using CocoaPods, Carthage, or Swift Package Manager.
- Import the library in your Swift file:
import RxSwift import RxCocoa import RxDataSources
- Create a data source and bind it to your table or collection view:
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>( configureCell: { dataSource, tableView, indexPath, item in let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = item return cell } ) viewModel.sections .bind(to: tableView.rx.items(dataSource: dataSource)) .disposed(by: disposeBag)
Competitor Comparisons
Reactive Programming in Swift
Pros of RxSwift
- Comprehensive reactive programming framework for Swift
- Extensive documentation and community support
- Supports a wide range of reactive operators and patterns
Cons of RxSwift
- Steeper learning curve for developers new to reactive programming
- Can lead to complex code if not used judiciously
- Larger codebase and potential performance overhead
Code Comparison
RxSwift:
Observable.from([1, 2, 3, 4, 5])
.filter { $0 % 2 == 0 }
.map { $0 * 2 }
.subscribe(onNext: { print($0) })
RxDataSources:
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(
configureCell: { (_, tableView, indexPath, item) in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(item)"
return cell
}
)
RxSwift provides a comprehensive reactive programming framework, while RxDataSources focuses specifically on simplifying table and collection view data sources using RxSwift. RxSwift offers more flexibility and a wider range of reactive programming capabilities, but RxDataSources excels in its specialized use case, providing a more streamlined approach to working with table and collection views in a reactive manner.
Elegant HTTP Networking in Swift
Pros of Alamofire
- Broader scope: Handles various networking tasks beyond data sources
- Extensive documentation and community support
- Simpler setup for basic networking operations
Cons of Alamofire
- Not specifically designed for reactive programming
- Lacks built-in support for table and collection view data sources
- May require additional libraries for advanced reactive patterns
Code Comparison
RxDataSources:
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(
configureCell: { (_, tv, indexPath, element) in
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(element)"
return cell
}
)
Alamofire:
AF.request("https://api.example.com/data").responseJSON { response in
switch response.result {
case .success(let value):
print("JSON: \(value)")
case .failure(let error):
print("Error: \(error)")
}
}
RxDataSources focuses on providing reactive data sources for UITableView and UICollectionView, making it easier to handle complex data structures in a reactive way. Alamofire, on the other hand, is a more general-purpose networking library that simplifies HTTP requests and responses. While Alamofire is widely used and well-documented, it may require additional setup to integrate with reactive programming paradigms. RxDataSources is more specialized but offers a smoother experience for reactive table and collection view management.
Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.
Pros of ReactiveCocoa
- More comprehensive framework, offering a wider range of reactive programming features
- Supports both Objective-C and Swift, providing better compatibility for older projects
- Has a longer history and more established community support
Cons of ReactiveCocoa
- Steeper learning curve due to its more complex architecture
- Heavier framework, which may impact app size and performance
- Less focused on specific use cases like data sources for collection and table views
Code Comparison
ReactiveCocoa:
let searchResults = searchString
.flatMap(.latest) { (query: String) -> SignalProducer<[SearchResult], NoError> in
return API.search(query)
}
.observe(on: UIScheduler())
RxDataSources:
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(
configureCell: { (_, tv, indexPath, element) in
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(element) @ row \(indexPath.row)"
return cell
}
)
ReactiveCocoa provides a more general-purpose reactive programming approach, while RxDataSources focuses specifically on simplifying data source management for UITableView and UICollectionView. RxDataSources offers a more streamlined solution for handling sectioned data in these views, making it easier to work with complex data structures in table and collection views.
A Swift binding framework
Pros of Bond
- More comprehensive framework, offering a wider range of reactive programming features beyond just data sources
- Provides a declarative approach to UI binding, which can lead to cleaner and more maintainable code
- Includes built-in support for KVO and custom bindings
Cons of Bond
- Steeper learning curve due to its broader scope and more complex API
- May introduce unnecessary overhead for projects that only need data source functionality
- Less focused on table and collection view data sources compared to RxDataSources
Code Comparison
Bond:
let items = Observable<[String]>([])
items.bind(to: tableView) { dataSource, indexPath, string in
let cell = dataSource.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = string
return cell
}
RxDataSources:
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, String>>(
configureCell: { dataSource, tableView, indexPath, item in
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = item
return cell
}
)
Both libraries provide ways to bind data to table views, but Bond offers a more declarative syntax, while RxDataSources focuses specifically on sectioned data sources with a more explicit configuration.
The better way to deal with JSON data in Swift.
Pros of SwiftyJSON
- Simpler and more focused: SwiftyJSON is specifically designed for JSON parsing, making it easier to use for this specific task
- Lightweight: It has fewer dependencies and a smaller codebase, which can lead to faster compilation times
- More intuitive syntax: SwiftyJSON offers a straightforward way to access JSON data, which can be easier for beginners to understand
Cons of SwiftyJSON
- Limited scope: Unlike RxDataSources, SwiftyJSON doesn't provide advanced features for table and collection views
- No reactive programming support: SwiftyJSON doesn't integrate with RxSwift or other reactive frameworks out of the box
- Less flexibility: It may not be as suitable for complex data transformations or advanced use cases compared to RxDataSources
Code Comparison
SwiftyJSON:
let json = JSON(data: dataFromNetworking)
if let name = json["name"].string {
// Do something with name
}
RxDataSources:
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(
configureCell: { (_, tv, indexPath, element) in
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(element)"
return cell
}
)
While SwiftyJSON focuses on JSON parsing, RxDataSources provides a more comprehensive solution for working with table and collection views in a reactive programming context. The choice between the two depends on the specific requirements of your project and your preferred programming paradigm.
Network abstraction layer written in Swift.
Pros of Moya
- Abstracts network layer, simplifying API interactions
- Supports stubbing for easy testing
- Provides type-safe network requests
Cons of Moya
- Steeper learning curve for beginners
- May be overkill for simple projects
- Requires additional setup compared to basic URLSession
Code Comparison
Moya:
let provider = MoyaProvider<MyAPI>()
provider.request(.userProfile) { result in
switch result {
case let .success(response):
let data = response.data
// Handle data
case let .failure(error):
// Handle error
}
}
RxDataSources:
let dataSource = RxTableViewSectionedReloadDataSource<SectionModel<String, Int>>(
configureCell: { (_, tv, indexPath, element) in
let cell = tv.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = "\(element)"
return cell
}
)
While Moya focuses on network abstraction, RxDataSources specializes in table and collection view data management. Moya simplifies API interactions, while RxDataSources streamlines UI data binding. The choice between them depends on your project's specific needs: network abstraction (Moya) or efficient data source management (RxDataSources).
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Table and Collection view data sources
Features
- O(N) algorithm for calculating differences
- the algorithm has the assumption that all sections and items are unique so there is no ambiguity
- in case there is ambiguity, fallbacks automagically on non animated refresh
- it applies additional heuristics to send the least number of commands to sectioned view
- even though the running time is linear, preferred number of sent commands is usually a lot less than linear
- it is preferred (and possible) to cap the number of changes to some small number, and in case the number of changes grows towards linear, just do normal reload
- Supports extending your item and section structures
- just extend your item with
IdentifiableType
andEquatable
, and your section withAnimatableSectionModelType
- just extend your item with
- Supports all combinations of two level hierarchical animations for both sections and items
- Section animations: Insert, Delete, Move
- Item animations: Insert, Delete, Move, Reload (if old value is not equal to new value)
- Configurable animation types for
Insert
,Reload
andDelete
(Automatic, Fade, ...) - Example app
- Randomized stress tests (example app)
- Supports editing out of the box (example app)
- Works with
UITableView
andUICollectionView
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:
- Turn your data into an Observable sequence
- Bind the data to the tableView/collectionView using one of:
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)
How
Given the following custom data structure:
struct CustomData {
var anInt: Int
var aString: String
var aCGPoint: CGPoint
}
- Start by defining your sections with a struct that conforms to the
SectionModelType
protocol:
- define the
Item
typealias: equal to the type of items that the section will contain - declare an
items
property: of type array ofItem
struct SectionOfCustomData {
var header: String
var items: [Item]
}
extension SectionOfCustomData: SectionModelType {
typealias Item = CustomData
init(original: SectionOfCustomData, items: [Item]) {
self = original
self.items = items
}
}
- 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
})
- Customize closures on the dataSource as needed:
titleForHeaderInSection
titleForFooterInSection
- etc
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
}
- 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:
- SectionOfCustomData needs to conform to
AnimatableSectionModelType
- Your data model must conform to
IdentifiableType
: Theidentity
provided by theIdentifiableType
protocol must be an immutable identifier representing an instance of the model. For example, in case of aCar
model, you might want to use the car'splateNumber
as its identity.Equatable
: Conforming toEquatable
helpsRxDataSources
determine which cells have changed so it can animate only these specific cells. Meaning, changing any of theCar
model's properties will trigger an animated reload of that cell.
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.
Top Related Projects
Reactive Programming in Swift
Elegant HTTP Networking in Swift
Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.
A Swift binding framework
The better way to deal with JSON data in Swift.
Network abstraction layer written in Swift.
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot