Convert Figma logo to code with AI

RxSwiftCommunity logoRxDataSources

UITableView and UICollectionView Data Sources for RxSwift (sections, animated updates, editing ...)

3,083
494
3,083
85

Top Related Projects

24,358

Reactive Programming in Swift

40,982

Elegant HTTP Networking in Swift

Cocoa framework and Obj-C dynamism bindings for ReactiveSwift.

4,233

A Swift binding framework

The better way to deal with JSON data in Swift.

15,122

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

  1. 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)
  1. Handling item selection:
tableView.rx.itemSelected
    .map { indexPath in
        dataSource[indexPath]
    }
    .subscribe(onNext: { item in
        print("Selected item: \(item)")
    })
    .disposed(by: disposeBag)
  1. 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

  1. Install RxDataSources using CocoaPods, Carthage, or Swift Package Manager.
  2. Import the library in your Swift file:
    import RxSwift
    import RxCocoa
    import RxDataSources
    
  3. 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

24,358

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.

40,982

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.

4,233

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.

15,122

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 Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Travis CI

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 and Equatable, and your section with AnimatableSectionModelType
  • 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 and Delete (Automatic, Fade, ...)
  • Example app
  • Randomized stress tests (example app)
  • Supports editing out of the box (example app)
  • Works with UITableView and UICollectionView

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:
  • 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)

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:
  • define the Item typealias: equal to the type of items that the section will contain
  • declare an items 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
  }
}
  1. 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
})
  1. 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
}
  1. 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: 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.

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.