RxSwift: How to bind a UITableView to a datasource

The purpose here is to setup a simple MVVM architecture to bind ViewModel’s data to its dedicated View which will be a UITableView contained in a UITableViewController. Furthermore, we are binding a UIBarButtonItem’s tap event to the ViewModel which will handle the datasource populating, informing the view that there is a new item to display. Every bindings will be made available through RxSwift.

This how-to is not a RxSwift tutorial and will not cover the keys and principles of Rx. There is a lot of great resources on the web if you’d like to get started with RxSwift, like the following :

That’s it, your Playground should be named ‘RxSwiftPlayground’, open that directory then open the ‘RxSwift.xcworkspace’ file and build the ‘RxSwiftPlayground’ scheme. You are now ready to get into this how-to!

Playground setup

Open the ‘RxSwift.playground’ then remove its content. We’ll need some imports to get started. Add these imports to the top of the Playground file.

Let’s define some properties, first a UIBarButtonItem with .add SystemItem which represents a ‘+’ sign. It will be the trigger button to add items to our UITableView. Second, is the cell identifier used by our UITableView to register and reuse UITableViewCell.

Inside the UITableViewController‘s viewDidLoad method we are calling two basic setup methods, one to add the addButton to the UITableViewController‘s UINavigationBar, the other to setup the UITableViewController‘s UITableView

The addButton is added to the UINavigationBar right part

We are setting up the UITableView, by removing UITableView‘s delegate and dataSource to let RxSwift works by handling it itself, then we are registering UITableViewCell type with the previously defined cellIdentifier.

Playground’s live view setup

In order to make the TableViewController visible in the Xcode Assistant Editor, we need to create a TableViewController instance embedded in a UINavigationController, then resize the UINavigationController to a real iPhone dimension.

Then just add the newly created UINavigationController.view to the current Playground’s live view.

PlaygroundPage.current.liveView = navigationController.view

The last step is to make the live preview visible, just click on View > Assistant Editor > Show Assistant Editor in the Xcode top menu. You should see the following render in your Assistant Editor

ViewModel setup

The ViewModel’s purpose here is to provide a dataSource of String items and add an item to it wherever the plus button is tapped. To achieve this, we’ll need to declare a privateDataSource property of type Variable<[String]> in the ViewModel. The Variable type is a simple container that can be observed. Here we declare a container of type: Array of String.

Then we’ll need to declare the exposed dataSource which is an Observable<[String]>, this is the actual dataSource the View will bind to.

We finally need to declare a DisposeBag property. A DisposeBag is a bag within which we will add our Disposables. This bag will handle their lifecycle. On DisposeBag deinit, every Disposable get disposed to ensure that resources are properly released when not needed anymore. In our case, the DisposeBag deinit will occur on ViewModel deinit.

Last requirement is to implement the ViewModel’s init method which takes one parameter of type Driver<Void> and represents the addButton tapped event sequence. On every tap, we’ll need to append a String item to the privateDataSource Variable through its .value property. The .value property here is an Array of String.

The Driver type is a perfect fit for UI interactions since it never fails and it delivers events on the MainThread.

The job is done for the privateDataSource, we just need to expose the public dataSource as an Observable of the privateDatasource.

init(addItemTap: Driver<Void>) {
// Make the output dataSource an Observable of the privateDataSource
self.dataSource = privateDataSource.asObservable()
//Register addButton tap to append a new "Item" to the dataSource on each tap -> onNext
addItemTap.drive(onNext: { [unowned self] _ in
self.privateDataSource.value.append("Item")
})
.addDisposableTo(disposeBag)
}

⚠️ Note: [unowned self] is used to bring self to the closure unretained to prevent retain cycle also known as memory leak.

We now have a View and a ViewModel ready to use, let’s bind them!

View and ViewModel binding

In order to make the View and the ViewModel working together, we’ll need to bind them.

Finally we need to tell the UITableView that every dataSource item must be represented by a UITableViewCell dequeued from the previously defined cellIdentifier on which we are setting a text value equals to the item value concatenated with the current row number.

You can now tap on the addButton in the UINavigationBar and see items being added to the UITableView.

Conclusion

We’ve quickly seen how ViewModels and Views are acting together in a MVVM architecture powered by RxSwift through event/data stream in a declarative way. This is achieved without implementing any of the UITableViewDelegate and UITableViewDataSource dedicated methods.