This article presents you how to apply the MVVM pattern in DummyMap application you have developed in previous tutorial. To do this, you’re going to refactor the FeedVC class by using Facade pattern to hidden complexity of binding UI process and MVVM to help slim down the feed view controller by delegating some view controller’s tasks to a view model. Finally you will have new code easier to read and smoother binding process.
Getting Started
MVVM(model-view-viewModel) is an architecture pattern that is an alternative to MVC(model-view-controller). The current version of FeedVC class is written in tradition MVC pattern, the image below illustrates the main components of the MVC pattern:
This pattern separates the UI into the Model that represents the application state, the View, which in turn is composed of UI controls, and a Controller that handles user interactions and updates the model accordingly.
The problem with MVC in project is that it’s quite confusing. The networking, persistent, binding data code are all placed in view controller class cause massive code. MVVM, sometimes referred to as presentation model, offers a way to organize code that solve these issues.
The core of this pattern is viewModel. This file holds the values to be presented in your view. The logic to format the values to be present is placed in the viewModel. In the context of current DummyMap app, it make a advantage that you can format a viewModel from multiple source of model. You will find out later in this article.
The relationships between the three components of the MVVM pattern are simpler than the MVC equivalents, following these strict rules:
- The View has a reference to the ViewModel, but not vice-versa.
- The ViewModel has a reference to the Model, but not vice-versa.
The aims of this part are to refactor feed tab with MVVM and move networking, persistent code to outside FeedVC class.
Improve loading process
Build and run starter project. You can see the problem with current loading process is that on the first launch, app is freezing and slow. The first launch, app have to load data from server and convert return json to Core Data object then present to UI. After first launch, app load data much faster because data is saved in Core Data, and app fetch data and present it. The following activity diagram show the current loading process:
To improve it, you’re going to use MVVM pattern. The improved process is presented in the following diagram:
In new process, the json data will be convert to Data Model and parsing json to Core Data object will implement in background on the first launch. This process make data display much faster and app runs smoother than old one.
To archive this, you’re going to use Facade pattern to refactor FeedVC class. Facade pattern defines an simpler interface to an complex subsystem. You will divide FeedVC into some subsystems. The following sequence diagram will present how to use Facade pattern in this case:
- FeedVC implements only view logic .
- DataManager is Facade class. It provides a interface to use another subsystems.
- ApiClient implements requesting server to get json data and parsing json to Core Data object.
- PersistenceManager implements Core Data tasks like parsing json, clean data, fetching.
- RestaurantDataModel stores restaurant’s information and will be use to create view model
PersistenceManager
First, you need to add new Data Model struct to hold the restaurant’s information. Go to File\New\File…, select iOS\Source\Swift File template and click Next. Name the file RestaurantDataModel and click Create to save the file. Add the following contents:
1 | import Foundation |
Next, Go to File\New\File…, select iOS\Source\Swift File template and click Next. Name the file PersistenceManager and click Create to save the file. Add the following contents:
1 | import UIKit |
You can realize they are the functions you use in FeedVC class. The parseJsonData(input: [[String:Any]]) method has been add the following block:
1 | coreDataStack.storeContainer.performBackgroundTask { (context) in |
The adding new Core data and saving is performed in background thread. It make sure UI is not freezing during the process like before.
The class func fetchRestaurantModelList(completion: @escaping ([RestaurantDataModel]) -> Void) allows directly fetch and covert Core data object to Data Model.
ApiClient
This class just implements request server to get Json data. Go to File\New\File…, select iOS\Source\Swift File template and click Next. Name the file ApiClient and click Create to save the file. Add the following contents:
1 | import UIKit |
DataManager
This class is Facade class. It use to class 2 below subsystems. Go to File\New\File…, select iOS\Source\Swift File template and click Next. Name the file DataManager and click Create to save the file. Add the following contents:
1 | import UIKit |
There are 2 methods in this class.
- loadData() : It check if app is first load, It will call ApiClient to request server, otherwise it will call PersistenceManager to fetch Data Model.
- reloadData() : It will delete all data in Core Data and call ApiClient* to request new data from server.
Refactoring feedVC
Firstly, change the tint color of navigation bar to make app look nicer. Open Main.storyboard, select Navigation Bar in FeedVC‘s Navigation Controller.
Then, open Attributes Inspector and change Bar tint field like following:
Secondly, add a Bar Button Item to FeedVC by drag-drop it from Object library to FeedVC navigation item in storyboard, select button and open Attributes Inspector, set System item to Refresh. Your FeedVC should look like this:
Thirdly, it’s time to create your feed view model. Go to File\New\File…, select iOS\Source\Swift File template and click Next. Name the file FeedViewModel and click Create to save the file. Add the following contents:
1 | import Foundation |
Also, create detail view model with the same method in file DetailViewModel.swift:
1 | import Foundation |
The final step is refactoring FeedVC class. There are 2 sub-step
- Remove all code related to connect to server and parsing Core data. You’ve completed it in previous numberOfSections
- Using view model to become data source of collection view in FeedVC
After that, your FeedVC should look like this:
1 | import UIKit |
Open Main.storyboard, select refresh bar button in FeedVC, open Connections Inspector drag Send Action field to refreshBtnClicked() function. Now you have finished refactoring FeedVC.
Also, you refactor RestaurantDetailVC class by adding DetailViewModel*. Replace following code to the file:
1 | import UIKit |
Run and build project, you should see the result like this:
Conclusions
In this article, the project has been refactored with MVVM. Overall, the project works similar the starter project, you only add refresh function and change navigation bar tint color but you can see that app works much faster than before. Combine with Facade pattern , project has been divided to small sub-system that easier to maintain.
Here is the final project of this part.