MVVM(Model-View-ViewModel) with Swift application 3/3

This is the last post of creating an MVVM (Model-View-ViewModel) application with Swift series. In the first and second part, we went through the basic principles of MVVM, covered data binding pattern and also handled presenting errors to the user. At the end of the second post, we were able to download and present friend data to the user using table view. This last post adds functionality to the application. We’ll create, update and delete users from the backend and we will also pass a ViewModel into View when we are updating friends information.

As you might have guessed, we will continue to use the FriendService built 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: Friend. It contains complete code for the Friend application. But without further ado let’s get to it!

Adding a new Friend

Open the main storyboard, add a new view controller to the project and make it look similar to the one in the picture below:

ViewModel for AddFriendViewController is created in prepareForSeque

Now add a new file inside the ViewModel folder, name it FriendViewModel and put the code below in it:

FriendViewController is going to be used both for adding and updating friend information. By defining a protocol you can have two different view models conforming that protocol and view controller only needs FriendViewModel as a reference type. The view models share all the other variables except for the title and for the submitFriend function which will have different implementations based on the task at hand. Next, continue to implement the AddFriendViewModel class by adding the following code below the FriendViewModel:

FriendViewModel code

Title variable works as a title for our view and this case it returns “Add friend”. The rest of the string variables are pretty self-explanatory except for the didSet part. Every time a user inputs data to the form a validation function is called. Inside validateInput function all the strings, that can be modified by the user, is checked that it contains more than 1 character and then set the validInputData variable accordingly. If the value is different than the previous one, FriendViewController is triggered inside the didSet block to update the submission button state. This will prevent the user from sending invalid data to the backend. One thing to mention here is that you can define a new rule for the validInput data if you want to be more specific about what information the fields should have. For now, this example will only use the simple check that the fields should have a string that contains more than one character.

 

Other than those there is also showLoadingHud, which is used to indicate that information is being sent to the backend, navigateBack function which triggers returning to the previous view and also onShowError function which will present errorDialog to the user. View model also has its own instance of AppServerClient which handles networking and the actual data sending happens in the submitFriend function. At the end of the file, there is a private extension for mapping error codes to messages that can be shown to the user. The code should look pretty familiar because all the basic concepts here are pretty much repetitions of similar things in the FriendTableViewViewModel. The only new thing is the SingleButtonAlert which will be covered next.

SingleButtonAlert

SingleButtonAlert is a helper struct for presenting alert dialog with a single button. With this helper struct, dialog creation can be handled mostly in view model side. Create a new file called Alert inside the Helpers folder and put the code below in it:

AlertAction defines button title and handler. Button title defines the title for the button and handler is a function which will be called after the user presses that button. SingleButtonAlert defines title and message for the alert dialog and it also has the AlertAction as the button title and tap handler.

 

Now open the FriendViewModel again and look the failure block inside submitFriend function. Here you create a new SingleAlertAction with correct texts and also define a response block for the button. Now when the user presses the “OK” button in the dialog, the code block defined in the failure will be called. This case the error dialog only dismisses itself and no further actions are needed. Here “Ok pressed!” is printed for demonstration. If you’d need to react for the button press, for example by reloading data etc, you’d put the reload function call here.

 

Send a new friend to the backend

Open AppServerClient and implement the following code:

Again this all looks very familiar since you have done it once before. The biggest difference here is the EmptyResult which is used because the server doesn’t send any JSON as a response from a successful post. Other than that, at the beginning of the postFriend function dictionary containing keys and values are created for the friend and it is passed as a parameter in the Alamofire request. One thing to notice here is that the method in the call is defined as “post” when previously when you were listing the friend’s method, it was defined as “get”.

 

Now all that is left to get this code to compile, is to add that EmptyResult enumeration. Open Result file in the Helpers folder and add the following code to it:

EmptyResult is almost the same as the Result enum defined before, but now success case doesn’t have any parameter defined. Now that the view model side is ready it is time to move on to the view side.

 

FriendViewController

FriendViewController works as a view for the friend creation part of the application. Create a new file inside the ViewController folder and put the code below inside it:

At the beginning of the file, all the outlets for the text fields and the button are defined. The most interesting thing here is once again the didSet blocks. When outlets are created FriendViewController is set as the delegate for the text fields. Also, a textFieldDidChange function is defined for all the text fields so that every time user types in something, the view controller will catch the change. As you can see inside the textFieldDidChange functions the view model variables are always updated according to the input provided by the user.

 

When you look at the bottom of the file you’ll see an extension which conforms to UITextFieldDelegate. Here the activeTextField is updated according to the active field. This allows you to dismiss the keyboard when it is open and the user taps the screen outside the text fields. Next focus on the other extension which is just above the UITextFieldDelegate. You’ll see that the actions for both the rootViewTapped and the button press are handled here. Open storyboard and connect the UIButton action to the submitButtonTapped (which will then call view model submitFriend function), and then create the UITabGestureRecognizer according to the image below.

 

Add UITabGestureRecognizer

You can just drag and drop the UITabGestureRecognizer inside the view controller and then set the outlet, action, and delegate according to the image. Now every time the user taps outside the fields, resignFirstResponder is called for the active text field and keyboard is dismissed. Now that the view is properly set up and all the actions and outlets are connected (you might want to double check if you have doubts), its time to focus on the connection between the view model and the view.

Connecting the ViewModel to the View

View model type is defined as the FriendViewModel protocol. This is because the same view controller will also be used for updating friend information. At that point, a new view model is defined called UpdateFriendViewModel which conforms to the same FriendViewModel protocol. The view model is always created when we trigger a segue to open FriendViewController, and set as the friend view controllers view model.

 

BindViewModel function looks pretty familiar, but there are few things that need further explanation. At first, if friend texts are available, the values are updated to the UI. This actually happens only if friend information is being updated, so the strings are always cleared at this state of the implementation. Also, the title for the view is set according to the title set inside the view model.

 

ShowLoadingHud part is familiar from the previous view implementation, it only presents and hides the hud according to network request activity. Also, updateSubmitButtonState is pretty self-explanatory, the submitButton state is set inside it, but the last two code blocks need a little bit more thorough explanation.

Navigate back

navigateBack is a function, which is called from the view model when a new user is successfully created, and after that user is returned straight back to the list of friends. Inside the function, the navigation controller pops the current view controller from the view stack, which returns the user to the previous view. Before the view is dismissed, the updateFriends function is called. You don’t want friend list to be updated every time you return back to it, as it is implemented at the moment (viewWillAppear is calling getFriends), so you need something to inform that there is new information available in the backend. When the segue for opening the FriendViewController is triggered, updateFriends function is defined, which then triggers the friend data updating. That will be covered right after the friend adding is completed, so you have to wait a little bit longer.

Presenting error dialog to the user

The last thing inside the bindViewModel function is the onShowError function. Here you create an UIAlertController and use the variables from the SingleButtonAlert which is created in the view model. You get the title and message from the SingleButtonAlerts title and message variables, and for the button, you can set the AlertActions title and handler. This way it is easy to handle the button press inside view model.

 

Things are looking great! Now that AddFriendViewModel and FriendViewController is completed, it is time to make the changes for FriendTableViewController.

 

Open the AddFriendView

Open the storyboard file again and if you haven’t done so, add the barButtonItem to the top right corner of the FriendTableViewController:

ViewModel for FriendViewController is created in prepareForSeque

Next add a new segue from the button to the FriendViewController by holding ctlr, then drag the mouse cursor over to the FriendViewController and select show as the type for the segue. Inside the attributes inspector give this segue an identifier called “friendsToAddFriend”. Now open FriendTableViewController file again and put the code below in it:

Inside the prepare for segue function (which is called by the system every time a segue is triggered), you first check that the identifier for the segue is the one defined a moment ago. After that make sure that the destination view controller is the type of FriendViewController and if that is the case create the AddFriendViewModel and set it as the view model for the FriendViewController. Here you also define the updateFriends function. Set it as closure, remember to add a capture list and set self-reference as weak, and define that getFriends is called when the block is run. The last thing to do is to remove the getFriends call from the viewWillAppear function, and now friend data is only updated if new data is actually available. You actually should not have anything inside the viewWillAppear function so the implementation can be removed completely.

 

Now you can run and test the code. Open the AddFriendView and type some information to the form. You can see the submit button active only after all the fields are correctly filled, and after you press the submit button the information is sent to the server. The app returns to the previous view after the request is completed, which then updates the friend information, that now also holds the information you just added. Now that’s what you can call a successful first run! Now it’s the time when you can give your self a small tap in your shoulder for all the good work you have done so far. You might also want to call a friend and explain to her how exceptionally good person you are!

 

Updating a friend

Now that you are able to add new friends, the next step is to update friend information. Create a file called UpdateFriendViewModel and put the code below in it:

UpdateFriendViewModel conforms to FriendViewModel protocol so most of the code here is very familiar. It has all the same strings, validInputData and functions as the AddFriendViewModel. The only difference here is actually the title implementation which returns “Update friend” string this time and submitFriend function which also has a different implementation, and that now you store an instance of Friend model. Friend model is here because you need to identify the user which information is updated. Inside the submitFriend function you won’t call the postFriend function this time, but instead, a new function in AppServerClient called patchFriend is used. Since all the other things here are more or less familiar, next you will implement the patchFriend function.

Send friend information to the backend

Open AppServerClient and put the code below in it:

Most of the code here is a repetition of previous lines you have written. You create a dictionary containing friend information, handle response and call the completion block with the correct value. One thing to point out here is that in order to identify the user you will pass the id as a parameter in the URL, not inside the parameter object and that the HTTP-method used here is a patch.

 

Now because all the other stuff inside UpdateFriendViewModel is familiar you can move straight to FriendViewController to activate friend updating when the user selects a cell inside the table view.

Opening update friend view

Open the main.storyboard again and ctrl-drag cursor from a cell to the FriendViewController. Once again select “Show” as the segue type, and set the identifier as “friendToUpdateFriend” in the attribute inspector. Now open FriendTableViewController and add the following code inside the prepare for segue:

Now there is a new block of code for handling the segue you just created. First, check that the identifier is a match. Then check that destination view controller is FriendViewController. Next check which table view cell was pressed and then get the matching view model from the friendCell array. Since friendCells holds instances of FriendTableViewCellType use switch to go through the types. The only thing that can be used here is the normal cellViewModels, so the empty and error types need no operations. If the selected cell contains a normal cell, create a new UpdateFriendViewModel and use the viewModel.friendItem as the parameter in the initializer. Next just as it was the case with AddFriendViewModel, define the udpateFriends function so that data is updated when friend data is updated.

 

That is actually all you need to do. Since the FriendViewController is already set up for the FriendViewModel protocol, and the bindViewModel function is called, all is set inside the FriendViewController. Now that was quick! Wasn’t it? Now you can build and run the application, update any friends information, and you should also hear the applause’s at the back of your mind while testing this new functionality!

 

Deleting a friend

The application is almost ready and the last thing to do is to add deleting functionality. While it is really encouraged to always forgive when someone has done wrong to you, there are still some lines that cannot be crossed. Just in case, make sure that the application is ready for a situation that you need to remove someone from your circle of trust.

 

Open FriendsTableViewController and add the following two functions inside the FriendsTableView extension block at the bottom of the file:

The first new delegate function canEdiRowAt tells the table view that it is ok for the user to edit the rows and the second one, editingStyle defines how to react to different editing styles. Inside the editingStyle function, check first that the style is delete and then call a function named deleteFriend which takes the editing row index as a parameter. Now open up FriendTableViewViewModel and next you will implement the deleting function.

At the beginning of the function, you’ll first get the correct friend which is being deleted using the index. Next, as it was the case with the prepareForSegue function, use a switch statement to check the type of the friendCell. You only want to call AppServerClients delete function when the row that is affected is a normal one and also holds a view model, so define normal case so that the AppServerClient is called inside that block. You have to also define a default case so that every case is handled in the switch statement, although the default case will never be called with the current implementation (all other cells isUserInteractionEnabled is set to false).

 

Inside the normal case call the deleteFriend function, which will be implemented in a second. Give it friend id as a parameter so that backend can identify which friend needs to be deleted. After that just handle the result using another switch statement. Inside success block call getFriends to get the latest data from the backend which will then update it to the UI. In the failure case construct and error and present it the same way that was done a moment ago with FriendViewController. You also need to add a definition for the onShowError function, which is exactly the same as in the AddFriendViewModel:

And also remember to add the handler inside the FriendsTableViewControllers bindViewModel like this:

Now there is still one thing that needs to implemented inside the FriendsTableViewViewModel before you can proceed to AppServerClient to add the deletion function. Add the following extension for error handling at the bottom of the file:

Now also the deletion HTTP-error codes are transformed to human-readable form which can be presented to the user.

Deleting friend from the server

Now open the AppServerClient to implement the last method that is needed in the AppServerClient: deleteDriend.

The request is almost identical to the last one, with the exception that there are no parameters and that method is delete. Ones again the userId is used to identifying the user in the server. Now that the delete function is also implemented everything is ready! Run the application and swipe left from one of the cells:

When the cell is swiped left the delete button is shown in the right side of the cell and pressing that will start the deletion of the selected friend. As you see in the picture above Dolph Lungren is the one being deleted for showing such a weak character in the first expendable movie.

 

Application is finished

And that’s it! Now the application is ready. Now it can create, update, delete and list friends. If you have hanged with me this long I really want to thank for reading it through. I also hope that you have learned something and if you have liked the blog, you can subscribe to the mailing list or follow me on Twitter to get frequent updates.

 

I know that unit testing is mentioned many times in the blog post and so far you have not seen a single test written. The MVVM series started getting bigger and bigger so I didn’t want to add the fourth post in it. But no worries, the blog post is available and you can check it here: unit testing view models

This article has 13 comments

  1. Filip M.

    Read your articles about MVVM and it was great help to me to understand this topic and implement into my project. There is a lot of resources on other third part libraries but none of them explain core concept of MVVM explained in code. This is a 5 star article. Thank you a lot for this awesome blog, keep up with good work 🙂 cheers

    • Jimmy

      Thanks Filip!

      I am glad that you liked the post and that I was able to help you 🙂 cheers

  2. Sergey

    Thanks. MVVM Like MVP, but different between theirs that in MVP all logic we holding in Presenter, and UI implementation in ViewController. In MVVM logic and UI implementation we holding in ViewModel.

    If I understand right.

    • Jimmy

      Hi Sergey,

      I haven’t used MVP in any of my projects but what I have understood that is pretty much how it works.

      MVVM is very close to MVP. ViewModel holds all the business logic and ViewController acts as a View and implements the UI. As I said, I am not that familiar with MVP so I cannot tell you what are the fundamental differences between the two, other than the 2 letters in the name 🙂

  3. Michael

    Very good article!!! This helped me a lot in understanding MVVM.

  4. karthik

    thanks for the artical. Hers i had one doubt how to assign AddfriendViewmodel with in the FriendViewController in the example your passing from different controller but i need to assign with in the view controller.

    help me.

    • Jimmy

      Hi Karthik,

      If I understand your problem correctly you can just create a new viewmodel inside your view controller like this:

      final class ViewController: UIViewController {
      let viewModel = AddFriendViewModel()

      }

      Does that work in your situation?

  5. karthik

    hi jimmy it’s working fine,
    how to search the elements in tableview using mvvm , can you please help me out.

    • Jimmy

      Hi Karthik,

      Add a searchbar to the tableview. Then implement the callback methods, in the viewcontroller, that get’s called when ever searchbar input changes. When user types in a new character call a
      ‘sortData(input: String)’ function in the view model from the delegate method. Inside it filter the array that holds your tableview data with the input the user typed.

      Hope this helps! 🙂

  6. suresh

    can you share the example (search )please with your friend application .

    • Jimmy

      I’ll put in on my todo list, but I have many things to write about before that. I think you can find examples about searchbar tableview filtering after a little googling. Just implement the filtering part in the view model side and you should be good to go 🙂

  7. Suresh L

    hi jimmy , it was nice tutorial, i am new to ios development. i am facing issues in search with tableview. can you share any code for search table view with mvvm. i am using your application as reference.

    It will help me lot if you share search with mvvm.

    Thanks