Kotlin Multiplatform (KMP) is a technology from JetBrains that lets developers write shared code in Kotlin once and compile it for several platforms, including Android, iOS, desktop, web and server, while keeping full access to each platform’s native APIs. In mobile app development it is used mainly to share business logic, networking, data storage and validation between Android and iOS apps, while the user interface can remain native or be shared as well.
Unlike frameworks that run an app inside a separate runtime, KMP compiles to each platform’s native format: JVM bytecode for Android, native binaries through Kotlin/Native for iOS, and JavaScript or WebAssembly for the web. This makes it possible to adopt it gradually inside existing apps, one module at a time, which is a key reason why many teams choose it over a full rewrite.
How Kotlin Multiplatform shares code
A KMP project is organized into source sets. The commonMain source set holds code that is compiled for every target, and platform source sets such as androidMain and iosMain hold code specific to one platform. Common code may use only multiplatform libraries and the Kotlin standard library, while platform code can call any Android or iOS API directly.
When common code needs a platform capability, the developer declares an expect declaration in common code and provides an actual implementation for each platform. For example, a common expect fun deviceName(): String gets one actual implementation using Android APIs and another using UIKit. Many teams prefer plain interfaces with dependency injection for the same purpose, keeping expect and actual for small, low-level pieces.
| Target | Compiler backend | Output | Typical use |
|---|---|---|---|
| Android and JVM | Kotlin/JVM | JVM bytecode | Android apps, servers, desktop |
| iOS and macOS | Kotlin/Native (LLVM) | Native framework for Xcode | iOS apps and Apple platforms |
| Web | Kotlin/JS or Kotlin/Wasm | JavaScript or WebAssembly | Browser applications |
| Desktop native | Kotlin/Native | Native executable | Command-line tools, Linux and Windows |
Sharing logic versus sharing the user interface
KMP supports a spectrum of sharing, from a single shared module to an almost entirely shared app. The UI decision is the most important architectural choice, because it determines how native the app feels and how much code is reused.
| Approach | What is shared | User interface | Typical code reuse |
|---|---|---|---|
| Single shared module | One area such as networking or validation | Native on each platform | Low, chosen for gradual adoption |
| Shared business logic | Data, domain and networking layers | Jetpack Compose and SwiftUI | Moderate to high for non-UI code |
| Shared logic and UI | Almost everything, using Compose Multiplatform | One Compose UI on all platforms | Highest |
Compose Multiplatform, also from JetBrains, extends Jetpack Compose to iOS, desktop and web. It lets teams share screens as well as logic, while still allowing native SwiftUI or UIKit views where a platform-specific look or API is required. Teams that value a fully native iOS look often keep SwiftUI for screens and share everything beneath them.
Kotlin Multiplatform compared with other cross-platform frameworks
Kotlin Multiplatform is often compared with Flutter, React Native and .NET MAUI. The key difference is that KMP treats sharing as optional and native integration as the default, whereas most alternatives start from a shared UI layer.
| Technology | Language | UI approach | Native integration |
|---|---|---|---|
| Kotlin Multiplatform | Kotlin | Native UI or Compose Multiplatform | Direct calls to platform APIs |
| Flutter | Dart | Own rendering engine and widgets | Through platform channels and plugins |
| React Native | JavaScript or TypeScript | Native components driven from JavaScript | Through native modules |
| .NET MAUI | C# | Abstraction over native controls | Through .NET bindings |
Because KMP produces ordinary Android libraries and iOS frameworks, an existing native app can consume a shared module like any other dependency. That makes it attractive for teams with established native codebases, while Flutter or React Native often suit greenfield apps that want one UI from the start.
Tooling, libraries and project setup
KMP projects are built with Gradle and developed mainly in Android Studio or IntelliJ IDEA, while Xcode is still needed to build, sign and run the iOS app. The shared code is delivered to iOS as an XCFramework, either directly or through Swift Package Manager or CocoaPods integration.
A mature ecosystem of multiplatform libraries covers the most common needs: Ktor for HTTP networking, kotlinx.serialization for JSON, kotlinx.coroutines for concurrency, SQLDelight or Room for local databases, and Koin for dependency injection. Choosing libraries with multiplatform support from the start avoids rewriting modules later.
- Start with a stable, logic-heavy module such as networking or data validation.
- Keep platform code thin and test shared logic with common unit tests.
- Agree on iOS integration early, including how Swift developers consume the shared API.
- Pin Kotlin and library versions across modules to avoid binary incompatibilities.
Limitations and practical pitfalls
KMP is production-ready, but it introduces trade-offs that teams should plan for. Most of them concern the iOS side, where Kotlin code is consumed from Swift.
| Pitfall | Effect | Mitigation |
|---|---|---|
| Swift interoperability gaps | Some Kotlin features look awkward in Swift | Design a Swift-friendly API or use interop tools |
| Longer iOS build times | Slower feedback for iOS developers | Modularize and cache builds |
| Team skill imbalance | iOS developers must read and review Kotlin | Shared ownership and training |
| Library gaps | Some SDKs exist only for one platform | Wrap them behind interfaces in platform code |
| Debugging across languages | Harder tracing from Swift into Kotlin | Use IDE plugins for native debugging |
Another mistake is sharing too much too early. Code that differs between platforms by design, such as permission flows or platform-specific UI behavior, often becomes more complex when forced into common code.
Explained without jargon
Kotlin Multiplatform lets a team write the hidden parts of an app once and use them on both Android phones and iPhones. Think of a restaurant chain with one central kitchen. The recipes are the same everywhere, but each restaurant still decorates its dining room in its own way.
The shared kitchen is the app’s logic, such as loading data and doing calculations. The dining room is what users see and touch. This saves work and keeps both apps behaving the same way.
A typical use case
A mid-sized fitness company maintains separate Android and iOS apps, and the two versions often calculate training statistics slightly differently, which confuses users who switch phones. The team moves the statistics engine, the API client and the offline database into a Kotlin Multiplatform module, while keeping the existing Jetpack Compose and SwiftUI screens.
Over two releases, about half of the non-UI code becomes shared, and both apps now produce identical results from the same tested logic. New features in the data layer are written once, and the iOS developers consume them through an XCFramework added with Swift Package Manager. The company reduces duplicated bug fixes without rewriting either app or changing how they look.