An Alternative to MVC in Swift: Basic MVVM Structure

Federico Mazzini
Major League
Published in
3 min readDec 3, 2018

--

MVVM (Model-View-View-Model) and MVC (Model-View-Controller) are iOS design patterns. Apple defines a design pattern as a template for a design that solves a general, recurring problem in a particular context. It is a tool of abstraction that is useful in fields like architecture and engineering as well as software development.

In MVVM we add a layer between the different MVC layers that is called ViewModel. It consists of classes or structs that refer to instances that we want to represent or communicate.

How to Create Our ViewModel

Let’s suppose we want to list results of a service. In this example I will list news sources. In order to do that I’ve created the Source model class:

class Source {

var id :String
var name :String
var description :String

init(id :String, name :String, description :String) {
self.id = id
self.name = name
self.description = description
}
//...

If we want to list our Sources in a ViewController, we can use a class that encapsulates the information that Controller needs. We call it SourceViewModel:

class SourceViewModel {var id :String
var name :String
var description :String

init(source: Source) {
self.id = source.id
self.name = source.name
self.description = source.description
}
// ...

But the information that our Controller needs is not that of a Source, but that of a list of Sources. So, we need something like a higher level View Model, a Source (List) ViewModel. Let’s create it:

class SourceListViewModel {

private var webservice: Webservice
private (set) var sourcesViewModel: [SourceViewModel] = [SourceViewModel]()
private var completion: () -> () = { }

init(webservice: Webservice, completion: @escaping () -> ()) {
self.webservice = webservice
self.completion = completion
populateSources()
}

private func populateSources() {
self.webservice.loadSources { sources inself.sourcesViewModel = sources.compactMap(SourceViewModel.init)
self.completion()
}
}
// ...

This is the object that our ViewController will receive, and which has all the information it needs to fill the list. What’s going on here?

  • The SourceListViewModel class uses a WebService, which in this case represents the service that provides us with our Source. This is private, but is injected by a constructor, which makes our ViewModel testable.
  • This class also has a collection of SourceViewModel, which will be used by our ViewController to populate a view. This collection has a private setter, but public getter.
  • This class also has a private block, but it will be executed upon successful initialization.
  • The populateSources () auxiliary method, which is nothing more than the use of our WebService service to obtain instances of our Source model. And so we can instantiate our SourceViewModel.

Working with our ViewModel

We use our higher level ViewModels in our ViewController. In order to list our Source, we use a ViewController in the following way:

class SourcesTableViewController : UITableViewController {

private var webservice: Webservice!
private var sourceListViewModel: SourceListViewModel!

override func viewDidLoad() {
super.viewDidLoad()

self.webservice = Webservice()
self.sourceListViewModel = SourceListViewModel(webservice: self.webservice, completion: {

self.tableView.reloadData()
})
}
// ...

Once our SourceListViewModel has been initialized within our ViewController, we use it to populate it, in this case we load a table with the data of our collection. Using the collection as follows:

// MARK: - Table view data sourceoverride func numberOfSections(in tableView: UITableView) -> Int {

return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return sourceListViewModel.sourcesViewModel.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let sourceViewModel = self.sourceListViewModel.sourcesViewModel[indexPath.row]
cell.textLabel?.text = "\(String(describing: sourceViewModel.firstName!)) \(String(describing: sourceViewModel.lastName!))"

return cell
}
// ...

This was a basic example of the implementation of this pattern. We can use generics to create a generic Data Source and inject it into our View Controllers and Promises to avoid blocks in our services and View Models. This is how we can achieve a cleaner, readable and testable architecture.

Are you a Developer or Designer looking for challenging new projects? Are you looking for Developers or Designers? Major League is the right fit for you!

--

--