The Future of Apps :Declarative UIs with Kotlin MultiPlatform (D-KMP) — Part 2/3

The original link

Three articles on the new D-KMP architecture based on declarative UI, Kotlin cross-platform, and MVI patterns.

Part 1: D-KMP Architecture and declarative UI

Kotlin cross-platform and MVI mode

Chapter 3: Stratification and team organization of D-KMP

Last update: April 8, 2021

Version 1.4 Kotlin cross-platform is really coming

With the release of Kotlin 1.4 in August 2020, Kotlin cross-platform is no longer experimental, but Alpha.

Now, in fact, we can start to apply this technology to our products. Although there will be some changes and improvements in the future, the current stability is very good.

If anything is currently blocking our implementation of the D-KMP project, it’s coming from the UI section (Jetpack Compose is still under close development, but getting better with the release of the Navigation component) and not from the KMP section.

Due to Kotlin’s success in the JVM community, Google announced Kotlin as the first supported language for Android in 2017. In 2019, Google designated Kotlin as the preferred language for Android development (replacing Java).

Kotlin has now evolved into a cross-platform language with code compiled to three different platforms:

  • JVM (Android and Server)
  • Native (iOS, macOS, Windows, Linux)
  • JavaScript (Web, also provides React encapsulation)

Because of this, we can now use Kotlin to develop shared code that runs directly on all platforms.

There are currently two different initials used across platforms:

  • KMM = Kotlin Multiplatform Mobile (Android and iOS only)
  • KMP = Kotlin Multiplatform (also desktop and Web)

Since Kotlin 1.4, there has been a fixed KMM portal that explains how to start cross-platform mobile development. Great material for those unfamiliar with Kotlin cross-platform.

JetBrains (authors of both Kotlin and Android Studio) released an AS plug-in for KMM that allows developers to run iOS apps directly inside AS. This is actually very useful.

Kotlin isn’t the only cross-platform programming language. It is also a very interesting programming language and avoids a lot of template code. It has a lot of advanced features you can imagine: coroutines, computable properties, attribute delegates, extension functions, higher-order functions, lambda, and more.

Kotlin quickly became a mainstream programming language. Code written with Kotlin will certainly last for decades. As a long-term project, it’s definitely ok.

KMP vs Flutter vs ReactNative

When it comes to cross-platform, the two main frameworks you probably hear about most are Flutter and React Native, both of which allow you to share code,

You may also hear that people don’t like these frameworks because they limit the ability to customize the native UI for each platform.

The advent of Kotlin cross-platform offers both of these benefits:

  • Share code across all platforms
  • Can freely customize the UI of each platform

In KMP, the shared code is written in Kotlin, but is eventually compiled into a native library: a JAR package on Android, an OC framework on iOS, and a JS library on the Web. Because of this, the native UI layers naturally interact with shared code on their respective platforms.

Within Flutter, the code is written in Dart and eventually compiled into a native library, which is NDK on Android, LLVM on iOS, and JS on the Web. But unlike KMP, Flutter needs to start its own engine, which has a greater impact on the package size. Flutter does not use a native UI. Instead, it uses its own declarative UI widgets drawn pixel by pixel by Skia’s image engine. There are a lot of people using Flutter these two years because Flutter provides a declarative UI instead of the traditional Android and iOS View. But now that the declarative UI has accelerated support on native Android and iOS, Flutter’s main advantage is gone. Jetpack Compose and SwiftUI let you build a top-of-the-line APP at full speed.

React Native code is written in javascript and can only communicate with the Native layer through the C/C++ bridge running JS code. UI components encapsulate native Android and iOS components, and developers have very limited control over the UI. RN’s entire architecture has not proved particularly good, and even Facebook itself is slowly abandoning RN. In 2018 AirBnB announced the end of RN.

Language matters: Kotlin vs Dart vs JavaScript

Another advantage that KMP has over Flutter and RN is a programming language. Compared to Dart and JS, Kotlin is a next-generation top-level language with reliability and simplicity. It’s easy to write high-quality code at Kotlin because of its smart features such as coroutines, computable properties, higher-order functions, and so on.

Only 15% of the platform-specific code is in d-KMP architecture

At this point, some people might think, “Ok, I know KMP is great. I knew declarative UI would eventually come to Android and iOS. But that way I still had to draw the UI for each platform, which also caused a lot of rework!”

The answer is “NO! There’s not a lot of rework.”

Under the new native declarative UI platform, the UI layer is very light. In our current D-KMP APP, the UI layer takes up about 15% of the total code volume. And the UI is all platform-specific code, the rest is all KMP shared code.

This extra 15% of code is totally worth it because it allows us to customize for each platform, rather than the restrictions that Flutter and RN have. Android and iOS are two different platforms with a lot of differences. A top APP needs to maintain true UI/UX mode for each platform.

From our experience, once we write Jetpack Compose UI on Android, we can implement it on iOS SwiftUI. The structure of the code is almost the same. For a simple APP, it takes less than a day.

There are also some components that are the same in both frameworks, albeit with different names. For example, if you want to place multiple texts horizontally in a single layout, Android Jetpack Compose is Row, iOS SwiftUI is HStack, and the Text component is Text in both frameworks, but the syntax is slightly different. Once you’re familiar with these little differences, you can quickly copy the declarative UI from one end to the other.

All the data comes from the state of the page, provided by kMP-shared code. The declarative UI on each platform, where you only care about the view, is a very light task.

The important thing is that declarative UIs don’t need to process data. Don’t worry about anything. Just show it. This will also significantly reduce platform-related bugs.

Once shared code doesn’t worry about bugs, everything runs smoothly on each platform. That’s why you don’t need to worry too much about developing a UI for each platform.

MVI pattern: The third core of d-KMP architecture

As we mentioned at the outset, the MVI pattern (which stands for Model-view-Intent) is the third core part of this structure. The main concept behind this is monomial data flow, which is what sets it apart from previous MVC, MVP, and MVVM. MVI is a responsive pattern that makes APP behavior more consistent and predictable, and you can think of it as an evolution of MVVM.

In MVI mode, the state of the View has only one trusted data source. State is made up of mutable data at any time and can only be modified by Model. Everything is a one-way street. The user launches an event /intent. The Model changes state after doing something and performing some operations. The new state is then reflected on the View.

In our D-KMP architecture, we implement MVI’s Model in KMP shared code (see figure below), which allows us to do state management in shared code, which is very important!

Because of this, our platform-specific code is just that layer of declarative UI, which is light and stateless because it delegates state management entirely to the KMP ViewModel.

Let’s look at some code: let’s start our third article!