MVVM with Swift application 2/3

This post continues the series of creating a 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 ViewModel and the View without creating unnecessary references.

We’ll continue to use the FriendService build in the first post: Server-side Swift, how to setup 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: https://github.com/JussiSuojanen/friends. It contains complete code for the Friend application so there is more code and functionality than we cover in this post.

ViewModel & View

In MVVM, ViewModel is the one who does the heavy lifting. That is the place where you put all the logic and transform the data so that View can use it. 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 TableView so create a ViewModel and also name it in a way that you instantly know what it is for. It’s a ViewModel that responds to a UITableViewController. FriendsTableViewViewModel has few responsibilities:

  1. It loads friends from friend service.
  2. It updates view to show indicator when 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 user and for that enum FriendTableViewCellType is used.

  1. Normal type cell is used for situation when friend data is successfully fetched from the server. Notice that it also takes a ViewModel 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 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. You might remember that 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 ViewModel class it self. 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 ViewModel can get the friend information.

GetFriends function

Inside getFriends function use 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 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 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 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 first thing to do is to stop the loading indicator and then use switch to go through the result. Remember that Result has a 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 sound, 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 FriendCellViewModels from the data and store them to the friendCells. Since Friend 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 error message to the user.

Now let’s stop for a second and concentrate 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 ViewModel. Create a file called FriendCellViewModel and place it in the ViewModel folder among the other ViewModels 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 the FriendCellViewModel protocol all the protocol variables needs to be implemented.

  1. FriendItem basically just returns the Friend model.
  2. FullName is combination of first & lastname.
  3. PhonenumberText returns the phonenumber information of the Friend 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 to use 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 haven’t been discussed yet: Bindable. How to update the information from the ViewModel 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 ViewModel, it will just make things messy, break the mentality of MVVM, and you’ll loose 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 observer pattern. Bindable is crucial part of MVVM and it takes care of the data state changes between the ViewModel and the View. You could also use some reactive lib such as RxSwift, but it seems a bit heavy for Friend application. 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 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 and it is the same Generic type that was used before. In the values’ didSet function, listener function is called with the new value, so every time the value is changed listener is informed.

In the init function, value is given both type and 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. 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, ViewController is more of the first and less of later. This means ViewController basically works as a View and does as little work as possible as a Controller. It is responsible for updating what ever 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 ViewController 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 ViewController 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 cells class to FriendTableViewCell. In case you are not familiar with AutoLayout it might be best for you to copy the Main.storyboard from the provided source code since autoLayout is 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 corresponds 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 TableViewCell). A nice tip is to name the outlets starting with the type it represents, so all UILabels start with a word label. Later on autocomplete will help you when you try remember the exact name of the label you should use.

FriendTableViewCell also has ViewModel 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 parameter when the cell is created from the UITableViewCell prototype. Every time ViewModel 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 ViewModel 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 few times if the ‘?’ isn’t there.

FriendTableViewController

FriendTableViewController has 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 Views ViewModel right after we define it. Inside the ViewDidLoad call the bindViewModel function, and bind View to the ViewModels 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 ViewModels getFriends function which start the request to the server. This way friend data is always up to date, when the view is opened.

You might noticed that I skipped the showLoadingHud variable. That’s because of a 3rd party component called PKHUD is used to present a hud which has loading indicator in it. Before using it, it has to be added to the project using CocoaPods. You could also use a simple TableViewCell 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, setup 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 ViewModel is set up, and 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 TableView how many cells needs to be created, and the other to create the cells. NumberOfRows in section is simple since you only need to ask from ViewModel, how many cells needs 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 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 cellTypes also holds a value as a parameter which can be access 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 ViewModel to be that cells ViewModel. Remember what happens when FriendTableViewCell ViewModel is set? bindViewModel is called which updates the texts in the outlets, so the cell is correctly set after ViewModel 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 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 ViewModel side, but I wanted a bit of variation between the error and the empty case.

And thats about it! Now when you run the project you should see your friends in a TableView! Next you might want to take a moment and pat yourself on the shoulder.

 

After the well deserved moment of congratulations of 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 getFriends 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 ViewModel 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 13 comments

  1. Adrien Reply

    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 Reply

        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 Reply

    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 Reply

      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 Reply

        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 Reply

          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 Reply

    @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 Reply

      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 Reply

        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 Reply

          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 🙂

Leave a Comment

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