Using SwiftUI with View Model written in Kotlin Multiplatform Mobile

Lammert Westerhoff
3 min readMar 8, 2021

Ever since I started developing for iOS, which is more than 10 years ago now, I’ve had a love for anything related to iOS development. In the early days I loved the strange syntax of Objective-c, but then I immediately jumped on Swift as soon as it was released. As such, I was always a big proponent of native iOS development and couldn’t stand hybrid technology. A few years ago I was part of a React Native development team and even though I could see certain advantages and liked the declarative nature of it, I quickly went back to iOS development in Swift.

Two years ago (this was February 2019) me and three other developers started working on a brand new app and decided to give Kotlin Multiplatform Mobile a try. You can imagine I was very sceptical at first…

But over the last two year, as we moved more and more code from Swift and Android specific Kotlin to our shared Kotlin layer I’ve grown to like it more and more. One of the last things to move to our shared layer were our view models. And even though that project was only using UIKit, I recently started a new project in which the iOS UI is 100% SwiftUI and the view models are 100% Kotlin.

In this article I’ll share exactly how and which pattern I use.

Kotlin view models

The view models are defined through simple Kotlin interfaces. They expose everything that need needs to be displayed in a view through simple data types, like Strings for labels, or custom data classes with simple data types or nested data classes. We call all this data the “output” of the view model. And the view should be able to subscribe to this output in case anything changes. This is done through Kotlin Flows, which are similar to reactive streams such as the ones from RxSwift or Combine.

Besides that it can contain functions that correspond to actions from the view, usually as a result of user interaction.

A typical view model looks something like this:

As you might notice, we’re using CFlow instead of Flow here. That is to keep the generic information of our output type. Kotlin Flow itself is an interface and generic type information of interfaces is not available from iOS code. The CFlow is taken from the KotlinConf app, see https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/mobileMain/kotlin/org/jetbrains/kotlinconf/FlowUtils.kt.

Of course we also need an implementation of our view model. Let’s pretend that we need two api calls for this view. One to fetch the restaurant information and one to fetch the reservations. Our network layer returns a Flow that tells us that it’s either loading, is failed or is successful with a response. The implementation of how you implement such a network call is out of scope for this article.

We’ll use the following api interface and domain classes for this example:

Then our view model implementation could look something like this:

We’re now done with our Kotlin code and all that remains is creating our view in SwiftUI.

SwiftUI

Of course SwiftUI has no way to deal directly with Kotlin Flows. But it does work well with the Combine framework. Therefore to make things a little easier for ourselves we create a little utility function to transform a CFlow<T> into a Publisher, or AnyPublisher<T, Never> specifically:

Now that we have a publisher, the ‘normal’ way to be able to use it would be to create an ObservableObject that subscribes to the publisher and then use that object in our view:

Unfortunately that’s quite a bit of boilerplate code if we need to do that for every view. We have a single view model that each of our views relies and therefore our view needs to re-render each time something in our view model changes. Taking this into account, we can create a kind of wrapper view that listens to our view model output changes:

Now our view becomes a lot cleaner:

SwiftUI Previews

Since our RestaurantDetailView depends on the RestaurantDetailViewModel, which is an interface, it’s a bit hard to create SwiftUI previews without having to create an actual instance of RestaurantDetailViewModelImpl. And that’s something we don’t want to do since it depends on network calls. However we can simply extract the inner part of out view to a new view to be able to create previews:

Now we can simply create previews by initialising the output, which is a Kotlin data class with simple types and therefore easy to create:

Make sure to let me know your feedback and questions. In the near future I’m planning to write follow up articles to go deeper into error handling and (user interface) event handling.

--

--