ViewModels in Swift & Kotlin
Everyone’s heard of a ViewModel, but what do they actually do?
From StackOverflow:
A view model represents the data that you want to display on your view/page, whether it be used for static text or for input values (like textboxes and dropdown lists) that can be added to the database (or edited). It is something different than your domain model. It is a model for the view.
As part of a Clean MVVM architecture, the use of ViewModel classes aligns closely with the classic definition: it holds the state of the view, reacts to user actions to trigger operations that ultimately modify that state, and emits events connected to user actions. It is represented by the green 🟢 parts of Uncle Bob’s Clean architecture.
3 design rules for ViewModels
When writing a ViewModel class for a mobile architecture, consider these three rules:
1. Never reference UI libraries
Never import anything from android.view
and UIKit
package. From a practical matter, when you have a ViewModel without such dependencies you will be able to write and run tests directly from the command line, without an emulator. The only time we allow ourselves to break this rule is on iOS — we still may use models in UIKit
to represent images (e.g., UIImage
)
2. Always use Reactive streams
As has been said many times, you should use reactive primitives (such as those provided by LiveData, RxJava, RxSwift, ReactiveSwift, or Combine), and not types directly with callbacks.
You should also be careful not expose mutable streams. For android, this means avoid exposing MutableLiveData
/ PublishSubject
, and instead expose LiveData
/ Observable
. For iOS, this means never expose MutableProperty
, instead exposing a Property
/ Signal
(see code example, below).
3. Consider State vs Events
There are two types of data exposed by ViewModels: State and Events. State is something that we need to retain during the lifetime of the view, and can be represented using:
- 🍎
Property
of an enum with associated values in ReactiveSwift, or a@Published
in Combine - 🤖
LiveData
of a sealed class with associated data
Events are something that need to be handled only once. For example, navigating to a particular Activity
or UIViewController
because of a user tap on a button, or showing a toast because of the user hitting a paywall.
Here is how state and events get exposed in a ViewModel class:
Naming
Of course naming, and the end of the day, is an aesthetic decision of the developer, and whatever pattern you choose should be consistent. The ViewModel itself should likely be called <feature>ViewModel
for example ProfileViewModel or MatchViewModel. Further:
- The names of the public methods should reflect the action that is performed in the view, for example onStartButtonTapped
- Methods that react to asynchronous events or Reactive streams should start with handle.
Next up
ViewModels are not the end of the story, not even close! There are many more types of objects and classes to define and use when building a mobile application architecture consistently across iOS + Android.
Next, we describe our stateless logic classes, the point in an application where repositories meet.
More in the series
- Intro to Clean MVVM
- ViewModels in Swift & Kotlin ← you are here
- Logic Classes in Swift & Kotlin
- Repositories and Domain Models in Swift & Kotlin
- API classes in Swift & Kotlin
- Views in Swift & Kotlin
- Testing MVVM in Swift & Kotlin
- Testing Asynchronicity in Swift & Kotlin
- Clean MVVM Summary
Other series you might like
Clean API Architecture (2021)
Classes, execution patterns, and abstractions when building a modern API endpoint.
Android Activity Lifecycle considered harmful (2021)
Android process death, unexplainable NullPointerExceptions, and the MVVM lifecycle you need right now
About the author
Eric Silverberg is the CEO and founder of Perry Street Software, publisher of two of the world’s largest LGBTQ+ dating apps on iOS and Android.