So you’re starting a new iOS project, you received from the designer all the needed .pdf and .sketch documents, and you already have a vision about how you’ll build this new app.

You start transferring UI screens from the designer’s sketches into your ViewController .swift, .xib and .storyboard files.

UITextField here, UITableView there, a few more UILabels and a pinch of UIButtons. IBOutlets and IBActions are also included. All good, we are still in the UI zone.

However, it’s time to do something with all these UI elements; UIButtons will receive finger touches, UILabels and UITableViews will need someone to tell them what to display and in what format.

Suddenly, you have more than 3,000 lines of code.

3,000 lines of Swift code

You ended up with a lot of spaghetti code.

The first step to resolve this is to apply the Model-View-Controller (MVC) design pattern. However, this pattern has its own issues. There comes the Model-View-ViewModel (MVVM) design pattern that saves the day.

Dealing With Spaghetti Code

In no time, your starting ViewController has become too smart and too massive.

Networking code, data parsing code, data adjustments code for the UI presentation, app state notifications, UI state changes. All that code imprisoned inside if-ology of a single file that cannot be reused and would only fit in this project.

Your ViewController code has become the infamous spaghetti code.

How did that happen?

The likely reason is something like this:

You were in a rush to see how the back-end data was behaving inside the UITableView , so you put a few lines of networking code inside a temp method of the ViewController just to fetch that .json from the network. Next, you needed to process the data inside that .json, so you wrote yet another temp method to accomplish that. Or, even worse, you did that inside the same method.

The ViewController kept growing when the user authorization code came along. Then data formats started to change, UI evolved and needed some radical changes, and you just kept adding more ifs into an already massive if-ology.

But, how come the UIViewController is what got out of hand?

The UIViewController is the logical place to start working on your UI code. It represents the physical screen you’re seeing while using any app with your iOS device. Even Apple uses UIViewControllers in its main system app when it switches between different apps and its animated UIs.

Apple bases its UI abstraction inside the UIViewController, since it is at the core of the iOS UI code and part of the MVC design pattern.

Upgrading to the MVC Design Pattern

MVC Design Pattern

In the MVC design pattern, View is supposed to be inactive and only displays prepared data on demand.

Controller should work on the Model data to prepare it for the Views, which then display that data.

View is also responsible for notifying the Controller about any actions, such as user touches.

As mentioned, UIViewController is usually the starting point in building a UI screen. Notice that in its name, it contains both the “view” and the “controller.” This means that it “controls the view.” It doesn’t mean that both “controller” and “view” code should go inside.

This mixture of view and controller code often happens when you move IBOutlets of little subviews inside the UIViewController, and manipulate on those subviews directly from the UIViewController. Instead you should’ve wrapped that code inside of a custom UIView subclass.

Easy to see that this could lead to View and Controller code paths getting crossed.

MVVM To the Rescue

This is where the MVVM pattern comes in handy.

Since UIViewController is supposed to be a Controller in the MVC pattern, and it’s already doing a lot with the Views, we can merge them into the View of our new pattern - MVVM.

MVVM Design Pattern

In the MVVM design pattern, Model is the same as in MVC pattern. It represents simple data.

View is represented by the UIView or UIViewController objects, accompanied with their .xib and .storyboard files, which should only display prepared data. (We don’t want to have NSDateFormatter code, for example, inside the View.)

Only a simple, formatted string that comes from the ViewModel.

ViewModel hides all asynchronous networking code, data preparation code for visual presentation, and code listening for Model changes. All of these are hidden behind a well-defined API modeled to fit this particular View.

One of the benefits of using MVVM is testing. Since ViewModel is pure NSObject (or struct for example), and it’s not coupled with the UIKit code, you can test it more easily in your unit tests without it affecting the UI code.

Now, the View (UIViewController/UIView) has become much simpler while ViewModel acts as the glue between the Model and View.

Applying MVVM In Swift

MVVM In Swift

To show you MVVM in action, you can download and examine the example Xcode project created for this tutorial here. This project uses Swift 3 and Xcode 8.1.

There are two versions of the project: Starter and Finished.

The Finished version is a completed mini application, where Starter is the same project but without the methods and objects implemented.

First, I suggest you download the Starter project, and follow this tutorial. If you need a quick reference of the project for later, download the Finished project.

Tutorial Project Introduction

The tutorial project is a basketball application for the tracking of player actions during the game.

Basketball application

It’s used for the quick tracking of user moves and of the overall score in a pickup game.

Two teams play until the score of 15 (with at least a two-point difference) is reached. Each player can score one point to two points, and each player can assist, rebound, and foul.

Project hierarchy looks like this:

Project hierarchy

Model

  • Game.swift
    • Contains game logic, tracks overall score, tracks each player’s moves.
  • Team.swift
    • Contains team name and players list (three players in each team).
  • Player.swift
    • A single player with a name.

View

  • HomeViewController.swift
    • Root view controller, which presents the GameScoreboardEditorViewController
  • GameScoreboardEditorViewController.swift
    • Supplemented with Interface Builder view in Main.storyboard.
    • Screen of interest for this tutorial.
  • PlayerScoreboardMoveEditorView.swift
    • Supplemented with Interface Builder view in PlayerScoreboardMoveEditorView.xib
    • Subview of the above view, also uses MVVM design pattern.

ViewModel

  • ViewModel group is empty, this is what you will be building in this tutorial.

The downloaded Xcode project already contains placeholders for the View objects (UIView and UIViewController). The project also contains some custom-made objects made to demo one of the ways on how to provide data to the ViewModel objects (Services group).

The Extensions group contains useful extensions for the UI code that are not in the scope of this tutorial and are self-explanatory.

If you run the app at this point, it will show the finished UI, but nothing happens, when a user presses the buttons.

This is because you’ve only created views and IBActions without connecting them to the app logic and without filling UI elements with the data from the model (from the Game object, as we will learn later).

Connecting View and Model with ViewModel

In the MVVM design pattern, View should not know anything about the Model. The only thing that View knows is how to work with a ViewModel.

Start by examining your View.

In the GameScoreboardEditorViewController.swift file, the fillUI method is empty at this point. This is the place you want to populate the UI with data. To achieve this, you need to provide data for the ViewController. You do this with a ViewModel object.

First, create a ViewModel object that contains all the necessary data for this ViewController.

Go to the ViewModel Xcode project group, which will be empty, create a GameScoreboardEditorViewModel.swift file, and make it a protocol.

import Foundation

protocol GameScoreboardEditorViewModel {
    var homeTeam: String { get }
    var awayTeam: String { get }
    var time: String { get }
    var score: String { get }
    var isFinished: Bool { get }
    
    var isPaused: Bool { get }
    func togglePause();
}

Using protocols like this keeps thing nice and clean; you only must define data you will be using.

Next, create an implementation for this protocol.

Create a new file, called GameScoreboardEditorViewModelFromGame.swift, and make this object a subclass of NSObject.

Also, make it conform to the GameScoreboardEditorViewModel protocol:

import Foundation

class GameScoreboardEditorViewModelFromGame: NSObject, GameScoreboardEditorViewModel {
    
    let game: Game
    
    struct Formatter {
        static let durationFormatter: DateComponentsFormatter = {
            let dateFormatter = DateComponentsFormatter()
            dateFormatter.unitsStyle = .positional
            return dateFormatter
        }()
    }
    
    // MARK: GameScoreboardEditorViewModel protocol
    
    var homeTeam: String
    var awayTeam: String
    
    var time: String
    var score: String
    var isFinished: Bool
    
    var isPaused: Bool
    func togglePause() {
        if isPaused {
            startTimer()
        } else {
            pauseTimer()
        }
        
        self.isPaused = !isPaused
    }
    
    // MARK: Init
    
    init(withGame game: Game) {
        self.game = game
        
        self.homeTeam = game.homeTeam.name
        self.awayTeam = game.awayTeam.name
        
        self.time = GameScoreboardEditorViewModelFromGame.timeRemainingPretty(for: game)
        self.score = GameScoreboardEditorViewModelFromGame.scorePretty(for: game)
        self.isFinished = game.isFinished
        self.isPaused = true
    }
    
    // MARK: Private
    
    fileprivate var gameTimer: Timer?
    fileprivate func startTimer() {
        let interval: TimeInterval = 0.001
        gameTimer = Timer.schedule(repeatInterval: interval) { timer in
            self.game.time += interval
            self.time = GameScoreboardEditorViewModelFromGame.timeRemainingPretty(for: self.game)
        }
    }
    
    fileprivate func pauseTimer() {
        gameTimer?.invalidate()
        gameTimer = nil
    }
    
    // MARK: String Utils
    
    fileprivate static func timeFormatted(totalMillis: Int) -> String {
        let millis: Int = totalMillis % 1000 / 100 // "/ 100" <- because we want only 1 digit
        let totalSeconds: Int = totalMillis / 1000
        
        let seconds: Int = totalSeconds % 60
        let minutes: Int = (totalSeconds / 60)
        
        return String(format: "%02d:%02d.%d", minutes, seconds, millis)
    }
    
    fileprivate static func timeRemainingPretty(for game: Game) -> String {
        return timeFormatted(totalMillis: Int(game.time * 1000))
    }
    
    fileprivate static func scorePretty(for game: Game) -> String {
        return String(format: "\(game.homeTeamScore) - \(game.awayTeamScore)")
    }
    
}

Notice that you’ve provided everything needed for the ViewModel to work through the initializer.

You provided it the Game object, which is the Model underneath this ViewModel.

If you run the app now, it still won’t work because you haven’t connected this ViewModel data to the View, itself.

So, go back to the GameScoreboardEditorViewController.swift file, and create a public property named viewModel.

Make it of the type GameScoreboardEditorViewModel.

Place it right before the viewDidLoad method inside the GameScoreboardEditorViewController.swift.

var viewModel: GameScoreboardEditorViewModel? {
    didSet {
        fillUI()
    }
}

Next, you need to implement the fillUI method.

Notice how this method is called from two places, the viewModel property observer (didSet) and the viewDidLoad method. This is because we can create a ViewController and assign a ViewModel to it before attaching it to a view (before viewDidLoad method is called).

On the other hand, you could attach ViewController’s view to another view and call viewDidLoad, but if viewModel is not set at that time, nothing will happen.

That’s why first, you need to check if everything is set for your data to fill the UI. It’s important to guard your code against unexpected usage.

So, go to fillUI method, and replace it with the following code:

fileprivate func fillUI() {
    if !isViewLoaded {
        return
    }
    
    guard let viewModel = viewModel else {
        return
    }
    
    // we are sure here that we have all the setup done
    
    self.homeTeamNameLabel.text = viewModel.homeTeam
    self.awayTeamNameLabel.text = viewModel.awayTeam
    
    self.scoreLabel.text = viewModel.score
    self.timeLabel.text = viewModel.time
    
    let title: String = viewModel.isPaused ? "Start" : "Pause"
    self.pauseButton.setTitle(title, for: .normal)
}

Now, implement the pauseButtonPress method:

@IBAction func pauseButtonPress(_ sender: AnyObject) {
    viewModel?.togglePause()
}

All you need do now, is set the actual viewModel property on this ViewController. You do this “from the outside.”

Open HomeViewController.swift file and uncomment the ViewModel; create and setup lines in the showGameScoreboardEditorViewController method:

// uncomment this when view model is implemented
let viewModel = GameScoreboardEditorViewModelFromGame(withGame: game)
controller.viewModel = viewModel

Now, run the app. It should look something like this:

iOS App

The middle view, which is responsible for the score, time, and team names, no longer shows values set in the Interface Builder.

Now, it’s showing the values from the ViewModel object itself, which gets its data from the actual Model object (Game object).

Excellent! But what about the player views? Those buttons still don’t do anything.

You know that you have six views for player-moves tracking.

You created a separate subview, named PlayerScoreboardMoveEditorView for that, which does nothing with the real data for now and displays static values that were set through the Interface Builder inside the PlayerScoreboardMoveEditorView.xib file.

You need to give it some data.

You’ll do it the same way you did with GameScoreboardEditorViewController and GameScoreboardEditorViewModel.

Open the ViewModel group in the Xcode project, and define the new protocol here.

Create a new file named PlayerScoreboardMoveEditorViewModel.swift, and put the following code inside:

import Foundation

protocol PlayerScoreboardMoveEditorViewModel {
    var playerName: String { get }
    
    var onePointMoveCount: String { get }
    var twoPointMoveCount: String { get }
    var assistMoveCount: String { get }
    var reboundMoveCount: String { get }
    var foulMoveCount: String { get }
    
    func onePointMove()
    func twoPointsMove()
    func assistMove()
    func reboundMove()
    func foulMove()
}

This ViewModel protocol was designed to fit your PlayerScoreboardMoveEditorView, just as you did in the parent view, GameScoreboardEditorViewController.

You need to have values for the five different moves that a user can make, and you need to react, when the user touches one of the action buttons. You also need a String for the player name.

After you’ve done this, create a concrete class that implements this protocol, just as you did with the parent view (GameScoreboardEditorViewController).

Next, create an implementation of this protocol: Create a new file, name it PlayerScoreboardMoveEditorViewModelFromPlayer.swift, and make this object a subclass of NSObject. Also, make it conform to the PlayerScoreboardMoveEditorViewModel protocol:

import Foundation

class PlayerScoreboardMoveEditorViewModelFromPlayer: NSObject, PlayerScoreboardMoveEditorViewModel {
    
    fileprivate let player: Player
    fileprivate let game: Game
    
    // MARK: PlayerScoreboardMoveEditorViewModel protocol
    
    let playerName: String
    
    var onePointMoveCount: String
    var twoPointMoveCount: String
    var assistMoveCount: String
    var reboundMoveCount: String
    var foulMoveCount: String
    
    func onePointMove() {
        makeMove(.onePoint)
    }
    
    func twoPointsMove() {
        makeMove(.twoPoints)
    }
    
    func assistMove() {
        makeMove(.assist)
    }
    
    func reboundMove() {
        makeMove(.rebound)
    }
    
    func foulMove() {
        makeMove(.foul)
    }
    
    // MARK: Init
    
    init(withGame game: Game, player: Player) {
        self.game = game
        self.player = player
        
        self.playerName = player.name
        self.onePointMoveCount = "\(game.playerMoveCount(for: player, move: .onePoint))"
        self.twoPointMoveCount = "\(game.playerMoveCount(for: player, move: .twoPoints))"
        self.assistMoveCount = "\(game.playerMoveCount(for: player, move: .assist))"
        self.reboundMoveCount = "\(game.playerMoveCount(for: player, move: .rebound))"
        self.foulMoveCount = "\(game.playerMoveCount(for: player, move: .foul))"
    }
    
    // MARK: Private
    
    fileprivate func makeMove(_ move: PlayerInGameMove) {
        game.addPlayerMove(move, for: player)
        
        onePointMoveCount = "\(game.playerMoveCount(for: player, move: .onePoint))"
        twoPointMoveCount = "\(game.playerMoveCount(for: player, move: .twoPoints))"
        assistMoveCount = "\(game.playerMoveCount(for: player, move: .assist))"
        reboundMoveCount = "\(game.playerMoveCount(for: player, move: .rebound))"
        foulMoveCount = "\(game.playerMoveCount(for: player, move: .foul))"
    }
    
}

Now, you need to have an object that will create this instance “from the outside,” and set it as a property inside the PlayerScoreboardMoveEditorView.

Remember how HomeViewController was responsible for setting the viewModel property on the GameScoreboardEditorViewController?

In the same way, GameScoreboardEditorViewController is a parent view of your PlayerScoreboardMoveEditorView and that GameScoreboardEditorViewController will be responsible for creating of PlayerScoreboardMoveEditorViewModel objects.

You need to expand your GameScoreboardEditorViewModel first.

Open GameScoreboardEditorViewModel and add these two properties:

var homePlayers: [PlayerScoreboardMoveEditorViewModel] { get }
var awayPlayers: [PlayerScoreboardMoveEditorViewModel] { get }

Also, update the GameScoreboardEditorViewModelFromGame with these two properties just above the initWithGame method:

let homePlayers: [PlayerScoreboardMoveEditorViewModel]
let awayPlayers: [PlayerScoreboardMoveEditorViewModel]

Add these two lines inside initWithGame:

self.homePlayers = GameScoreboardEditorViewModelFromGame.playerViewModels(from: game.homeTeam.players, game: game)
self.awayPlayers = GameScoreboardEditorViewModelFromGame.playerViewModels(from: game.awayTeam.players, game: game)

And of course, add the missing playerViewModelsWithPlayers method:

// MARK: Private Init

fileprivate static func playerViewModels(from players: [Player], game: Game) -> [PlayerScoreboardMoveEditorViewModel] {
    var playerViewModels: [PlayerScoreboardMoveEditorViewModel] = [PlayerScoreboardMoveEditorViewModel]()
    for player in players {
        playerViewModels.append(PlayerScoreboardMoveEditorViewModelFromPlayer(withGame: game, player: player))
    }
    
    return playerViewModels
}

Great!

You’ve updated your ViewModel (GameScoreboardEditorViewModel) with the home and away players array. You still need to fill these two arrays.

You’ll do this in the same place you used this viewModel to fill up the UI.

Open GameScoreboardEditorViewController and go to the fillUI method. Add these lines at the method end:

homePlayer1View.viewModel = viewModel.homePlayers[0]
homePlayer2View.viewModel = viewModel.homePlayers[1]
homePlayer3View.viewModel = viewModel.homePlayers[2]
        
awayPlayer1View.viewModel = viewModel.awayPlayers[0]
awayPlayer2View.viewModel = viewModel.awayPlayers[1]
awayPlayer3View.viewModel = viewModel.awayPlayers[2]

For the moment, you have build errors because you didn’t add the actual viewModel property inside the PlayerScoreboardMoveEditorView.

Add the following code above the init method inside the PlayerScoreboardMoveEditorView`.

var viewModel: PlayerScoreboardMoveEditorViewModel? {
    didSet {
        fillUI()
    }
}

And implement the fillUI method:

fileprivate func fillUI() {
    guard let viewModel = viewModel else {
        return
    }
    
    self.name.text = viewModel.playerName
    
    self.onePointCountLabel.text = viewModel.onePointMoveCount
    self.twoPointCountLabel.text = viewModel.twoPointMoveCount
    self.assistCountLabel.text = viewModel.assistMoveCount
    self.reboundCountLabel.text = viewModel.reboundMoveCount
    self.foulCountLabel.text = viewModel.foulMoveCount
}

Finally, run the app, and see how the data in the UI elements is the actual data from the Game object.

iOS App

At this point, you have a functional app that uses the MVVM design pattern.

It nicely hides the Model from the View, and your View is much simpler than you’re used to with the MVC.

Up to this point, you’ve created an app that contains the View and its ViewModel.

That View also has six instances of the same subview (player view) with its ViewModel.

However, as you may notice, you can only display data in the UI once (in the fillUI method), and that data is static.

If your data in the views won’t be changing during the lifetime of that view, then you have a good and clean solution to use MVVM in this way.

Making the ViewModel Dynamic

Because your data will change, you need to make your ViewModel dynamic.

What this means is that when the Model changes, ViewModel should change its public property values; it would propagate the change back to the view, which is the one that will update the UI.

There are a lot of ways to do this.

When Model changes, ViewModel gets notified first.

You need some mechanism to propagate what changes up to the View.

Some of the options include RxSwift, which is a pretty large library and takes some time to get used to.

ViewModel could be firing NSNotifications on each property value change, but this adds a lot of code that needs additional handling, such as subscribing to notifications and unsubscribing when the view gets deallocated.

Key-Value-Observing (KVO) is another option, but users will confirm that its API is not fancy.

In this tutorial, you’ll use Swift generics and closures, which are nicely described in the Bindings, Generics, Swift and MVVM article.

Now, let’s get back to the example app.

Go to the ViewModel project group, and create a new Swift file, Dynamic.swift.

class Dynamic<T> {
    typealias Listener = (T) -> ()
    var listener: Listener?
    
    func bind(_ listener: Listener?) {
        self.listener = listener
    }
    
    func bindAndFire(_ listener: Listener?) {
        self.listener = listener
        listener?(value)
    }
    
    var value: T {
        didSet {
            listener?(value)
        }
    }
    
    init(_ v: T) {
        value = v
    }
}

You’ll use this class for properties in your ViewModels that you expect to change during the View lifecycle.

First, start with the PlayerScoreboardMoveEditorView and its ViewModel, PlayerScoreboardMoveEditorViewModel.

Open PlayerScoreboardMoveEditorViewModel and look at its properties.

Because the playerName isn’t expected to change, you can leave it as is.

The other five properties (five move types) will change, so you need to do something about that. The solution? The above mentioned Dynamic class that you just added to the project.

Inside PlayerScoreboardMoveEditorViewModel remove definitions for five Strings that represent move counts and replace it with this:

var onePointMoveCount: Dynamic<String> { get }
var twoPointMoveCount: Dynamic<String> { get }
var assistMoveCount: Dynamic<String> { get }
var reboundMoveCount: Dynamic<String> { get }
var foulMoveCount: Dynamic<String> { get }

This is how the ViewModel protocol should look like now:

import Foundation

protocol PlayerScoreboardMoveEditorViewModel {
    var playerName: String { get }
    
    var onePointMoveCount: Dynamic<String> { get }
    var twoPointMoveCount: Dynamic<String> { get }
    var assistMoveCount: Dynamic<String> { get }
    var reboundMoveCount: Dynamic<String> { get }
    var foulMoveCount: Dynamic<String> { get }
    
    func onePointMove()
    func twoPointsMove()
    func assistMove()
    func reboundMove()
    func foulMove()
}

This Dynamic type enables you to change the value of that particular property, and at the same time, notify the change-listener object, which, in this case, will be the View.

Now, update the actual ViewModel implementation PlayerScoreboardMoveEditorViewModelFromPlayer.

Replace this:

var onePointMoveCount: String
var twoPointMoveCount: String
var assistMoveCount: String
var reboundMoveCount: String
var foulMoveCount: String

with the following:

let onePointMoveCount: Dynamic<String>
let twoPointMoveCount: Dynamic<String>
let assistMoveCount: Dynamic<String>
let reboundMoveCount: Dynamic<String>
let foulMoveCount: Dynamic<String>

Note: It’s OK to declare these properties as constants with let since you won’t change the actual property. You will change the value property on the Dynamic object.

Now, there’s build errors because you did not initialize your Dynamic objects.

Inside PlayerScoreboardMoveEditorViewModelFromPlayer’s init method, replace initialization of move properties with this:

self.onePointMoveCount = Dynamic("\(game.playerMoveCount(for: player, move: .onePoint))")
self.twoPointMoveCount = Dynamic("\(game.playerMoveCount(for: player, move: .twoPoints))")
self.assistMoveCount = Dynamic("\(game.playerMoveCount(for: player, move: .assist))")
self.reboundMoveCount = Dynamic("\(game.playerMoveCount(for: player, move: .rebound))")
self.foulMoveCount = Dynamic("\(game.playerMoveCount(for: player, move: .foul))")

Inside PlayerScoreboardMoveEditorViewModelFromPlayer go to the makeMove method, and replace it with the following code:

fileprivate func makeMove(_ move: PlayerInGameMove) {
    game.addPlayerMove(move, for: player)
    
    onePointMoveCount.value = "\(game.playerMoveCount(for: player, move: .onePoint))"
    twoPointMoveCount.value = "\(game.playerMoveCount(for: player, move: .twoPoints))"
    assistMoveCount.value = "\(game.playerMoveCount(for: player, move: .assist))"
    reboundMoveCount.value = "\(game.playerMoveCount(for: player, move: .rebound))"
    foulMoveCount.value = "\(game.playerMoveCount(for: player, move: .foul))"
}

As you can see, you’ve created instances of Dynamic class, and assigned it String values. When you need to update the data, don’t change the Dynamic property itself; rather update it’s value property.

Great! PlayerScoreboardMoveEditorViewModel is dynamic now.

Let’s make use of it, and go to the view that will actually listen for these changes.

Open PlayerScoreboardMoveEditorView and its fillUI method (you should see build errors in this method at this point since you’re trying to assign String value to the Dynamic object type.)

Replace the “errored” lines:

self.onePointCountLabel.text = viewModel.onePointMoveCount
self.twoPointCountLabel.text = viewModel.twoPointMoveCount
self.assistCountLabel.text = viewModel.assistMoveCount
self.reboundCountLabel.text = viewModel.reboundMoveCount
self.foulCountLabel.text = viewModel.foulMoveCount

with the following:

viewModel.onePointMoveCount.bindAndFire { [unowned self] in self.onePointCountLabel.text = $0 }
viewModel.twoPointMoveCount.bindAndFire { [unowned self] in self.twoPointCountLabel.text = $0 }
viewModel.assistMoveCount.bindAndFire { [unowned self] in self.assistCountLabel.text = $0 }
viewModel.reboundMoveCount.bindAndFire { [unowned self] in self.reboundCountLabel.text = $0 }
viewModel.foulMoveCount.bindAndFire { [unowned self] in self.foulCountLabel.text = $0 }

Next, implement the five methods that represent move actions (Button Action section):

@IBAction func onePointAction(_ sender: Any) {
    viewModel?.onePointMove()
}

@IBAction func twoPointsAction(_ sender: Any) {
    viewModel?.twoPointsMove()
}

@IBAction func assistAction(_ sender: Any) {
    viewModel?.assistMove()
}

@IBAction func reboundAction(_ sender: Any) {
    viewModel?.reboundMove()
}

@IBAction func foulAction(_ sender: Any) {
    viewModel?.foulMove()
}

Run the app, and click on some move buttons. You’ll see how the counter values inside the player views change when you click on the action button.

iOS App

You’re finished with the PlayerScoreboardMoveEditorView and PlayerScoreboardMoveEditorViewModel.

This was simple.

Now, you need to do the same with your main view (GameScoreboardEditorViewController).

First, open GameScoreboardEditorViewModel and see which values are expected to change during the view’s lifecycle.

Replace time, score, isFinished, isPaused definitions with the Dynamic versions:

import Foundation

protocol GameScoreboardEditorViewModel {
    var homeTeam: String { get }
    var awayTeam: String { get }
    var time: Dynamic<String> { get }
    var score: Dynamic<String> { get }
    var isFinished: Dynamic<Bool> { get }
    
    var isPaused: Dynamic<Bool> { get }
    func togglePause()
    
    var homePlayers: [PlayerScoreboardMoveEditorViewModel] { get }
    var awayPlayers: [PlayerScoreboardMoveEditorViewModel] { get }
}

Go to the ViewModel implementation (GameScoreboardEditorViewModelFromGame) and do the same with the properties declared in the protocol.

Replace this:

var time: String
var score: String
var isFinished: Bool
 
var isPaused: Bool

with the following:

let time: Dynamic<String>
let score: Dynamic<String>
let isFinished: Dynamic<Bool>
    
let isPaused: Dynamic<Bool>

You’ll get a few errors, now, because you changed ViewModel’s type from String and Bool to Dynamic<String> and Dynamic<Bool>.

Let’s fix that.

Fix the togglePause method by replacing it with the following:

func togglePause() {
    if isPaused.value {
        startTimer()
    } else {
        pauseTimer()
    }
        
    self.isPaused.value = !isPaused.value
}

Notice how the only change is that you no longer set the property value directly on the property. Instead, you set it on the object’s value property.

Now, fix the initWithGame method by replacing this:

self.time = GameScoreboardEditorViewModelFromGame.timeRemainingPretty(game)
self.score = GameScoreboardEditorViewModelFromGame.scorePretty(game)
self.isFinished = game.isFinished
self.isPaused = true

with the following:

self.time = Dynamic(GameScoreboardEditorViewModelFromGame.timeRemainingPretty(for: game))
self.score = Dynamic(GameScoreboardEditorViewModelFromGame.scorePretty(for: game))
self.isFinished = Dynamic(game.isFinished)
self.isPaused = Dynamic(true)

You should get the point now.

You’re wrapping the primitive values, such as String, Int and Bool, with Dynamic<T> versions of those objects, which give you the lightweight binding mechanism.

You have one more error to fix.

In startTimer method, replace the error line with:

self.time.value = GameScoreboardEditorViewModelFromGame.timeRemainingPretty(for: self.game)

You’ve upgraded your ViewModel to be dynamic, just as you did with the player’s ViewModel. But you still need to update your View (GameScoreboardEditorViewController).

Replace the entire fillUI method with this:

fileprivate func fillUI() {
    if !isViewLoaded {
        return
    }
    
    guard let viewModel = viewModel else {
        return
    }
    
    self.homeTeamNameLabel.text = viewModel.homeTeam
    self.awayTeamNameLabel.text = viewModel.awayTeam
    
    viewModel.score.bindAndFire { [unowned self] in self.scoreLabel.text = $0 }
    viewModel.time.bindAndFire { [unowned self] in self.timeLabel.text = $0 }
    
    viewModel.isFinished.bindAndFire { [unowned self] in
        if $0 {
            self.homePlayer1View.isHidden = true
            self.homePlayer2View.isHidden = true
            self.homePlayer3View.isHidden = true
            
            self.awayPlayer1View.isHidden = true
            self.awayPlayer2View.isHidden = true
            self.awayPlayer3View.isHidden = true
        }
    }
    
    viewModel.isPaused.bindAndFire { [unowned self] in
        let title = $0 ? "Start" : "Pause"
        self.pauseButton.setTitle(title, for: .normal)
    }
    
    homePlayer1View.viewModel = viewModel.homePlayers[0]
    homePlayer2View.viewModel = viewModel.homePlayers[1]
    homePlayer3View.viewModel = viewModel.homePlayers[2]
    
    awayPlayer1View.viewModel = viewModel.awayPlayers[0]
    awayPlayer2View.viewModel = viewModel.awayPlayers[1]
    awayPlayer3View.viewModel = viewModel.awayPlayers[2]
}

The only difference is that you changed your four dynamic properties and added change listeners to each one of them.

At this point, if you run your app, toggling the Start/Pause button will start and pause the game timer. This is used for time-outs during the game.

You’re almost finished except the score is not changing in the UI, when you press one of the point buttons (1 and 2 points button).

This is because you haven’t really propagated score changes in the underlying Game model object up to the ViewModel.

So, open Game model object for a little examination. Check its updateScore method.

fileprivate func updateScore(_ score: UInt, withScoringPlayer player: Player) {
    if isFinished || score == 0 {
        return
    }
    
    if homeTeam.containsPlayer(player) {
        homeTeamScore += score
    } else {
        assert(awayTeam.containsPlayer(player))
        awayTeamScore += score
    }
    
    if checkIfFinished() {
        isFinished = true
    }
    
    NotificationCenter.default.post(name: Notification.Name(rawValue: GameNotifications.GameScoreDidChangeNotification), object: self)
}

This method does two important things.

First, it sets the isFinished property to true if the game is finished based on the scores of both teams.

After that, it posts a notification that the score has changed. You’ll listen for this notification in the GameScoreboardEditorViewModelFromGame and update dynamic score value in the notification handler method.

Add this line at the bottom of initWithGame method (don’t forget the super.init() call to avoid errors):

super.init()
subscribeToNotifications()

Below initWithGame method, add deinit method, since you want to do the cleanup properly and avoid crashes caused by the NotificationCenter.

deinit {
    unsubscribeFromNotifications()
}

Finally, add the implementations of these methods. Add this section right below the deinit method:

// MARK: Notifications (Private)

fileprivate func subscribeToNotifications() {
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(gameScoreDidChangeNotification(_:)),
                                           name: NSNotification.Name(rawValue: GameNotifications.GameScoreDidChangeNotification),
                                           object: game)
}

fileprivate func unsubscribeFromNotifications() {
    NotificationCenter.default.removeObserver(self)
}

@objc fileprivate func gameScoreDidChangeNotification(_ notification: NSNotification){
    self.score.value = GameScoreboardEditorViewModelFromGame.scorePretty(for: game)
    
    if game.isFinished {
        self.isFinished.value = true
    }
}

Now, run the app, and click on the player views to change scores. Since you’ve already connected dynamic score and isFinished in the ViewModel with the View, everything should work when you change the score value inside the ViewModel.

How to Further Improve the App

While there’s always room for improvement, this is out of scope of this tutorial.

For example, we do not stop the time automatically when the game is over (when one of the teams reaches 15 points), we just hide the player views.

You can play with the app if you like and upgrade it to have a “game creator” view, which would create a game, assign team names, assign player names and create a Game object that could be used to present GameScoreboardEditorViewController.

We can create another “game list” view that uses the UITableView to show multiple games in progress with some detailed info in the table cell. In cell select, we can show the GameScoreboardEditorViewController with the selected Game.

The GameLibrary has already been implemented. Just remember to pass that library reference to the ViewModel objects in their initializer. For example, “game creator’s” ViewModel would need to have an instance of GameLibrary passed through the initializer so that it would be able to insert the created Game object into the library. “Game list’s” ViewModel would also need this reference to fetch all games from the library, which will be needed by the UITableView.

The idea is to hide all of the dirty (non-UI) work inside the ViewModel and have the UI (View) only act with prepared presentation data.

What now?

After you get used to the MVVM, you can further improve it by using Uncle Bob’s Clean Architecture rules.

An additional good read is a three-part tutorial on Android architecture:

Examples are written in Java (for Android), and if you are familiar with Java (which is much closer to Swift then Objective-C is to Java), you’ll get ideas on how to further refactor your code inside the ViewModel objects so that they don’t import any iOS modules (UIKit or CoreLocation e.g.).

These iOS modules can be hidden behind the pure NSObjects, which is good for code reusability.

MVVM is a good choice for most iOS apps, and hopefully, you will give it a try in your next project. Or, try it in your current project when you are creating a UIViewController.

About the author

Dino Bartošak, Croatia
member since January 7, 2016
As a software engineer, Dino has specialized his skills in building mobile apps. He has worked with teams of various sizes and developed skills in building products iteratively, developed analytical abilities and prototyping techniques. He likes clean, modular, and maintainable code written to be reliable in all conditions. iOS is his platform of choice. [click to continue...]
Hiring? Meet the Top 10 Freelance iOS Developers for Hire in April 2017

Comments

Michel H.
I guess I have to read this a couple times. So much information!
Doug
I really appreciate your article and it has answered some of my questions. Let's say, for example, that you allow the user to select the font/font size/font color for each player in the table. Where do you include this UI related information? Is it in the model? The viewmodel? I haven't located an article yet that addresses this issue. This is UI related information that is needed for each entity. It needs to be persisted yet it doesn't feel right to store it in the model. Are there mirroring structures in the VM that just include UI information needed for each model entry?
Christina Bharara
The tutorial uses "playerMoveCount" (in method makeMove etc), but the Game class' method is moveCountForPlayer.
Dino Bartosak
You are correct! Issue was that the .zip versions of the project on Bitbucket were not pointing to the latest commits. Links to download 'Starter' and 'Finished' projects are updated now. Thanks for pointing this out!
Dino Bartosak
My goal is to never reference UIKit classes in the ViewModel. ViewModel only imports the Foundation framework. For the example you mentioned I would probably create Color and Font classes (NSObject subclass) or structs and put them as properties on the ViewModel (color, font). When it is time for the View to actually use these values, View can use *UIUtils file which would contain the mappings from-to UIColor<-->Color UIFont<-->Font Or if you don't need support for ALL fonts and colors and you are only defining few of them for the app, lets say "blue", "green", "gray", "black" colors to chose from, and "small", "bold" fonts, then I would create an enum of those values and use it inside the ViewModel. And when it is time to use it in the View, call Utils method which has a switch clause in it and which maps the enums into the UIFont/UIColor. But it depends on the project.
Vikramjeet Singh
Hi Dino Great article. However, I would like to know the reason you preferred creating a separate protocol for each ViewModel. What is the advantage of that over just instantiating ViewModel inside the ViewController? To me, If we can have a generic protocol that every ViewModel would conform to, that would make more sense than having separate protocol for every ViewModel.
Dino Bartosak
Thank you. No special reason for creating a separate protocol, manly to demonstrate one of the ways on how to implement it. If your app does not make use of it (e.g. if you don't have a need to instantiate ViewModel from some other objects different from the Game object, then it is ok to have only NSObject ViewModel without the protocol) Having a generic ViewModel can be helpful to extract some common functionality but you would still need a separate ViewModel for each View since each ViewModel is designed for a special view.
abdul rahman
this is one of the best website of the world that provides free and latest all computer softwares/applications to its users. All softwares of needyfile are virus free, spyfree. You can download any software just in one click http://www.needyfile.com/
Bluesman
Hi Dino, this is a really great post about MVVM! Every example I've found in the past only explains how to get data from the model to the UI. They never seem to explain how you get user input from the UI to the model.Could you please explain a little more about how the .bind and .bindAndFire methods work? In particular, the .bind function doesn't seem to ever be called. I've stepped through the code of the finished project and it never stops at a breakpoint I've set there.
Dino Bartosak
Thank you! The Dynamic object is just a wrapper around a value (Int, String, [String], Bool etc.). There can also be a listener of this Dynamic object so that each time Dynamic.value changes, listener will be notified. Listener in our case is the View (UIViewController) and if this View wants to listen for changes of a particular Dynamic, it needs to "bind" itself as a listener of that Dynamic object. This is achieved through these two methods of the Dynamic object: .bind and .bindAndFire These methods are almost the same, they both are used to assign "listener" to the "dynamic" and they both receive closure, which is the listener closure that will be defined in the View. The only difference is that .bind will attach listener to the Dynamic but won't call listener callback until Dynamic.value is changed. .bindAndFire will attach listener and call listener once immediately so that you can setup your initial UI values. When Dynamic.value changes, listener will be called again. Where exactly have you put the the breakpoints? I checked the demo app and can I can see that .bind is never used in this example, only .bindAndFire.
Bluesman
HI Dino, I figured out it didn't do anything by putting a breakpoint on the 'self.listener = listener' line of the .bind function. I have one other related question for you. What value does the '$0' represent in the closures? Is it the Dynamic value that called the close? In other words, in 'viewModel.score.bindAndFire { [unowned self] in self.scoreLabel.text = $0 }', does $0 = 'viewModel.score'?
Dino Bartosak
$0 in those closures represents the value property of the Dynamic object (the Dynamic.value property). So in the case which you mentioned, $0 == viewModel.score.value.
Bluesman
Dino, this tutorial has been so helpful. Thanks again for posting it. There's one piece of the puzzle that's missing for me - persisting data. if you needed to persist data to Core Data, where would you typically do that in an MVVM pattern? Let's say you want to save the Game information so your Game object is an NSManagedObject. We don't want our view controllers to have any idea what Core Data is so they shouldn't save the context in which Game object lives. It doesn't seem like the view model should be doing that kind of thing either. So that leaves the object itself or perhaps a service class that handles all Core Data actions (like I have in my app). My idea is to have my view model call a function in my data handler class (let's call it 'CoreDataHelper') and pass the model to the function to save its context. What do you think of that approach?
Dino Bartosak
I would first advise you not to use CoreData or at least use it safely and only features you need. I did not try Realm database but I've heard it has much cleaner API than CoreData, I'll check that library soon. The main thing that I don't like about CoreData is its restriction that objects can only be manipulated on a thread where its context was initialized and also the fact that anyone can always grab a reference on the context (NSManagedObject.managedContext) and call save. The thing you are describing would fit nicely into this architecture, having the separate service for CoreData API (CoreDataHelper or CoreDataService). I would go that road with this architecture if I would have to use CoreData (or any other persistence library). But I would not have the Game object be a subclass of NSManagedObject. CoreDataService (or CoreDataHelper) could be a place where objects are saved/restored from the file and this class would be working with NSManagedObjects but the output API of that class would be mapped to pure NSObjects (like the Game object in this example). That way CoreData is pushed lower on the dependencies line. So e.g. when you fetch all objects or object by identifier, you just map the properties of NSManagedObject to the NSObject properties (or some struct) and return those. All CoreData things should be hidden from the public.
Bluesman
Thank you for the advise, Dino. That's a very interesting approach to using CoreData you've suggested. I hadn't thought of hiding Core Data entirely like that but it sounds like a pretty good idea.
Bluesman
Hi Dino, I've used what I've learned from this article and I finally got it to work. I learned so much from working through this on my project so thank you very much again. But of course, I have another question. In my app, the user starts on view controller A that has a view model and that view model has a model object and I have it all wired up using your method taught here. On this view controller is a button and the button title comes from a property of the model object (which happens to be another object's property). The user can tap it to go to view controller B where they can make a selection from a list (UITableView), and the selection they make changes the value of that property (to a different object). View controller B is dismissed after the selection is made and the button title changes to reflect the change that was made to the object's property (using the property from the new object the user selected). I've gotten it to work, but I'm pretty sure the way I did it is a terrible hack plus, when view controller B is dismissed, you can see the button title change from the old value to the new value on view controller A. Here's how I've done it: 1. User taps the button and in prepareForSegue I create a view model for view controller B by passing the model object to it. 2. When the user makes a selection from the list on view controller B, the didSelectRowAtIndexPath method calls a method on the view model which then calls a method on the model object to update itself with the selection. 3. After that (still in didSelectRowAtIndexPath) the view controller's delegate is called telling it to dismiss the view controller like this: func selectionDidFinish(controller: UIViewController) { self.dismissViewControllerAnimated(true, completion: nil) viewModel.refreshUI() <-- this essential does what your fillUI() method does } I hope that's easy enough to understand. Unlike your score keeper example app that has nested view controllers, mine has multiple view controllers the user can transition to and I'm not sure how to properly use your concepts when I have to pass an object between view controllers and the make the view controllers refresh themselves at the right times. If you have any words of advice I'd really appreciate your thoughts. Thanks
Dino Bartosak
The issue with UIButton title being animated during the dismissal can be avoided if you use .custom buttonType or something like this: http://stackoverflow.com/questions/18946490/how-to-stop-unwanted-uibutton-animation-on-title-change As for the approach itself, I think it is ok what you did. I would only change the name of the viewModel.refreshUI() method to reflect what it really does. If I understood it correctly from your info, something like 'viewModel.selectTitle()/selectData()/selectX()', you would be better to judge this.
Bluesman
Thanks for the tips, Dino!
comments powered by Disqus
Subscribe
The #1 Blog for Engineers
Get the latest content first.
No spam. Just great engineering posts.
The #1 Blog for Engineers
Get the latest content first.
Thank you for subscribing!
You can edit your subscription preferences here.
Trending articles
Relevant Technologies
About the author
Dino Bartošak
Objective-C Developer
As a software engineer, Dino has specialized his skills in building mobile apps. He has worked with teams of various sizes and developed skills in building products iteratively, developed analytical abilities and prototyping techniques. He likes clean, modular, and maintainable code written to be reliable in all conditions. iOS is his platform of choice.