Latest 0.1.0
License MIT
Platforms ios 8.0

Tableau is a library for making your table and collection view setup routine smaller, more declarative, more type safe, and simply more fun. It
includes support for automatically diffing and animating data on your tables, ability for cells to emit custom events, and support for RxSwift.

A quick intro

With Tableau, you ‘bind’ information and functionality like data, cell types (or closures that provide cells), and event handlers to your table on a
per-section basis (or just to the whole table if your table isn’t sectioned).

For a sectioned table, we start by declaring an enum (or struct) that defines your sections and create a ‘binder’ object:

enum Section: TableViewSection {
    case first
    case second
    case third
    case fourth

let binder = SectionedTableViewBinder(tableView: self.tableView, sectionedBy: Section.self)

Then, we start ‘binding chains’ to one or multiple sections, kind of like in a switch statement. Here, we’ll make three ‘binding chains’ to add
different data and handlers to the sections we just declared:

var myModels: [MyModel] = ...

    .bind(headerTitle: "FIRST SECTION")
    .bind(cellType: MyCustomTableViewCell.self, models: { myModels })
    .onDequeue { (row: Int, cell: MyCustomTableViewCell, model: MyModel) in
        // setup the dequeued 'cell' with the 'model'
    .onTapped { (row, cell, model: MyModel) in
        // e.g. go to a detail view controller with the 'model'

binder.onSections(.second, .third)
    .bind(headerTitles: [.second: "SECOND SECTION", .third: "THIRD SECTION"])
    .bind(cellProvider: { (tableView, section, row, model: MyOtherModel) -> UITableViewCell in 
        if ... {
            return tableView.dequeue(MyOtherTableViewCell.self)
        } else {
            return tableView.dequeue(MyCustomTableViewCell.self)
    }, models: { () -> [MyOtherModel]
        .cellHeight { section, row, model in 
            return UITableViewAutomaticDimension 
        .estimatedCellHeight { _, _ in 120 })

    .bind(cellType: MyOtherOtherTableViewCell.self, viewModels: { ... })

Cells are automatically dequeued for given ‘model’ arrays, and bound model and cell types are remembered and passed to other handlers on
the binding chain. By having tables setup using chains like this, you can more clearly describe how your table views work in a way that reads
more like the requirements you receive.

Custom cell events

Figuring out how to pass events like when a button is pressed or text is entered on a cell back up to your view controller is always a hassle,
usually involving conformance to a bunch of different delegate protocols. To solve this, Tableau also gives you the ability for your cells to
declare custom event enums that can be observed on your binding chains. This is done by conforming your cell to the ViewEventEmitting
protocol and giving it a ViewEvent enum, like this:

class MyCustomTableViewCell: UITableViewCell, ViewEventEmitting {
    enum ViewEvent {
        case switchToggled(state: Bool)
        case buttonPressed
        case textEntered(text: String)

    @objc func onSwitchToggled(switch: UISwitch) {
        self.emit(event: .switchToggled(state: switch.isOn)

You can then observe for when cells emit events on a binding chain like this:

    .bind(cellType: MyCustomTableViewCell.self, models: { myModels })
    .onEvent(from: MyCustomTableViewCell.self) { (row, cell, event, model: MyModel) in
        switch event {
        case .switchToggled(let state):
            // update something in the model
        case .buttonPressed:
            // perform some action
        case .textEntered(let text):
            // update something in the model

Diffing and animation

Tableau supports deep diffing and automatic animation of your table when its data changes. Without doing anything special to your models,
Tableau can already determine when (and which) sections update and run a basic count on the changes to apply ‘reload’, ‘insert’, and ‘delete’
animations on sections.

However, if you want it, the library also gives you more powerful and more fine-grained control over diffing by taking into account conformance
to Equatable and CollectionIdentifiable on the models you bind to provide more precise ‘reload’, ‘insert’, and ‘delete’ animations, as
well as for tracking moves – all automatically!

Advanced features

You’re a good developer, though – you look at these samples and think of all the limitations and edge cases you can run into with that (more
than one cell or model type per section, custom header/footer view type, section data not known at compile time, infinite scroll…). Luckily,
Tableau supports many more advanced features that cover cases like these and scales well with the complexity of your table, so you should
never be so limited by the library that you find yourself resorting to the regular UIKit ‘data source/delegate’ routine.

Tableau is fully documented, including tutorials and working code samples (with documented walkthroughs!) in the ‘TableauExample’ Xcode
project in the repository. Available tutorials to get you familiar with Tableau are as follows:


Tableau (will be) available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Tableau'


Aaron Bosnjak (email: [email protected], Twitter: @aaron_bosnjak)

Tableau is open to contributors! If you have a feature idea or a bug fix, feel free to open a pull request. Issues and feature ideas are tracked on
this Trello board.


Tableau uses a fork of Tony Arnold’s super-awesome Differ library to do its diffing work. If you like
Tableau, make sure to give that repo a star as well!


Tableau is available under the MIT license, so do pretty much anything you want with it. As always, see the LICENSE file for more info.

Latest podspec

    "name": "Tableau",
    "version": "0.1.0",
    "summary": "Declarative, type-safe table view binding.",
    "description": "Tableau is a table view binding library that makes setup for table views more declarative, more functional, andnmore type-safe.",
    "homepage": "",
    "license": {
        "type": "MIT",
        "file": "LICENSE"
    "authors": {
        "Aaron Bosnjak": "[email protected]"
    "source": {
        "git": "",
        "tag": "0.1.0"
    "swift_version": "4.2",
    "platforms": {
        "ios": "8.0"
    "default_subspecs": "Core",
    "subspecs": [
            "name": "Core",
            "source_files": [
            "ios": {
                "frameworks": "UIKit"
            "name": "Rx",
            "source_files": "Rx/",
            "ios": {
                "frameworks": "UIKit"
            "dependencies": {
                "Tableau/Core": [],
                "RxSwift": [
                    "~> 4.4"
                "RxCocoa": [
                    "~> 4.4"
            "pod_target_xcconfig": {
                "OTHER_SWIFT_FLAGS": "-DRX_TABLEAU"

Pin It on Pinterest

Share This