Kotlin Multiplatform Tales: A Shared ViewModel

David Chavez
Double Symmetry
Published in
3 min readApr 7, 2022

Note: Our posts will generally be about Kotlin Multiplatform for iOS & Android (KMM)

At Double Symmetry, we’ve been using Kotlin Multiplatform (KMP) for building iOS and Android applications for the last two years. KMP’s ability to share Kotlin code between different platforms has enabled our team to continue building beautiful native UI while sharing our platform independent code. For the last two years, our goal with KMP has been to maximise the amount of code that can be shared between platforms while also aiming to not lose the commodities of each.

The Problem Space

At the moment most apps (we know of) use KMP to share their data/domain layer between platforms.. in this post we’ll take this one step further — bringing sharing to the presentation layer! At Double Symmetry, the presentation layer of the apps we build is built on the MVVM(-C) pattern. Therefore a very important part of our apps and shared code is the ViewModel.

Requirements for a Shared ViewModel

On Android, Jetpack provides a ViewModel class which is automatically tied to scope (usually a fragment, an activity or a composable) and is retained for as long as that scope is alive. It also makes available a CoroutineScope that's tied to this ViewModel's lifecycle which can be used for writing your business logic.

If we’re to create a shared view model we’d like the following:

  1. On Android our ViewModel should also be able to leverage lifecycle awareness.
  2. Our ViewModel should provide a CoroutineScope that we can use.

Implementation

We’ll start by defining our base ViewModel class that exposes a CoroutineScope as we'd expect from our requirements:

Simple enough! Now let’s work on our Android definition. The requirement is that we can also leverage lifecycle awareness.. so should we figure out how Jetpack’s ViewModel does it? No need. Let's use that!

Voila! Now on Android our instances of BaseViewModel will conform to all the sweet commodities we'd expect and exposes a CoroutineScope. But what about iOS?

Not as straightforward. But also not complicated. When a scope is requested, we first check if one has already been created and return it, or we create a new one. We also don't have an automatic way to bind to the lifecycle of a UIViewController so we'll have to expose a method that should be called on deinit to clear the scope.

Using the ViewModel

Now that we’ve got a ViewModel we can write our business logic for both platforms!

Beautiful ✨

Thanks for reading this article! I hope this library makes sharing business logic easier for you and your team while building your application.

Are you building mobile apps and overpaying for Github Actions in your private repos? Check out Blaze — Apple Silicon runners designed to reduce your build time at a much lower cost.

--

--

David Chavez
Double Symmetry

Building apps | iOS, Android, Kotlin/Native. Cofounder at DoubleSymmetry.