So you want to use an observable to drive your table view data? Awesome! Your life is about to change for the better! No more data sources or delegates necessary: it’s Rx time!

Let’s start where we will end up to see the final call site:

1
2
3
4
5
viewModel.generateOutput().drive(tableView.rx.items(cellIdentifier: "Cell")) { index, repo, cell in

cell.textLabel?.text = repo.title

}.disposed(by: disposeBag)

So it looks a little different from our usual ‘cellForRowAt’ method we would traditionally use, right? That’s okay though. This way we don’t even need to worry about index paths. We’re going to cover the scenario here where we only have one section to deal with, multiple sections and more complex table views will be covered shortly.

Let’s Break This Down

So what will be immediately familiar is the ‘cellIdentifier’ part. We’re used to setting up a cell identifier in a storyboard prototype and dequeueing it when it is scrolled off the screen’s bounds and we simply pass an identifier as a String as always.

You can also see that this closure gives us an indexPath to work with, a repo (the data model) as well as the cell itself. Essentially this method will go to our observable and pass in the model at the necessary indexPath which is awesome – no more ‘out of bounds’ crashes. We can then configure our cell as we’d like and you could work with custom cells if you wanted, there’s another method for that, but this one just assumes you’ll be working with a default UITableViewCell.

Now onto the weird stuff…

When we import RxCocoa, we get a lot of interaction between UIKit for free. Think of RxSwift as separate from the UI side – RxCocoa will handle working with taps, tableviews, gestures and just about anything else with the ‘rx’ namespace. We access the items in the tableview with the ‘items’ property generated for us using RxCocoa. There are a tonne of other cool things you’ll find in there so check it out and experiment.

‘drive’ – what is this? Well, from a semantic point of view it make sense – ‘ Get me the output observable from the view model and make it drive my table view’ – in other words, data-driven UI. This is exactly what we want in most situations. Drive is essentially just a uni-directional bind from data to UI but we get a few things for free: it is done on the Main Scheduler (Rx talk for main thread) and if there is an error it won’t literally break everything!

If there is an error on a subscription it usually terminates the subscription. This would be fine, except that our entire UI depends on that bind and it would just break! So ‘drive’ is a special time-saver that allows us to pass something, just incase there is an error and we’ll look at that code shortly.

Let’s head over to our basic View Model – we’ll create a simple protocol to work with and define our inputs and outputs. We covered this in a previous post.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class PostViewModel: ViewModelling {

private struct Out {
   var postText: Observable
}

typealias Input = Observable

func generateOutput() -> Driver<[PostModel]> {
   return posts.asDriver(onErrorJustReturn: [])
}

private var posts: BehaviorSubject<[PostModel]>

   init(with posts: [PostModel], input: Input) {
      posts = BehaviorSubject<[PostModel]>(value: posts)
   }
}

struct PostModel {
   var title: String
}

We’ve created a basic model with a title for a social media app. Each post will be used for one cell and all it needs is a basic title. The big different here is that we’re using ‘Driver’ and it works exactly the same as other observable except that we get the amazing ‘onErrorJustReturn’ parameter. If there is an error, the bindings will receive an empty array instead of breaking, which is awesome.

Now we’ve managed to hook up our Driver to our table view and made sure it is disposed when needed. Nice work!