When starting the process of rebuilding our apps, we were confronted with a problem that most old, big apps have. The logic in our apps responsible for transforming raw data to something on the screen was completely contained in our ViewControllers and Views. This means that these classes were huge, hard to read and even harder to understand. This is a common issue with iOS apps that results mostly from the M(odel) (V)iew (C)ontroller design pattern that is often used.
For our new app we decided on the MVVM (Model/View/ViewModel) design pattern as the core of our codebase. This design pattern is meant to remove the logic of an app from the View and ViewController. Instead a new entity is introduced called a ViewModel. This ViewModel describes the elements that should appear on the screen. You could consider it a code-representation of the UI. This ViewModel is then passed to a View. The View then only has to read the ViewModel and modify the UI as specified.
However, this approach still does not take into account one of the problems we had: Where should the main business logic of the app be located? The original Model should still be transformed into a ViewModel for the View to be read. We decided to tackle this problem by introducing yet another entity: a Mapper. a Mapper is a function or class that contains all the logic necessary to transform a Model into a ViewModel. These Mapper functions mostly take a Model as a parameter but they can also be made more flexible. For instance, you might need multiple kinds of Models to generate a single ViewModel or you might want to be able to specify what kind of ViewModel has to be created.
The core functionality of our app is getting data from an API and transforming this data into something the user understands. Because our business logic that transforms our data is now contained in a single Mapper class or Mapper function, testing this logic becomes extremely easy: we write unit tests that cover our Mapper classes. An added bonus is that our ViewModels are a very precise description of our UI state, which means we don’t have to write as many (slow and cumbersome) UI tests.