Open AddLocationViewController.swift and add an import statement for RxSwift and RxCocoa at the top.
AddLocationViewController.swift
We also need to declare a property, disposeBag
, of type DisposeBag
. As I mentioned in the previous chapter, don’t worry about this if you’re not familiar with RxSwift. The goal is to learn how the Model-View-ViewModel pattern works with bindings. We’re not here to learn RxSwift.
AddLocationViewController.swift
Our next stop is the viewDidLoad()
method of the AddLocationViewController
class. We need to update the initializer of the AddLocationViewViewModel
class. We pass a driver as the only argument of the initializer. Because we imported RxCocoa, we have access to the reactive extensions of UISearchBar
.
AddLocationViewController.swift
A search bar emits a sequence of String
values. We ask it for a reference to that sequence. The orEmpty
operator converts any nil
values to an empty string. The asDriver()
method turns the sequence into a driver. We pass this driver of type String
to the initializer of the AddLocationViewViewModel
class.
AddLocationViewController.swift
We can remove the remaining lines from the viewDidLoad()
method. Instead, we’re going to use bindings to update the user interface if the view model performs a geocoding request and when it receives a response.
AddLocationViewController.swift
We listen for events of the locations
driver of the view model. If a new event is emitted, the table view is reloaded. Because the view model is owned by the view controller, we use an unowned reference to self
within the closure.
AddLocationViewController.swift
To show you how powerful and elegant Rx is, we use the querying
driver of the view model to start and stop animating the activity indicator view.
AddLocationViewController.swift
We use a similar technique to hide the keyboard. When the user taps the search or cancel buttons, we resign the search bar as the first responder.
AddLocationViewController.swift
This means we can remove the implementation of the UISearchBarDelegate
protocol in its entirety. Delegation is a nice pattern, but it feels great every time I can use Rx to replace boilerplate code like this.
We could do the same for the UITableViewDataSource
and UITableViewDelegate
protocols, but I don’t want to overwhelm you too much at this point. Build and run Cloudy to make sure we didn’t break anything during the refactoring operation.
You may be wondering what we gained by introducing the Model-View-ViewModel pattern and the AddLocationViewViewModel
class in the AddLocationViewController
class. Let’s take a look.
The view controller is no longer in charge of forward geocoding. In fact, it doesn’t even know about the Core Location framework. That’s our first accomplishment.
But, more importantly, the view controller no longer manages state. This is thanks to Rx and the Model-View-ViewModel pattern. The less state your application manages the better and this is especially true for view controllers. But what has changed?
The user’s input is directly funneled to the view model. The view model uses the input of the search bar to perform geocoding requests. The results of these geocoding requests are streamed back to the view controller through the locations
driver and the view controller’s table view is updated as a result.
The view model doesn’t keep any state either. In true Rx fashion, it manages two data streams, a stream of arrays with locations and a stream of boolean values that indicate whether a geocoding request is in flight. If you’re new to reactive programming and bindings, then this may take some getting used to. But I hope you agree that the result is a welcome improvement.
We also got rid of the UISearchBarDelegate
protocol implementation. It’s a small win but nevertheless welcome.
We’re not quite done yet. In this book, I promised you that testing becomes easier if you adopt the Model-View-ViewModel pattern. Let’s put that to the test like we did earlier in this book. But, first, we need to deal with an obstacle that’s preventing us from writing good unit tests.