KINTO Tech Blog
General

Mobile App Development Using Kotlin Multiplatform Mobile (KMM)

Cover Image for Mobile App Development Using Kotlin Multiplatform Mobile (KMM)

Introduction

Hello, we're Yao Xie and Mooseok Bahng, who work on mobile app development in the Global Group at KINTO Technologies.

We're currently working on an app called Global KINTO App. The Global KINTO App (GKA) was built with the goal of connecting together KINTO services all over the world with a single app. So far, KINTO services have been deployed in Thailand and Qatar.

While working on a project destined to replace an existing app, we decided to adopt Kotlin Multiplatform Mobile (KMM). So, we're going to talk about that.

Background on why we decided to adopt KMM

There were a few issues that lead to our decision to adopt KMM. - No matter what we did, differences always arose in the business logic between iOS and Android. - The development team is physically split between 2 locations, which can impact development efficiency. -> We thought we could improve things by dividing the team into KMM and native. - Our development resources are limited, so we want to create an efficient development system.

Based on these, we started to consider KMM.

What is Kotlin Multiplatform Mobile (KMM)?

An SDK for developing iOS and Android apps, KMM uses Kotlin as its base language, and offers the benefits of both cross-platform and native apps. You can develop a common business logic using KMM, then develop the platform-dependent UI elements natively.

We personally think that it's best to provide the optimal UI/UX for each OS. With KMM, you basically develop each UI natively, so the UI/UX can be optimized with little dependence on iOS and Android, and we also think version upgrades are going to have virtually no impact.

KMM is still a new technology and isn't very mature, but it's been getting used more and more by various companies in recent years.

KMM

Kotlin Multiplatform Mobile (source: https://kotlinlang.org/lp/mobile)

Architecture

Before we talk about KMM, let's talk about the architecture currently being used by the development team. In short, we develop using an MVVM pattern. This policy basically won't change even though we're adopting KMM.

MVVM Architecture

At this point, we were wondering just how much we should include in KMM. There are around three options.

KMM Native
Option 1 Repository, Usecase, View Model UI
Option 2 Repository, Usecase View Model, UI
Option 3 Repository Usecase, View Model, UI

We tried various approaches, but for now, we're moving in the direction of using KMM up to the view model. We also considered leaving out the view model, but couldn't find any good reasons for handling it separately despite making the effort of adopting KMM. This is especially true for simple features like displaying data in a list. Maybe we'll need to do the view model separately as more complex features get added. When that happens, it should be possible to do just some parts of it separately.

KMM Architecture

The codebase for iOS is now pretty compact. We're using KMM up to the domain layer and view model, so there's only UI- and platform-dependent hardware-related features, and we think it'll probably amount to at most half as much source code as before.

Here's some iOS code for a simple screen with an FAQ list. Apart from a common UI Utility class, this is all we need.

swift struct FaqView: View {
    
    private let viewModel = FaqViewModel()
    @State var state: FaqContractState
    
    init() {
        state = viewModel.createInitialState()
    }
    
    var body: some View {
        NavigationView {
            listView()
        }
        .onAppear {
            viewModel.uiState.collect(collector: Collector<FaqContractState> {
                self.state = $0
            }
            ) { possibleError in
                print("finished with possible error")
            }
        }
    }
    
    private func listView() -> AnyView {        
        manageResourceState(
            resourceState: state.uiState,
            successView: { data in
                guard let list = data as? [Faq] else {
                    return AnyView(Text("error"))
                }
                return AnyView(
                    List {
                        ForEach(list, id: \.self) { item in
                            Text(item.description)
                        }
                    }
                )
            },
            onTryAgain: {
                viewModel.setEvent(event: FaqContractEvent.Retry())
            },
            onCheckAgain: {
                viewModel.setEvent(event: FaqContractEvent.Retry())
            }
        )
    }
} 

Pros

  • Single codebase

We can manage iOS and Android networking, data storage, business logic, and more with a single codebase.

  • Consistency

Having a common business logic means we can basically provide the same UX.

  • Efficiency

Adopting KMM has enabled us to do development work more efficiently. Cutting our time costs almost in half means we're getting to spend that much longer on source code optimization and business deployment.

  • Expandability

We can easily expand development as needed to include other platforms besides iOS and Android.

Expandability

Cons

  • For iOS debugging, you need to install a separate plugin.
  • If you use XCframework, using Simulator on an Apple Silicon Mac results in an error because it references arm64. We think this will need to be fixed on the KMM SDK side, but for the time being, we can use Simulator either by adding arm64 to the excluded architecture, or running Xcode in Rosetta mode.

Distribution method for iOS

Build & Sourcesets

sourceset

Build XCFrameworks

Up to now, distributing KMM to iOS has basically been done with a Universal (FAT) framework. Official support for XCFramework has finally come out recently, so we're planning to go with that.

https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#build-xcframeworks

Other

They're not directly related to KMM, but here are some other new technologies we're also thinking of adopting:

Ktor

  • You can use the same settings for both clients.
  • The API Request code is the same.
  • The engines for iOS and Android are separate, but no additional code is required.

Ktor

Apollo Client

We're also using some of the GraphQL API in some of existing projects. We're thinking of adopting the Apollo Client in order to use GraphQL. You just need to create Queries.graphql using schema.graphqls in the backend, and Models, Adapters, and Queries will all get created automatically.

https://github.com/apollographql/apollo-kotlin

MMKV

MMKV is a mobile key-value storage framework. It supports multiple platforms, including Android and iOS of course.

https://github.com/Tencent/MMKV https://github.com/ctripcorp/mmkv-kotlin

With MMKV-Kotlin, we can easily integrate MMKV into our projects and manage key-value storage with a shared module.

Performance comparison on Android

MMKV_vs_SP

Performance comparison on iOS

MMKV_vs_NSUserDefaults

Future developments

Fast growing KMM ecosystems

KMM is being developed by Jetbrains. It can be used seamlessly with Android Studio and has some Xcode support as well. It's spreading among developers, and there are a lot of open source libraries for it.

https://github.com/terrakok/kmm-awesome

Cross-platform UI

Touchlab has already started experimenting with using Compose UI for both iOS and Android.

https://touchlab.co/compose-ui-for-ios/

@Composable
internal actual fun PlatformSpecificSettingsView(viewModel: SettingsViewModel) {
    IconTextSwitchRow(
        text = "Use compose for iOS",
        image = Icons.Default.Aod,
        checked = viewModel.observeUseCompose,
    )

    Divider()
}

KMM may start to officially support cross-platform UIs in the near future.

Summary

In this article, we talked about how we've adopted KMM. We're now expecting it to bring the following improvements: - Being able to minimize the business logic gap between iOS and Android - Being able to optimize the development team structure if required - Reducing the development time to a certain extent

We're still in the early stages of adopting KMM and are going to face lots of issues and steadily build up our know-how, so we'll share more once we've made some more progress.

Thank you for reading.

Facebook

関連記事 | Related Posts

Cover Image for Kotlin Multiplatform Mobile(KMM)およびCompose Multiplatformを使用したモバイルアプリケーションの開発

Kotlin Multiplatform Mobile(KMM)およびCompose Multiplatformを使用したモバイルアプリケーションの開発

Rasel
Rasel
Cover Image for SwiftUI in Compose Multiplatform of KMP

SwiftUI in Compose Multiplatform of KMP

T. Koyama
T. Koyama
Cover Image for Using Combine to Achieve MVVM

Using Combine to Achieve MVVM

Rasel
Rasel
Cover Image for SwiftUIをCompose Multiplatformで使用する

SwiftUIをCompose Multiplatformで使用する

T. Koyama
T. Koyama
Cover Image for Combineを使ってMVVMを実現した話

Combineを使ってMVVMを実現した話

Cover Image for グローバル開発グループ(2/3)

グローバル開発グループ(2/3)

We are hiring!

【iOS/Androidエンジニア】モバイルアプリ開発G/東京・大阪

モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。

【部長・部長候補】/プラットフォーム開発部/東京

プラットフォーム開発部 について共通サービス開発GWebサービスやモバイルアプリの開発において、必要となる共通機能=会員プラットフォームや決済プラットフォームの開発を手がけるグループです。KINTOの名前が付くサービスやTFS関連のサービスをひとつのアカウントで利用できるよう、様々な共通機能を構築することを目的としています。