Use MVVM and Combine in SwiftUI
MVVM is the acronym for Model-View-ViewModel. It is an architectural pattern that primarily focuses to provide a clear separation between the core components of your app. MVVM is composed of the following components:
Model is basically the data model or entities that your application has. They are simply structs or classes with simple associated properties. In general practice, they hold the data that has been mapped from your raw data structure which might come from your API’s or other sources such as SQLite files, etc. The view model owns the model
The view is the visual element that gets displayed. All the UI components on an app screen are views. The view contains only the UI logic, such as data rendering, navigation, etc. The view reactively observes properties of the view model that represent the view content. The view owns the view model.
View Models receive UI events, performs business logic and provides the output to be displayed on the UI. It is responsible for handling the business logic driving the view. View models hold properties that represent the presentation data. The View binds itself to these properties thereby observing the changes to these properties and refreshing itself. It is here where Combine comes into play. Combine’s easy integration with SwiftUI provides binding APIs for SwiftUI views.
Some useful property wrappers
Before getting on to the implementation details, let us familiarise ourselves with some useful property wrappers:
ObservableObject is a class protocol which when conformed by any class, becomes a
Publisher and the class can automatically notify just before changes to its properties marked as
@Published. It implements an
objectWIllChange property to publish changes in its data. Check out the documentation for more info on ObservableObject.
@Published property wrapper that is to be used on properties upon change of which a SwiftUI view that is dependent on the property refreshes or re-renders itself. SwiftUI observes the changes to these properties internally and reloads the view which is dependent on them.
A property marked with
@ObservedObject will hold an instance of an
ObservableObject. You have to use an
@ObservedObject property in a SwiftUI view. Whenever there is any change to the its
@Published property, the view will be automatically refreshed.
We will build a Super Hero Viewer app which will show few superheroes of DC and Marvel.
You can download the sample project and start referring to it during the implementation below.
Configuring the Model
The data that we need to render would be the superhero image and name. These data will be represented by our model.
Configuring the View Model
It is convenient to think view models as a collection of outputs and inputs, where output is the data or instruction for the view to construct itself and inputs are UI events that need some change in the business logic and manipulate data.
In our case, the output will be a data source which would be a list of superheroes. Input to the view model will be a user selection of the universe. On the basis of selection, we have to change the list to the respective universe’s superheroes.
What have we done here?
We created a view model representing our view. The view model conforms to
ObservableObject. It has a
selectedUniverse will be the input to the view model, as well as it would be the property on which our UI rendering depends. Whenever the user chooses any universe, we feed the selection to this property. We have made
selectedUniverse as an optional because, when there is no
selectedUniverse , we hide the superheroes grid and vice-versa.
You would be wondering what is the crazy thing going on in the
init(). We have injected a
DataProvider which will supply us data of selected universe. You can think this as some sort of database. This is called Dependency Injection. Let us decipher the other happenings one by one:
- Line 1: We accessed the publisher of the
- Line 2: Then we removed duplicates from the publisher event stream. As tapping on the same universe, again and again, will not alter the data source, thus we can ignore duplicates
- Line 3: After removing duplicates, we subscribed to the values that come from the publisher event stream. We then asked the
dataProviderto provide data for the
selectedUniverseand stored it to the
- Line 4: All
AnyCancellableresults are stored in a
subscriptionsset, ensuring that your subscriptions are still in memory to receive any upcoming events.
Configuring the View
Let us create a view
SuperHeroesView which will hold the code for the UI. It uses a SwiftUI grid to hold the superheroes collection.
Why should you use MVVM?
- Separation of concerns: MVVM provides a clear separation between the core components of your code i.e View, Model, and the Business Logic.
- Test-Driven Development: The most important of all, MVVM really provides a good platform to perform TDD. We can write down Unit Test Cases for View Models and test the business logic driving the UI. Unit Test cases are really important while developing as they minimize our code-break chances to a great extent.
Why should you use Combine?
“By adopting Combine, you’ll make your code easier to read and maintain, by centralizing your event-processing code and eliminating troublesome techniques like nested closures and convention-based callbacks.” — Apple
- Make your code flow reactive.
- Use a functional approach to write your code.
- Simplify applications event flow and communication mechanism.
- Asynchronous event handling becomes much simpler.
What is the difference between MVVM in UIKit and MVVM in SwiftUI?
Clear separation of a View component in SwiftUI.
Unlike its UIKit counterpart where you have a ViewController which is a part of MVC design pattern, SwiftUI has its View component separated. In UIKit, we are just extending the MVC to adopt MVVM by assuming the controller to be the View.
We miss out of the box data binding between components in UIKit, as we have with SwiftUI, with the help of
@Published. In order to make it reactive, we have to write our own wrappers. Using Combine can somewhat make the flow reactive as it provides binding APIs to few UIKit components, but not for all of them. You can check out my blog on UIKit + MVVM + Combine implementation.
I would love to hear from you
You can reach me for any query, feedback, or just want to have a discussion by the following channels: