MVVM is the architecture we widely adopt here in OverApp when we have to deal with a new project.
I want to discuss our implementation of this pattern and the setup of the related Xcode project.
Numerous variables are involved in the choice of an architectural pattern. The choice is dictated by considerations on the complexity of the project, the ease and cost in terms of time linked to the development of the required features also with a view to maintenance and product evolution. Last but no least, it is also dictated by the experience gained in the field by the development team.
The experience gained with this pattern here in OverApp has allowed us to work on numerous projects of different complexity, convincing us of the quality of the MVVM solution.
MVVM stands for Model, View, ViewModel, a specific architecture where the ViewModel stands between View and Model providing interfaces to connect to UI components. This connection is made by “binding” values, linking logical data to the UI.
Like other architectures, MVVM tries to satisfy at least the following principles
How? Defining the following concepts:
The Model represents the data consumed by the application. A set of structs or classes that define information that would be used (and eventually rendered by a view). They could be eventually recovered through an API or a local DB or generated by the app itself.
The View only consists of visual elements. In the View, we initialize UI controls (such as labels, buttons, tables), setup the layout, perform animations, etc.
The ViewModel provides a set of interfaces, or properties if you prefer, each of which represents an UI component in the View. Interfaces and UI Controls are connected to each other with a technique called “binding”. Doing this, a change in a property of the model is automatically reflected on the UI.
The ViewModel also contains presentational logic. In general it obtains data from the Model and performs all the logic to prepare ready-to-display piece of data that should be presented by the View (for example converting dates into a readable form). Its data flow could be described in this way:
We said that the view updates itself after observing the change of the ViewModel. This is done using a technique called “binding”. How this binding is implemented?
In Swift, we can use different approaches:
Each of the proposed methods have pros and cons. In this article I will use the closure approach. Also the delegate pattern approach is a good choice.
In a hypothetical app that shows a simple picture gallery we could have a very simple Model like the following:
Here is the ViewModel:
Let’s examine it in detail. We have a property:
It contains data about a collection of pictures. It also has a so called property observer: didSet. Property observers observe and respond to changes in a property’s value. Property observers are called every time a property’s value is set, even if the new value is the same as the property’s current value.
So, once we set the value of the picture property a piece of code is executed:
It is a closure. If you go forward and read the rest of the code of the PictureListViewModel you can see that it is defined as:
Let’s take a look at the implementation of our View (our PictureListViewController) to understand how we can set this closure:
The implementation is really simple. We have:
Now let’s discuss about a property called viewModel declared in this way:
We declared our ViewModel using the lazy modifier. This means that the property initial value is not calculated until the first time it is used. The first time the property is used it is instantiated.
Now focus your attention on the implementation of the InitViewModel function called by viewDidLoad, inside we can read:
It assigns a block of code to the property of the ViewModel we defined as:
At then end of the InitViewModel we can see the code:
Whatever is the implementation of the function fetchData in the ViewModel, we expect that it retrieves some sort of data in some way. At the end of the fetching operation we expect to obtain a collection of pictures and that the property “pictures” in our ViewModel is set with the information collected.
Now, put in order the information I provided until now:
Instead of implementing the required code to deal with UITableViews delegates inside the PictureListViewController class or through an extension, we choose to create a separate object that we call adapter. It behaves in a manner similar to the adapters for Android, it implements all the stuff required. By doing this, we write simpler ViewControllers, create specific objects to deal with tables or collection views. The adapter is implemented in this way:
This class implements an extension to conform to UITableViewDelegate,
UITableViewDataSource protocols. In the extension it implements all the delegates required for the UITableView. Look at the implementation of these delegates. See for example the function used to obtain the number of rows to display: it uses a specific property of type PictureListProtocol called delegate.
The adapter must be considered as a part of the view (the ViewController) and it uses the PictureListProtocol to communicate with the remaining part of the view. It is defined as follows:
When the adapter executes code to render the table on the screen and requires information such as the number of rows and of sections and finally the data that should be drawn in a cell, it executes the specific functions defined by the protocol that are implemented by the ViewController. The ViewController retrieves the requested data using it own property that represents the viewModel.
Now, let’s spend some words about the last piece of this puzzle, the apiService.
In the PictureListViewModel class we declared a property called:
It represents our APIManager, the class that is responsible for recovering information (for example remotely) that will be consumed by our ViewModel.
It is actually a dummy API Manager that reads a local text file containing a json. Its implementation is described below.
Remember that in the ViewModel class we implemented a function called fetchData? If we examine the implementation we see:
It uses the apiService property that represents our APIService to execute the function that gets popular pictures from somewhere. If the operation completes successfully, it assigns the obtained collection to the picture property of the ViewModel. We have already described what happens when we change the value of that property due to the existence of a property observer.
Due to the fact that lots of components are involved in our architecture we organize our projects in this way:
You can read the complete article on Medium available here.