Skip to content

MVVM -> Avoiding the “Imperative Soup”

The Setup

After working with MVC and MVP in cocoa touch for the last few years, I think it’s time to take a step back and consider the MVVM approach. Recently, I watched a talk given my Ben DiFrancesco¬†where he discusses how and why you should adopt MVVM in your projects. Ben explains how adding target/action, delegation, notifications, KVO, and blocks(closures in Swift) to your ViewControllers really just creates this “imperative soup” where it’s complicated to reason about your business logic and other code. Additionally, it becomes harder to test and target your code for other platforms because you’re tightly coupled to platform specific frameworks.¬†Speaking from experience, this definitely becomes a problem when you want to start adding extensions to your app; making sure all of your business logic works on Today, Watch, and other extensions¬†and¬†your main app can be a nightmare if the implementation isn’t handled with care.

Recap of MVC

Screen Shot 2016-07-29 at 4.05.23 PM

 

 

 

 

 

 

With MVC in iOS the controller has shared responsibilities; presentation logic is is mixed with business logic. This creates the imperative soup that Ben talks about: “The end result is code that’s hard reason about, hard to test, and [generally] ugly.”

Let’s take a look at how MVVM attempts to solve this problem.

Overview of MVVM

Screen Shot 2016-07-29 at 4.36.09 PM

 

 

 

 

With MVVM, we introduce a new object called the ViewModel that encapsulates presentation logic. The ViewModel¬†consumes a model or group of models and creates properties that are pre-formatted for presentation on the view. The example that Ben gives are formatting strings, determining subview colors, setting show/hide booleans, or determining the number of rows in a table view. With this pattern, the ViewController’s job becomes a lot simpler because it’s just connecting ViewModel Properties to view properties.

Example App

In the spirit of learning new things, we’re going to put our MVVM skills to the test by making an example app. Imagine you’ve just been hired for a new role at a photograph agency¬†and they’ve tasked you with making an app to showcase company’s directory of agents and their portfolios. They’ve decided on the name Portfolious, because it did well in hip/trendy focus groups. Anyways, in Portfolious, you’ll have a list of agents¬†with data pertinent to them and a portfolio of their work. For your starting point, you’ll be using a subset of this¬†JSON Placeholder¬†data and a¬†hip new networking library¬†I’ve been turned on to recently. You can checkout the complete project on Github.

Application Layout

Using Sketch, we’ll create a view to showcase the¬†theme of¬†Portfolious. We’ll have a table¬†view of agents¬†where each cell will have some basic information like their name. Once you tap into an agent, you’ll be given an expanded set of ¬†their details (provided by the user model). Below that, we’ll have a table¬†view of their albums where each album will present a collection view of the photos in that album.

Simulator Screen Shot Jul 31, 2016, 8.48.55 PM

Data Models

For our data source, we’ll be using three primary models: User, Album, and Photo. Each user will be an employee in the directory and we’ll include an album of their favorite photos when you go into their profile.

Users are defined as such:

Here are albums:

And photos within those albums:

To get access to these JSON models, we’re going to use this networking library we mentioned earlier. To add it to our project, we’re going to use Carthage. Add the following line to your Cartfile in the top-level of your project directory.

Next, run the following command in your project directory to get the framework built.

 Drag and drop the frameworks produce by Carthage to your Embedded Binaries area of your main target.

Screen Shot 2016-07-30 at 8.44.36 AM

Screen Shot 2016-07-30 at 8.45.51 AM

And add this run phase script with the frameworks as input files.

Screen Shot 2016-07-30 at 8.51.20 AM

If we run the project we should get a blank screen, but our frameworks were added successfully.

Adding Models

The first thing we’re going to do is create some data models that our application will work with. Each model is a struct with properties based of the JSON we’re getting from JSON Placeholder. Each model¬†conforms to the equatable¬†protocol and implements its own¬†==¬†function.

API Client Controller

We’re going to add a network client called¬†JSONPlaceholderClient.swift and it’s gaining to contain some code to connect to the JSONPlaceholder service

Now that we have some code to talk to our remote JSON service, we can start thinking¬†about how we can add JSON promising for our models. We’ll be using Argo to take the JSON we receive so we can encode it into our models. To add Argo, Curry, and Runes(Curry and Runes are¬†Argo dependencies) into the project using Carthage, we need to add these¬†lines to our Cartfile:

and run

to build the scheme.

Once they’re built, we need to add them to our embedded binaries like we did before with ReactiveJSON.

Next we need to add Argo to our embedded binaries and run script like we did before. If everything worked, we should be able to import Argo into models to allow for JSON decoding.

Binding Example

For binding, we’re going to use plain old delegation with protocols. When we get new data from our network resources, we’re going to parse the response, use argo to build promised models that are guaranteed to be the objects we want, and finally send the model back to the ViewController so things like table/collection views can reload the data.

Here’s an example setup for the Photos collection view:

and the ViewController

When the data comes back from the ViewModel, we’re making a slight deviation from our networking library we used earlier and instead using this UIImageView extension that asynchronously downloads the image data from the Photo model.

Wrapping up

Wow, it’s been quite a ride. We’ve taking a look at some design, Carthage, ReactiveJSON, MVVM, JSON promising with Argo, and things are looking almost good enough to ship. Going from here we would probably want to think of how we could optimize when we get resources from the network by caching results. Additionally, we might want to add some placeholder images for the albums and agent portfolios. Finally, we might want to add some unit tests if we decide to take Portfolious to market so we can be sure we’re shipping great production code.

Complete Project

Simulator Screen Shot Jul 31, 2016, 8.48.55 PM

Simulator Screen Shot Jul 31, 2016, 8.49.57 PM

Simulator Screen Shot Jul 31, 2016, 8.50.00 PM

You can find the complete project for this post on Github and I thank you for sticking with me on this journey!


Also published on Medium.

Be First to Comment

Leave a Reply

Your email address will not be published. Required fields are marked *

9 + 1 =