MVVM with Swift application 2/3

This post continues the series of creating an MVVM application with Swift. In the first part, we managed to go through the basic principles of MVVM and also prepare our AppServerClient to get friends information from friendService. We were also able to create the model to which we store the data we receive from the server. In this post, we’ll continue our happy journey in the beautifully structured code of the MVVM pattern and present the friend information to the user. This time our focus is more on the ViewModel and View of MVVM and we will also cover Data Binding pattern with something called Bindable. Bindable is a crucial part of connecting view model and the view without creating unnecessary references.

We’ll continue to use the FriendService build in the first post: Server-side Swift, how to set up a backend. You can either set up the backend at your localhost by following the instructions in the post, or you can use the service I have running on Heroku. For example to list all friends you should use HTTP-get with this URL: http://friendservice.herokuapp.com/listFriend.

You can download the complete source code to the mobile client here: Friends. It contains complete code for the Friend application so there are more code and functionality than we cover in this post.

ViewModel & View

In MVVM, the view model is the one who does the heavy lifting. That is the place where you put all the logic and transform the data so that the view can use it. The view does not need to know what happens under the hood. View only receives correct type data and shows the data on the screen.

FriendsTableViewViewModel

Create a file called FriendsTableViewViewModel. Friends will be listed in a table view so create a view model and also name it in a way that you instantly know what it is for. It’s a view model that responds to a UITableViewController. FriendsTableViewViewModel has few responsibilities:

  1. It loads friends from friend service.
  2. It updates the view to show an indicator when the app is loading.
  3. Processes data from server and updates it to view.
  4. Handles error situations and updates information to view.

FriendTableViewCellType

The first thing FriendsTableViewViewModel defines is an enum called FriendTableViewCellType. You want to have different types of cells to present different types of information to the user and for that enum, FriendTableViewCellType is used.

  1. Normal type cell is used for a situation when friend data is successfully fetched from the server. Notice that it also takes a FriendCellViewModel for the UITableViewCell as a parameter.
  2. Error type cell is used when getting friend information fails for some reason. It has the message string as a parameter which will be used to show the error message to the user.
  3. Empty type cell is for situations when empty JSON array of friends is received.

 

Now, FriendTableViewViewModel has a variable for friendCells which can store any type of cell information and it is defined as Bindable[rad-hl]. You might remember that [rad-hl]Bindable is the type which handles the data binding between the ViewModel and the View, that pattern is covered in a second. But first, let’s concentrate on the view model class itself. FriendsTableViewViewModel also has another Bindable variable called showLoadingHud, which is used to tell the view to present loading indicator to the user. Lastly, an instance of AppServerClient is created so that view model can get the friend information.

GetFriends function

Inside the getFriends function uses the AppServerClient to fetch friend information. When loading is started the first thing to do is to update loading status to screen so that user doesn’t think that UI is jammed. For that the showLoadingHud is available. Next thing to do is to call the getFriends function with the correct parameter which means that a completion block needs to be defined right after the function call.

Remember that completion parameter is defined as @escaping? Now it’s the time handle it. A capture list needs to be defined which contains a single object: [weak self]. This means if the FriendViewModel gets released from memory before getFriend has returned, it doesn’t have a strong reference to FriendViewModel object inside the closure. This would prevent FriendViewModel from being deallocated and boom! A memory allocation issue is introduced. The situation might, for example, occur when a user opens a view which starts to load some information, and immediately returns to the previous view before the async loading request is completed.

Inside the completion block, the first thing to do is to stop the loading indicator and then use a switch to go through the result. Remember that Resulthas success and a failure case and those can be used here to define if an error needs to be presented or friend information needs to be updated to the view.

In case the request was a success, first check that the response contains information about friends. If the friend count is zero you don’t want to show an empty screen. Instead, inform the user that he doesn’t have any friends (The situation isn’t actually as bad as it sounds, truth is that the user just hasn’t
added any friend information to the friendService yet.). That is accomplished by setting the friendCells type value to empty.

If the request was successful and there is friend data available, create an array of FriendCellViewModelsfrom the data and store them to the friendCells. Since Friend model soon conforms to FriendCellViewModel protocol, the data from backend can be converted to CellViewModels with a single cast inside the flatMap closure. Oh and don’t worry FriendCellViewModel is covered in a minute!

If however, the request was a failure, use the error type cell to present an error message to the user.

Now let’s stop for a second and concentrate on how to convert GetFriendFailureReason to human readable messages that suites this particular view.

Error handling

As mentioned before, you might want to present different texts depending on which view is currently active when an error occurs. One way to handle is to create a file private extension for GetFriendsFailureReason and define getErrorMessage function. The reason to create the extension as fileprivate, is because we only want the texts to be used inside FriendsTableViewViewModel.

Now that the extension is defined you can call getErrorMessage for the error variable and the GetFailureReason, created from the HTTP-status code, is converted to human readable error text. I know there isn’t any authorization in the app, (not yet at least) so the unauthorized case is there for demonstration.

FriendCellViewModel

Let’s quickly go through the cells view model. Create a file called FriendCellViewModel and place it in the view model folder among the other view models of the project. Define the cell as protocol and then make Friend model conform to that protocol.

FriendCellViewModel has three variables: friendItem, fullName, phonenumberText. To make Friend model conform to the FriendCellViewModel protocol all the protocol variables needs to be implemented.

  1. friendItem basically just returns the Friend model.
  2. fullName is a combination of first & last name.
  3. phonenumberText returns the phone number information of the Frien model.

Since the FriendCell is simple and only displays information, it’s good to define it as a protocol and then make the model conform to that protocol. If however, the cell would have a lot of functionality, for example expanding on a click, a button which press needs to be handled, then I would suggest using a struct or a class. But in this case, the simple protocol is a good way forward.

Now almost all the components FriendTableViewViewModel needs are covered, but before we go to the view, there is one important part that hasn’t been discussed yet: Bindable. How to update the information from the view model to the view so that view is always up to date with the information it’s presenting. You don’t want to keep a reference to the view inside the view model, it will just make things messy, break the mentality of MVVM, and you’ll lose the benefits of decoupling the view and the logic (hard to unit test, dependency etc.). This is where Bindable comes to save the day.

Bindable

Bindable is just a simple way to start observing a value for changes. It is implemented using an observer pattern. Bindable is a crucial part of MVVM and it takes care of the data state changes between the view model and the view. You could also use some reactive lib such as RxSwift, I also have a tutorial about how to use it, but it is not in the scope of this tutorial. If you want to learn more about it check you’ll find the tutorial here: how to use RxSwift with MVVM. Create a new file called Bindable.swift and copy the code below to the file.

First typealias Listener is defined, which is just a function that takes a Generic type as a parameter which means that it can be anything. Also, a variable named listener is defined which is the type of Listener and an optional. Next parameter is called value[(rad-hl] and it is the same Generic type that was used before. In the values’ [rad-hl]didSet function, listener function is called with the new value, so every time the value is changed listener is informed.

In the init function, the value is given both type and the starting value. Notice that if T is defined as an optional the parameter can be nil. Bindable also has 2 functions: bind & bindAndFire. These are the functions that are going to be used inside the view. The view starts to listen to a value by calling bind or bindAndFire and defines the closure right after it. Now that the observer is set, every time Bindable value is changed view is notified and it can update the data according to the change.

The difference between bind & bindAndFire, is that bindAndFire also returns the starting value of the Bindable while bind only returns the value the first time it changes.

View – FriendsTableViewController & FriendTableViewCell

In MVVM, view controller is more of the first and less of later. This means view controller basically works as a view and does as little work as possible as a controller. It is responsible for update whatever information ViewModel provides it to the UI.

This tutorial isn’t really about storyboard so I won’t go through that part in too much detail. Open your Main.storyboard file, delete the empty view controller xCode created and drop the navigation controller in it. The default navigation controller has UITableViewController as the root view controller so you only need to set the class of that view controller to FriendsTableViewController.

 

Give FriendsTableViewController title ‘Friends’ and also create one prototype UITableViewCell. In the cell create the labels shown in the picture above and set the cell’s class to FriendTableViewCell. In case you are not familiar with the  autolayout it might be best for you to copy the Main.storyboard from the provided source code since autolayout is a pretty wide topic and deserves an own blog post to really go through it. If you are familiar with autolayout set the constraints so that the cell looks pretty similar to the one in the picture.

FriendTableViewCell

FriendTableViewCell presents the information of a single friend to the user. Create a class named FriendTableViewCell and put the code below in it.

In FriendTableViewCell create two outlets which correspond to the labels you just created in the storyboard: labelFullName & labelPhoneNumber. Now go back to storyboard and connect those outlets to the correct ones in the storyboard TableViewCell (Remember to set FriendTableViewCell as the custom class for the table view cell). A nice tip is to name the outlets starting with the type it represents, so all [rad-hlUILabels[/rad-hl] start with a word label. Later on, autocomplete will help you when you try to remember the exact name of the label you should use.

FriendTableViewCell also has view model as a variable. It’s the now familiar FriendCellViewModel and it’s defined as an optional so that we don’t have to give it as a parameter when the cell is created from the UITableViewCell prototype. Every time view model is set, bindViewModel function is called which updates information to the UI. We don’t need the Bindable here since the information won’t change until the cell is reused, and in that case, a new view model is set.

Notice that even if the outlets are defined using! we are using the ? operand here to make sure we won’t end up with a nil pointer when the cells are reused. Not exactly sure why it happens but I’ve managed to crash an application a few times if the ? isn’t there.

FriendTableViewController

FriendTableViewController has a few more things to do than a single cell. It creates all the UITableViewCells and also presents loading hud and some errors. Again start by creating a file named FriendTableViewController and put the code below in it.

Since FriendTableViewController is the first view in our application create the view model right after we define it. Inside the ViewDidLoad call the bindViewModel function, and bind view to the view models values. Now, this is the place to use the Bindable for the first time. Call bindAndFire and define the listener as the closure that comes right after it. Here you also need to use the capture list so that you don’t create a reference cycle. Now every time cells are changed inside the friendCell array view is informed and it will reload the cells.

Inside the viewWillAppear function call view models getFriends function which starts the request to the server. This way friend data is always up to date when the view is opened.

You might notice that I skipped the showLoadingHud variable. That’s because of a 3rd party component called PKHUD is used to present a hud which has a loading indicator in it. Before using it, it has to be added to the project using CocoaPods. You could also use a simple UITableViewCell which has a loading indicator, but when more functionality is added, you want the cells to be visible behind the indicator.

Close xCode, open Podfile and add the line below in it. Then run pod install and open the project again.

After that set up the closure to react to changes in the showLoadingHud value: presenting hud when it should be visible, and hiding it when it should be hidden.

Now the communication between the view and view model is set up, and the next step is to create the UITableViewCells.

Creating UITableViewCells

Next, you should implement the UITableView delegate methods or the two of them which is needed at this point. First one to tell the table view how many cells needs to be created, and the other to create the cells. numberOfRows in the section is simple since you only need to ask from the view model, how many cells need to be created. Just use friendCell value, and since it is an array, count method can be used to get the count of the objects inside.

cellForAtIndexPath has a bit more code and here you see the FriendTableViewType enumeration in action. Use a switch statement to access the friendCells value and get the correct index from the array using the indextPath row. Now that you have the correct cell you can check which type of information it holds inside. Implement all three cases: .normal, .error and .empty. As you might remember the normal and error cell types also holds value as a parameter which can be accessed by simple defining it inside the brackets as you see above.

In normal cell case create a cell from prototypes with identifier friendCell and cast it to FriendTableViewCell. If the result is a success set the view model to be that cells view model. Remember what happens when FriendTableViewCell view model is set? bindViewModel is called which updates the texts in the outlets, so the cell is correctly set after view model is set. In the last line just return the cell.

In error cell case create a standard UITableViewCell. You can use UITableViewCells default textLabel outlet and set the error message defined as a parameter as the text which will be shown to the user.

The empty case is pretty similar to error, the only difference being that it uses hardcoded text inside the case. Now you could move also the empty cells’ text to view model side, but I wanted a bit of variation between the error and the empty case.

And that’s about it! Now when you run the project you should see your friends in a table view! Next, you might want to take a moment and pat yourself on the shoulder.

 

After the well deserved moment of congratulations on the good job you have done so far, you might still want to test the empty friend data case. The easiest way to do this is to modify AppServerClients [rad-hlgetFriends[/rad-hl] function success case to look like this:

After the loading indicator hud disappears you will see the empty cell.

The error cell case is easy to test with Macs Network link conditioner. Just open it using spotlight search (cmd + space), start typing network link conditioner and then hit enter. Now select 100% loss and open run the app again. Notice that after a while you’ll get the error cell informing: “Loading failed, check network connection”.

This completes the second part of the MVVM with Swift application series. Last part of this blog series will add a bit more functionality: create, delete and update a friend. You will also pass a view model to a view when getting ready to update friends information to the server.

Thank you for reading and I’ll see you on the last part of the series. Until next time!

This article has 35 comments

  1. Adrien

    First of all thanks for your tutorial. A real life example is the best explanation you can provide.
    In my case I use Parse and I just want to list a collection of objects. Suppose that each object has a cover field (which is a file, an image most specficialy) and I want to display it in each cell, how would you handle the download ? In the ViewModel, the view ?

    This is a way of fetching the content : https://github.com/parse-community/Parse-SDK-iOS-OSX/issues/1093

      • Jimmy

        Hi Adrien,

        That is a good question! I think in this case I’d put the image loading inside the view. But I’d create an ImageLoading class that handles the image loading. That way you can keep the view layer cleaner and set the image using code similar to this:

        coverImage.image = ImageLoader().imageFrom(file: viewModel.cover)

        Another possibility would be to create an extension for UIImageView and put imageFrom in there. This way could set the image like this:

        coverImage.setImageFrom(file: viewModel?.cover)

        I think I’d go with the UIImageView extension since it just looks better and fits nicely in the view.

  2. Adrien

    Thanks a lot I’ll try to implement it with the extension. Should I display the HUD loader while I fetch the content ? if yes can I add it in this extension ?

    I had another issue with this PKHUD package. I’m not able to display the HUD loading while I fetch the content because the lib crashes.
    I created an issue in case of we need to update the code : https://github.com/pkluz/PKHUD/issues/216

    • Jimmy

      I don’t think you should use the HUD since you are loading the image for one cell. When you scroll down the list the HUD would always popup again if new image is needed.

      You could instead use UIActivityIndicatorView placed on top of the image and set it visible and active while fetching the image. If you implement a callback for the UIImageView extensions setImageFrom(file: PFFile) like this:

      setImageFrom(file: PFFile, completed: @escaping () -> Void)

      you can start the indicator before calling the function and stop it after the image is received or fetching failed.

      I am not sure if it is necessary to implement the indicator for the image loading. Experiment how the list feels using poor network connectivity and then decide if you need to do it or not. The indicator might even make the user experience worse 🙂

      • Adrien

        Perfect thanks for your explanations concerning the images loading.

        Concerning the crash of was talking about the display the HUD while you fetch the content from the API before rendering the table view.
        I posted a screenshot in my issue (https://github.com/pkluz/PKHUD/issues/216).
        With the new version (5) it seems that the code does not work anymore but I would like to have your opinion first 🙂

        ps: The UIImageView extension works like a charm. My code is cleaner ! Very nice tips for a swift beginner like me.

        • Jimmy

          Great that you got the UIImageView extension working! I am glad I can help 🙂

          I did reproduce the problem with the PKHUD, but then I updated the Friends project to use version 5.0 and it started working. Try to run “pod install” command again and see if that helps with the issue.

          • Adrien

            Hello sorry for my late answer (and happy new year by the way)

            I’m a little bit surprised it works directly, I should have missed something, did you adapt the code ?

            In my case I use CollectionViewController contrary to you (TableViewController) Can it be the source of the issue ?
            https://gist.github.com/aguidis/1153dcfffd0fbf81d81bd9eda285302f

            Another question, when you handle the enum contained in viewModel.friendCells.value[indexPath.row] there are 3 cases. When it’s “empty” or “error”, you display a custom message in the cell.textLabel?.text but a collection cell doesn’t have a default textLabel property, I thought I could display a “default image” instead, what do you think ?

            sorry if I ask a lot a questions

          • Adrien

            I just created a tableView and I still have the issue :s

  3. Jimmy

    @Adrien

    All questions are welcome, that’s one of the best ways to learn 🙂 It seems however that my blog platform has come to it’s limits with nested question depth, since I can’t directly answer to your last question anymore :D.

    Not really sure what goes wrong with the PKHUD, but I’ve used it also with UICollectionView so I don’t think that is the problem. One approach is to get rid of it completely and replace it with a loading cell. Just create another case in the enum named .loading and set the collection state to that while you are fetching information. Then identify the loading case when creating the cells and create a specific loading cell for that state.

    You can use the same approach with the error cell: Create a specific error cell that has a label outlet and set the text to that. You can also use the image approach, but if you want to present more than 1 kind of error you need a picture for every error state. Incase you plan to release the app to multiple languages, localizing images is not that fun… And coming up with a good image for all the error cases without any text is also a bit of a pain 🙂

    • Adrien

      I have a tons of question about the way to do it or to do that but I don’t want to spam you comments section ! ^^

      I’ll try to delete and re install the PKHUD package, I need to make this work before implement the .loading case.
      Thanks a lot for your time 🙂

      • Adrien

        I finally found a way to display the loader:

        The author explained in this this issue (https://github.com/pkluz/PKHUD/issues/162) that “In the root VC swift UIApplication.shared.keyWindow is nil, so you have to specify manually the view, or show the HUD in viewDidAppear”.

        I don’t why I didn’t try it before, anyway, the solution was to add an argument when calling the show() method: PKHUD.sharedHUD.show(onView: self.view)

        • Jimmy

          Great that it is finally working! I also have to update the Friend project sources so thanks for pointing out this problem!

          Not sure if I have the answer for all your questions, but at least regarding the blog posts I’ll try to help as much as I can 🙂 You can also DM me on twitter and I’ll try to find time to help 🙂

  4. SAMAR

    Hi jimmy ,

    I tried to build friendservice backend on Xcode 9.2. Its is showing me error.can can you update this code with vapor 3.It would be great help for me.

    • Jimmy

      Hi Samar,

      I’ve been planning to update the Friendservice to Vapor 3.0 but I just haven’t find the time for it yet. I’ll definitely try harder now that there is a request for it. I have few things on the pipeline before that but I’ll do it as soon as possible 🙂

      In the meanwhile, I hope you can study the Friend project against the backend running on Heroku.

  5. Vince

    Interesting article. I haven’t finished it yet but one question comes to mind. Why are Model updates sent to the ModelView using Notifications and ModelView updates sent to the View using Binding? Why isn’t the same technique used to pass on both updates?

    • Jimmy

      Hi Vince,

      Thanks! Not sure what you mean that the Model updates are sent ViewModel using notifications? ViewModel only updates the models when it does a new network request and gets new data from the backend. That data is sent back to the ViewModel inside the Result object in the completion block that ViewModel provides for the request.

      Did that answer you question? Or did I misunderstood something?

  6. Loser

    Can you do this Sample App with RxSwift, RxCocoa?

    • Jimmy

      Hi Loser,

      Interesting idea. I thought there is so many examples about RxSwift out there that it doesn’t help that much to write Friends project using it… But then again there is also a lot MVVM examples :). I’ll consider it.. it would be fun to see the difference!

  7. Orkhan

    Hi, thanks for the great article! But I could not understand why do you use bind and bindAndFire. In case of bindAndFire I understood, you change the value there, but in case of bind you only set listener without changing the value. So why to use bind instead of bindAndFire, could you please explain that part?
    Thanks in advance!

    • Jimmy

      Hi Orkhan!

      The difference with bind & bindAndFire is that bindAndFire calls the listener right away with the current value, while bind only sets the listener, and when ever the a new value for that Bindable is set the listener gets notified. So incase you don’t need the default value that you created the bindable with, you can use bind. But I have to admit that I usually go with bindAndFire even if bind would be enough.

  8. Omar

    Can you please help me understand something:

    When viewDidLoad get called, the tableView is populated with results, however shouldn’t the tableView appear empty as the web request to get the tableView data “viewModel.getFriends()” is called after the tableView has been loaded “self?.tableView?.reloadData()”

    • Jimmy

      Hi Omar,

      Yes the tableView actually has zero cells for a little while before friend data is loaded. But it might seem that it is populated straight away if you have good network connection.

      Anyway here is how the binding of the values between VM and VC works:

      All the values that we need in the viewController side are bound inside the bindViewModel(). ViewController starts to listen for changes in “friendCells”, “showLoadingHud” etc. So when the getFriends() call is finished ViewController gets notified in the friendCells.bindAndFire {} closure. And inside it it tells the tableView to reload it’s data.

      I hope was able to answer to your question 🙂

      Cheers,
      Jimmy

      • Omar

        Thank you for your speedy response. That clears a lot of things up but i still don’t understand:

        When getFriends call is finished, how exactly is the reloadData() method called again to display the updated values. You mention that friendCells.bindAndFire {} is used to reload the tableView data but that function only gets called inside bindViewModel(), and bindViewModel() is not called after the getFriends() call is complete?

        Basically once the getFriends() call is complete, how does the viewController know to update the tableView. I don’t see a piece of code telling the tableView to do anything, after the web request is done.

        (Sorry if this sounds like a silly question, i am eager to completely understand this design pattern)

        • Jimmy

          Actually the bindAndFire is a function that gets called every time viewModel sets a new value to friendCells variable which is defined as Bindable. Read the Bindable topic a bit further up. The concept is explained there :). And please ask as many questions you need until you understand the concept, I am glad if I can help you!

          • Omar

            Right i understand. But at what point is the tableView Data Reloaded after the viewModel sets a new value to the friendCells variable?

          • Omar

            Could you show me line of code in the bindable class that tells the tableView to update itself after a new friendCells value has been set?

          • Jimmy

            Everytime a value is set for Bindable this didSet function below is called:

            var value: T {
            didSet {
            listener?(value)
            }
            }

            Inside it it calls the listener -function. That listener is set inside Bindables bindAndFire -function.

            Now look at the view controllers bindViewModel -function. You see that when the bindAndFire -function is called viewController passes the trailing closure as a function parameter. So that is when it defines what block of code needs to be executed every time the value changes. Did you get it? 🙂

  9. Pritha Khasnobis

    Should a view talk to model directly in MVVM? I feel, friendItem (Model object) in FriendCellViewModel should not have a read property. It should not allowed to be used in FriendTableViewCell. Anyone can access the model directly ( such as viewModel?.friendItem.firstname) instead of viewModel?.fullName. Should we not take care of that? Please let us know your vies on this!

    • Jimmy

      Hi Pritha,

      If you are interpreting pure MVVM you are absolutely right. The view shouldn’t know about the model. However, in this case it is very convenient to pass the model also to FriendViewModel which also creates it’s data based on the model. Since the friendItem is read only, I wouldn’t say that this is the worst crime on MVVM pattern. But I have to say, know that you brought this up, I’ll consider doing some refactoring.

      Thanks for a great question!

  10. Stephan Korner

    Thank you very much for this tutorial. I like the concept of having two ViewModels. One question: Shouldn’t you also unbind when cell-reuse happens (i.e. inside “prepareForReuse() method for example” ??? Cell-reuse has the risk of too many bindings if not removed otherwise. Maybe the tableView’s ViewModel can remain but what about the cell’s ViewModel – does it not need to unbind ???

    • Jimmy

      Thanks Stephan!

      Actually there is no binding with cell view model, at least not in the sense of Bindable that we use all around the project. When a new cell is created, view model is set and in the tableViewCell we only read the values from the view model:

      private func bindViewModel() {
      labelFullName?.text = viewModel?.fullName
      labelPhoneNumber?.text = viewModel?.phonenumberText
      }

      Everytime the cell is reused this code is run and the cell is shown with the updated data.

  11. sriharish

    Nice article jimmy. it helps me lot, i am new to ios development directly started with MVVM and Graphql for data . My question is how to handle error and success part in grpahql and one more i want to implement search functionality in tableview.

    can you share any code for search.