Kotlin Multiplatform: what it is, shared module, and expect/actual

Author: IT Sectr Published: 2026-02-11 Reading time: 11 min

Kotlin Multiplatform (KMP) is a JetBrains technology that compiles shared Kotlin code for iOS, Android, Web, and Desktop. Unlike Flutter and React Native, KMP does not replace native UI — shared logic is extracted into a shared module, while each app's interface remains native. Kotlin Multiplatform documentation — the main reference for configuring modules and the expect/actual mechanism.

Key Takeaways

  • KMP — shared logic in Kotlin with expect/actual for platform APIs without replacing native UI
  • Shared module — a Gradle module with networking, database, validation, and business logic code for all platforms
  • Expect/actual — a mechanism for declaring platform API in shared code with implementation for each target
  • iOS integration — shared module compiles into an Apple framework via Kotlin/Native
  • KMP vs KMM — Kotlin Multiplatform Mobile (mobile focus) is now part of Kotlin Multiplatform

What is Kotlin Multiplatform?

Kotlin Multiplatform is a cross-compilation technology that allows writing shared code in Kotlin and compiling it for different platforms: JVM (Android), LLVM (iOS, macOS, watchOS), JavaScript (Web), and native binaries (Linux, Windows). KMP is not a UI framework — it solves the problem of reusing business logic, not interfaces.

The KMP architecture is built around a shared module — a Gradle module containing commonMain with platform-independent code and source sets for each target (androidMain, iosMain, desktopMain). According to JetBrains data for 2025, over 40% of new Kotlin projects use KMP to share code between platforms.

Kotlin Multiplatform Mobile (KMM) — the previous name for the iOS+Android mobile scenario. Since Kotlin 2.1+, the KMM term has been replaced by Kotlin Multiplatform, as the technology has expanded beyond mobile development. Netflix, McDonald's, and VMware use KMP in production to share code between mobile applications.

Expect/actual mechanism: architecture

Expect/actual — the key KMP mechanism for working with platform-specific code. In commonMain, an expect declaration (function, class, property) is declared, and in each platform-specific source set (androidMain, iosMain), an actual implementation is provided. The compiler checks that for each expect, there is an actual in every target platform.

kotlin
// commonMain — platform API declaration
expect fun getPlatformName(): String

expect class PlatformContext(val appVersion: String)

// androidMain — actual for Android
actual fun getPlatformName(): String = "Android \${Build.VERSION.SDK_INT}"

// iosMain — actual for iOS
actual fun getPlatformName(): String =
    UIDevice.currentDevice.systemName

The source set hierarchy in KMP allows creating intermediate levels: for example, iosArm64Main (physical iOS devices) and iosSimulatorArm64Main (simulator) with shared iosMain. Code from commonMain is available to all platforms, while code from iosMain is only available to iOS targets. This reduces duplication when the implementation differs not for each platform but for a group of platforms.

In practice, expect/actual is used for: accessing local storage (SharedPreferences vs NSUserDefaults), networking (HttpEngine per platform), file system access, cryptography, and analytics. JetBrains recommends minimizing the number of expect/actual declarations and moving as much code as possible to commonMain.

Shared module: structure and Gradle

Shared module — a standard Gradle module with the org.jetbrains.kotlin.multiplatform plugin. It contains shared code in src/commonMain/kotlin/ and platform-specific implementations in src/androidMain/kotlin/ and src/iosMain/kotlin/. A KMP project also includes androidApp and iosApp, which depend on the shared module.

kotlin
// build.gradle.kts — shared module
plugins {
    kotlin("multiplatform")
    id("com.android.library")
}

kotlin {
    androidTarget()
    
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = "shared"
            isStatic = true
        }
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("io.ktor:ktor-client-core:3.1.0")
                implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
            }
        }
        val androidMain by getting {
            dependencies {
                implementation("io.ktor:ktor-client-okhttp:3.1.0")
            }
        }
        val iosMain by creating {
            dependencies {
                implementation("io.ktor:ktor-client-darwin:3.1.0")
            }
        }
    }
}

The Gradle configuration for KMP requires explicit declaration of iOS targets — x64 (Intel simulator), arm64 (physical devices), and simulatorArm64 (Apple Silicon simulator). A separate Apple framework is generated for each target. The kotlin("multiplatform") plugin automatically configures compilation for JVM and LLVM depending on the declared targets.

Ktor and kotlinx.serialization — standard KMP libraries that support shared code. Ktor provides an HTTP client with engines for each platform (OkHttp for Android, Darwin for iOS). kotlinx.serialization works on all platforms without expect/actual thanks to its multiplatform implementation in commonMain.

iOS integration via Kotlin/Native

Kotlin/Native — a Kotlin compiler for native code via LLVM. For iOS, the shared module is compiled into an Apple framework (.framework) that is connected via Xcode. Calling shared code from Swift/Objective-C happens through generated Objective-C headers, so the shared module API must be compatible with Objective-C.

iOS integration limitations: Kotlin collections (List, Map) are converted to NSArray/NSDictionary. Functions with default parameters are not exported — overloads are needed. For suspend functions, callback-based methods are generated with @ObjCName and async/await support starting from Kotlin 2.0+.

swift
// iOS app: calling shared module from Swift
import shared

class ViewModel: ObservableObject {
    let repository = UserRepository()
    
    func loadUsers() {
        repository.fetchUsers(completionHandler: { result, error in
            if let users = result as? [User] {
                print("Users: \(users.count)")
            }
        })
    }
}

Shared module integration into Xcode happens via embed-and-framework — the generated .xcframework is added to the Xcode project. The Gradle plugin can automatically update the framework during build via embedAndSignAppleFrameworkForXcode. For testing on the simulator, an iosSimulatorArm64 or iosX64 binary is sufficient.

KMP vs Flutter vs React Native

The choice between KMP, Flutter, and React Native depends on the priority: code reuse or full cross-platform. KMP provides native UI on each platform but requires two code bases for the interface. Flutter and React Native use a single UI but sacrifice nativity.

FeatureKMPFlutterReact Native
UI frameworkNative (Android XML/Jetpack Compose + SwiftUI)Dart + own Skia rendererReact + native components
Shared codeBusiness logic, networking, DB, validation100% except native plugins100% except native modules
PerformanceNative (no middleware)High (Skia Engine)Medium (JSI Bridge)
iOS supportKotlin/Native (excellent)ExcellentGood
Entry barrierMedium (Kotlin + native platforms)Low (one language + one UI)Low (JS/TS + React)

When to choose KMP: the project requires high-performance UI (games, maps, animations), existing native code needs to be reused, the team already knows Kotlin and native platforms. When to choose Flutter/RN: MVP or startup with limited budget, a single-profile team, the UI does not require deep native customization.

KMP tools and libraries

The KMP tools ecosystem includes libraries for all application layers: networking (Ktor), serialization (kotlinx.serialization), database (SQLDelight), navigation (Decompose), DI (Koin), and data storage (multiplatform-settings). JetBrains supports Compose Multiplatform — a Kotlin UI framework that runs on all platforms.

kotlin
// KMP Repository with SQLDelight + Ktor
class UserRepository(
    private val httpClient: HttpClient,
    private val db: AppDatabase
) {
    suspend fun syncUsers(): List<User> {
        val remote = httpClient.get("https://api.example.com/users")
            .body<List<UserDto>>()
        
        db.userQueries.replaceAll(remote.map { it.toDomain() })
        
        return db.userQueries.selectAll().executeAsList()
    }
}

Compose Multiplatform — a UI framework for KMP based on Jetpack Compose. It allows writing interfaces in Kotlin for Android, iOS, Desktop, and Web. In 2025, Compose Multiplatform reached stable status for Android and Desktop; iOS target is in beta. For production projects with native UI, KMP's advantage remains the key difference from Flutter.

Frequently Asked Questions

How is Kotlin Multiplatform different from Kotlin Multiplatform Mobile?

KMM — the mobile scenario of KMP for iOS and Android. Since Kotlin 2.1+, JetBrains combined both terms into Kotlin Multiplatform, as the technology supports not only mobile platforms but also Desktop and Web. KMM projects continue to work, but they are now part of the overall KMP.

Can KMP be used with SwiftUI?

Yes. KMP compiles into an Apple framework via Kotlin/Native with Objective-C headers. SwiftUI imports this framework like any regular library. The shared module exports Kotlin classes and functions that are called from Swift with some limitations (for example, Kotlin collections are converted to Foundation types).

How to test shared module on iOS?

On iOS, the shared module is tested via Kotlin/Native tests in the iosTest source set. For UI tests, XCTest is used in Xcode with the imported framework. Kotlin tests are written in commonTest with kotlin.test and run on the iOS simulator via the iosSimulatorArm64Test Gradle task.

What libraries are available in KMP?

Main KMP libraries: Ktor (networking), kotlinx.serialization (JSON), SQLDelight (DB), Koin (DI), Decompose (navigation), multiplatform-settings (SharedPreferences/NSUserDefaults), Apollo GraphQL, Firebase (via KMP-NativeCoroutines). Compose Multiplatform provides UI for all platforms.

Does KMP support Gradle 8?

Yes. KMP is fully compatible with Gradle 8.5+. Starting from Kotlin 2.1, official plugins support Gradle 8. Configuration via build.gradle.kts with kotlin("multiplatform") requires Gradle 7.6+, but version 8.5 is recommended for optimal build performance.

Summary

  • Kotlin Multiplatform — JetBrains cross-platform technology for shared code without replacing native UI
  • Expect/actual — a mechanism for declaring platform APIs in commonMain with implementations for each target
  • Shared module — a Gradle module with commonMain and platform-specific source sets (androidMain, iosMain)
  • Kotlin/Native compiles the shared module into an Apple framework callable from Swift and Objective-C
  • KMP vs Flutter/RN — logic reuse + native UI vs single codebase and UI
  • Compose Multiplatform — a Kotlin UI framework for Android, iOS, Desktop, and Web
  • Ecosystem — Ktor, SQLDelight, Koin, kotlinx.serialization, Decompose for all application layers

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also