Why Android ViewModels are obsolete in KMM

The Problem
One of the challenges on Android is that UI state needs to be saved and restored when configuration changes like screen rotations happen. Android ViewModels
were introduced to make this process as painless as possible.
The
ViewModel
class is designed to store and manage UI-related data in a lifecycle conscious way. TheViewModel
class allows data to survive configuration changes such as screen rotations.(https://developer.android.com/topic/libraries/architecture/viewmodel)
While designing and implementing a UI framework for Kotlin Multiplatform I found that existing frameworks use one of two approaches to address the (Android specific) UI state retention problem:
- Use a
ViewModel
on Android that proxies from the UI to the component that implements the business logic (let’s call it bloc = business logic component). The sole reason for theViewModel
to exist is to be lifecycle aware and retain state across configuration changes.
Touchlab has made a virtue out of this in their KaMPKit and created a sharedViewModel
with platform specific implementations so all business logic can be put into the sharedViewModel
. - Add the ability to preserve state / retain instances in the bloc = business logic component. Decompose e.g. is using Essenty’s StateKeeper and InstanceKeeper. This makes the bloc independent of the Android
ViewModel
(at least on the surface) but now it needs to explicitly save and restore state. This is imo a less than ideal solution especially because it’s a solution to a problem that exists on a single platform only.
A “better” Solution
There’s a third way.
Just to recap, we need a bloc / business logic component that is lifecycle aware and is retained across configuration changes. This is how my solution looks like in an Activity
or a Fragment
:
val bloc by getOrCreate { bloc(it) } // `it` is an Essenty lifecycle
We’re using an Essenty lifecycle which is the platform-independent version of Android lifecycle. It’s passed as argument to the bloc and can be used by the business logic to start and stop and cleanup resources upon destruction (onDestroy() called).
Let’s see what’s under the hood:
getOrCreate
is an extension function of ViewModelStoreOwner
lazily creating a component using the builder function (lifecycle: Lifecycle) -> Component
. Activities and Fragments are both ViewModelStoreOwner
so we can call this from either.
ComponentLazy
is a bit harder to understand:
(Note: with androidx.lifecycle
2.5, the create function’s signature has slightly changed to: override fun <T : ViewModel> create(modelClass: Class<T>): T
-> T isn’t nullable any more)
Using the ViewModelStoreOwner
we create a ViewModelProvider
which will create ViewModels
and retain them in a store of the given ViewModelStoreOwner
. The get
function returns an existing ViewModel
or creates a new one in the scope (fragment or activity), associated with this ViewModelProvider
. The created ViewModel
is associated with the given scope and will be retained as long as the scope is alive (e.g. if it is an activity, until it is finished or process is killed):
- The created ViewModel is a
BlocViewModel
which holds an EssentyLifecycle
and an EssentyInstanceKeeper
:
- Now we use this
InstanceKeeper
to either retrieve or create theComponent
. TheComponent
is wrapped into a class than implementsInstanceKeeper.Instance
because only those can be store in anInstanceKeeper:
In a gist:
- The
Component
is stored in anInstanceKeeper
- The
InstanceKeeper
is stored in aBlocViewModel
which is an AndroidViewModel
- The
BlocViewModel
holds aLifecycle
tied to its own lifecycle. This lifecycle is used by theComponent
to manage its internal resources (coroutines, flows, channels etc.) - The
BlocViewModel
is stored in theViewModelStore
as long as its owner is alive meaning it will be retained across configuration changes
Summary
We can create a shared / platform-independent business logic component and make it lifecycle aware on Android with a single line of code:
val bloc by getOrCreate { bloc(it) }
The code that makes this possible can reside in a shared module (androidMain)
. Kotlin Bloc
, a recently released UI framework for Kotlin Multiplatform, implements this mechanism.
Thanks for reading and for your feedback.
Addendum
Note that while using a ViewModel under-the-hood makes sure to retain the bloc across configuration changes, it won’t survive a process death.
In order to survive a system-initiated process death, you’d still have to use a SavedStateHandle
:

Whether your UI needs that kind of “retention” depends on your app. Depending upon the action a user takes, they either expect that activity state to be cleared or the state to be preserved.