Menu & Search

A practical MVVM example in Swift – Part 2 (featuring RxSwift)

May 15, 2016

Welcome to part 2 of the practical MVVM example in Swift. If you missed part 1, make sure to check it out here.

Binding MVVM into something meaningful

We’ve seen the benefits of using an MVVM approach in the first part of this tutorial, but we’ve mostly been displaying data. This time, we’ll do some updating as well.

What to use?

On iOS, the first thing that comes to mind is usually Key-Value Observing (KVO). It does a great job, but we have to write a lot of boilerplate code. And we don’t want that, do we?

For this example, I’ve decided to play with an interesting programming paradigm called Functional reactive programming. If you’re not familiar with it, read this great introduction by @andrestaltz. Then, come back, obviously.

There are a few iOS implementations of FRP, two of the most popular ones are ReactiveCocoa and RxSwift.

In my past Objective-C projects, I’ve used ReactiveCocoa, but since RxSwift is a Swift implementation of Rx, we’ll use it for this project. The implementation itself doesn’t really matter that much, what matters this time is to demonstrate how we can use it together with MVVM to write a short but powerful little demo.

Note: I won’t go into too many details of RxSwift since this post will primarily be about MVVM.

Installation of RxSwift

We’ll follow the RxSwift installation guide for CocoaPods. We create a new Podfile:

And run pod install then open MVVM.xcworkspace.

Making CarViewModel reactive

Obviously, the first step is to make our CarViewModel reactive. The Car model should stay unchanged, since it’s not communicating with the UI directly, remember? It’s just there to hold data.

Since we’ll be working with signals, observers, and observables, we need to make sure we have a way of cleaning up everything, so we don’t have any resources permanently allocated (and causing memory leaks).

The first thing we’ll do to our CarViewModel is import RxSwift and RxCocoa and add a dispose bag.

Now, we’ll tweak the properties of the CarViewModel so they’ll be able to observe and react to changes.  BehaviorSubject is perfect for that.

A sharp eye might notice that we’ve added the kilowattText variable which wasn’t here before. It’s added to make our lives a bit easier once we start doing the bindings themselves.

The only thing remaining is the initializer where we previously just assigned the car variable to the CarViewModel object, but this time we’ll have to do a bit more work (which will pay off in the future).

Here’s the full initializer which I’ll explain in chunks further on:

The first two bits are mostly doing the same – being initialized with the current value and registering for changes so the car variable updates respectively.

The third bit ( titleText) is a bit more trick. It uses the combineLatest function to combine whatever changes the makeText and titleText receive, combine them into one string and assign them to titleText.

The fourth bit uses a feature called map() to calculate horsepower from kilowatts. It tries to parse a number from whatever string the kilowattText subject holds and binds the calculated and decorated horsepower string to horsepowerText.

As you’ve probably noticed, we’re explicitly adding all signals to our disposeBag for the reason stated above.

Making the project compile again

We introduced a totally new concept to our app so if we try to run it, we’ll get a build error in TableViewController on these two lines:

This is because our titleText and horsepowerText are not normal Strings anymore. The good news is that we’ll ditch our TableViewController completely and start off with a new (much shorter) View Controller that’ll contain some RX magic.

ReactiveTableViewController

First, delete the existing TableViewController.swift and the View Controller that’s in our Main.storyboard.

Next, let’s add a new (basic UIViewController) and name it ReactiveTableViewController.

Screen Shot 2016-05-11 at 20.06.58

Since there’s nothing on Main.storyboard, we have to add the corresponding View Controller and set its class to ReactiveTableViewController:

Screen Shot 2016-05-11 at 20.08.34

Note: We’ll be using navigation in the example, so I’ll embed our view controller in a Navigation Controller by selecting it (like on the screenshot) and going to Editor > Embed In > Navigation Controller.

We based our RactiveTableViewController on a UIViewController, so we have to add a table view ourselves. I configured it fullscreen like this:

We have our swift class and our view controller on the storyboard. The dance wouldn’t be complete without linking them together with an IBOutlet. We know how to do that, right? (Hint: View > Assistant Editor > Show Assistant Editor, then Ctrl+drag from the table view to the start of the ReactiveTableViewController class).

Custom table view cell

As one of the readers of Part 1 noted, we didn’t have a specific Car View in that example, so we’ll add one now!

Let’s drag a new UITableViewCell in the newly created table view and add one image and two labels to it:

PS: If you’re comfortable with Auto Layout, you should definitely check out my Auto Layout Fundamentals for iOS!

We’ll also need a new class for our custom cell. Let’s name it CarTableViewCell:

Screen Shot 2016-05-11 at 20.22.07

Next up is setting the class of our prototype cell to CarTableViewCell so that we can do some linking from the storyboard to our CarTableViewCell class:

We know some reactiveness is going to happen, so let’s also add a DisposeBag and one more important thing – a variable holding the CarViewModel object:

The base is set up, so let’s finally look at how we can use RxSwift to make our ReactiveTableViewController well … reactive!

Most boilerplate-less table view controller you’ll ever see

To kick things off in our ReactiveTableViewController, we’ll have to add two things – the DisposeBag for reactiveness and obviously the car data source:

To define the cell size, we add this line in viewDidLoad():

Where’s the reactiveness you might ask?! Check this out (still in viewDidLoad):

If we try to run the app, it won’t work yet, but it should compile if you try ⌘+B.

Half-time recap

To recap what we’ve done so far:

  • Added RxSwift to our project
  • Tweaked  CarViewModel so it now holds Rx-friendly variables
  • Got rid of the old TableViewController
  • Replaced it with a ReactiveTableViewController which is a subclass of UIViewController
  • Add a CarTableViewCell (which is a subclass of UITableViewCell)
  • Linked everything up from the storyboard to code
  • Wrote the most boilerplate-less table view controller ever

The only missing piece of the display-puzzle now is setting up the CarTableViewCell to display actual data in the two labels and the image view.

Reactive table view cell

Our (cleaned up)  CarTableViewCell currently looks like this:

When we assign the carViewModel to a cell, nothing happens. Let’s change that:

If you remember what we did in CarViewModel – we connected titleText and horsepowerText to the Car model so every time they change, those properties will fire off a signal.

In the two interesting lines above, we basically bound those observables to the carTitleLabel and carPowerLabel labels in our cell. And we’re already familiar with the disposable bags, right?

We’re not 100% done with the cell, but we can safely run the project and see that our three cars are properly displayed in the table view. Hooray!

Simulator Screen Shot 11 May 2016 23.23.35

Obviously, we’re missing car photos. A bit boring without them, right?

In our first example, we used the good ol’ async data fetching using NSData(contentsOfUrl:) . But we’re reactive now; we can do much better!

To enable photo loading, we can use NSURLSession with RxSwift support:

⌘+R and here we go! Images are properly loaded, and we’re happy programmers!

Simulator Screen Shot 11 May 2016 23.37.36

What about tests?

You’re right! UI tests should still pass, since the UI looks similar and holds all the information it held in part 1.

Unit tests will not even build because we changed the CarViewModel to hold BehaviorObjects, not Strings.

To make unit tests pass again, we just have to call value() on all BehaviourObjects which displays the current value it holds:

Run the whole test suite with ⌘+U and everything should pass!

Let the updating begin!

Up until now, we’ve basically just repeated what we did in part 1 – set up the MVVM architecture and display data from the cars data source. But it’s much cleaner now. And the best part is still yet to come.

At the begining of the post, I’ve mentioned we’re going to be updating data. To do that, we’ll add a new view controller with three text fields that will let us update the data and see the benefits of using a MVVM approach.

Before that, we should add at least one test to verify that our CarViewModel is updating and calculating everything as we predict.

In this new test we’re verifying that changing the horsepowerText properly calculates kilowatts and assigns the string to the kilowattText variable. Because of our smart map() in the CarViewModel initializer, the values are calculated properly and it even handles a negative kilowatt value which results in 0 HP.

Now that we’re confident our View Model works properly, we can continue with creating a view controller that will let us update data in our cars array.

CarViewController

Time for some UI! We’ll start by creating a new UIViewController subclass called CarViewController.

Screen Shot 2016-05-15 at 17.40.52

Then, in Main.storyboard, we’ll add a view controller containing 3 text fields. The view controller will be connected to our ReactiveTableViewController with a segue called showCar.

Screen Shot 2016-05-15 at 17.37.10

To be able to link those text fields, we know what we have to do, right? Set the class of the view controller to CarViewController and Ctrl+drag them from the storyboard to our class using the Assistant Editor in Xcode. The result should look like this:

Screen Shot 2016-05-15 at 17.43.05

We also need a variable that will hold the CarViewModel and a dispose bag for RxSwift:

Now let’s switch to the viewDidLoad function and write the code that will display and update data. If you’ve done this in the past without Functional reactive programming and MVVM, you’ll know that we’d need a text field delegate, then check which field the user is editing, map the fields to the Car properties or have some sort of switch or if statements that will update the proper field.

But not with RxSwift and MVVM!

Let’s start (note that we’re in the viewDidLoad function)

This might look complicated, but the comments should guide you through. There practically isn’t anything new that we haven’t seen in the CarViewModel’s initializer. Now compare these short 15 lines of code (including comments) to whatever we were used to writing with the UITextFieldDelegates, checking which text field updates which car property, etc … It’s a bliss!

Ok, so to test if we’ve wired the fields up properly, we have to actually perform our showCar segue and assign the carViewModel. We’ll use RxSwift for this as well and keep our ReactiveTableViewController nice and clean. Let’s open the file and navigate to the end of viewDidLoad and add this:

What does this do? If we look at the function name, it’s pretty self-explanatory. Whenever an item is selected ( rxItemSelected), our subscribeNext will get fired, and we’ll perform the showCar segue and deselect the row (we shouldn’t forget this!).

If we try to run the project at this point, the new CarViewController should be presented, but the text fields won’t hold any data because the guard is returning out of the viewDidLoad function. We’re still not passing the selected carViewModel to our new VC. Here’s how to do that:

A guard to make sure we have the correct destination view controller and that the sender object is an indexPath. It could also be the carViewModel itself – both approaches are fine.

Next, we set the carViewModel on our destination VC and run the project.

Considering how few lines of code we’ve written, the result is stunning. It’s not perfect – we’d still have to put some checks in place, so the user does not input crazy values in kilowatt field and such – but it’s still a great demonstration of the power that MVVM combined with a binding mechanism has.

The final result:

As always, you can see the full project on GitHub or download the latest zipped version directly. If you liked this post or have any suggestions, make sure you leave a comment below and subscribe to my newsletter!

Thanks for reading!

STOP LOSING TIME WITH AUTO LAYOUT!

Take part in the 5-day course with actionable tasks that will let you become a master at recognizing and solving the most common mistakes iOS developers do with Auto Layout.

Let Auto Layout become a tool you swing with your utmost confidence!

I won't send you spam, I promise. Unsubscribe at any time. Powered by ConvertKit
Hey there! You're already subscribed to my newsletter and you've hopefully gotten some useful tips and tricks when working with iOS. If you're also working with Auto Layout, make sure to check out my book called Auto Layout Fundamentals and get a 20% off for being my subscriber! https://gum.co/autolayoutfundamentals/youareawesome
Jure Zove

A lot of things but mostly a programmer who really likes fast cars. Check me out on Twitter, if you fancy.

Related article

How to properly do buttons in table view cells using Swift closures

Note: This post is an upgrade of the original post…

A practical MVVM example in Swift – Part 1

The good ol’ MVC pattern has been around for a…

Supporting links in text

If you’ve encountered a use case where you need a…

  • John

    Hey Jure. Many thanks for that super cool 2-part tutorial on MVVM and especially of using RxSwift in part 2.
    Just one question. I just start digging into the RxSwift world. Is there a rule of thumb what part of the app to make reactive or is this an all or nothing thing?

    Cheers,
    John

    • Hey John!

      If you decide to use RxSwift, I’d say as many parts of the app as possible. Otherwise you end up with a codebase that won’t make a lot of sense to other developers.

      If you’re new to this, I’d start by making all the UI reactive. That means all the fields, taps, swipes, etc. Everything that changes a model for example. Then, you can continue with making your backend/API communication code reactive. This is helpful when trying to sync stuff, so you don’t have to handle a gazillion different states in the app.

      Hope this helps!
      Thanks

  • person

    Thanks!

  • Gusev Slava

    Very cool tutorial!

    But what is the idea oh this code?

    modelText = BehaviorSubject(value: car.model)
    modelText.subscribeNext { (model) in
    car.model = model
    }.addDisposableTo(disposeBag)

    car.model = model – here car is the object that we pass inside initializer (we don’t use self).

    It means that inside the subscibeNext block we change the property of the object car, but this property is already changed if we are in this block.

    I mean that class is reference type. Objects self.car and car (that is parameter in initializer) are references on the one object.

    Are we need any bindings in such case?

  • António Pinho

    One thing, why are you adding the ViewModel to a View? CarTableViewCell has a reference to the ViewModel. Isn’t this breaking the whole concept?

    • Hey there! Which View do you have in mind? The CarTableViewCell needs a reference to the CarViewModel – they can (and should) communicate between each other and know about their public properties/methods – it’s the controller that shouldn’t be changing views on the CarTableViewCell itself. Hope I’m not misunderstanding.

  • Yemi

    What happened to photoURL? It was never updated when we made CarViewModel reactive.

    • That’s true – I decided to only add the three text fields for other properties, but updating that one is just a matter of adding a text field and subscribing to the event, then reloading the image.

Type your search keyword, and press enter to search