The mobile development landscape offers two extremes: build everything twice with native development for each platform, or use a cross-platform framework that shares both logic and UI. Kotlin Multiplatform Mobile (KMM) occupies a practical middle ground — share the business logic, networking, and data layers across platforms while keeping the user interface fully native on Android and iOS.
At StrikingWeb, we have been evaluating and deploying KMM for clients who need to support both platforms without the overhead of maintaining completely separate codebases. The approach works particularly well for applications where business logic is complex but the UI needs to feel truly native on each platform.
The KMM Approach
Unlike React Native or Flutter, which provide their own UI layer that renders on both platforms, KMM focuses exclusively on shared logic. The UI is built natively — Jetpack Compose on Android and SwiftUI on iOS. The shared Kotlin code compiles to a Kotlin library on Android (where it runs natively) and to a native iOS framework through Kotlin/Native.
This architecture means the shared layer includes:
- Networking: API clients, request/response models, and data serialization
- Business logic: Validation rules, calculations, state machines, and workflow logic
- Data persistence: Local database operations using SQLDelight
- State management: Application state that drives the UI on both platforms
The platform-specific layer includes:
- User interface: Native views, animations, and interactions
- Platform APIs: Camera, GPS, biometrics, push notifications, and other device features
- Platform-specific libraries: Integrations that only exist on one platform
Code Sharing in Practice
In a typical KMM project, we share 50 to 70 percent of the codebase. The exact percentage depends on how much platform-specific functionality the app requires. For a data-heavy app like a financial dashboard, the shared percentage is higher because most of the work involves data fetching, transformation, and caching. For a camera-centric social app, the shared percentage is lower because more code is platform-specific.
Project Structure
A KMM project is organized into three modules: a shared module containing the common code, an Android application module, and an iOS application module. The shared module uses Kotlin's expect/actual mechanism to define platform-specific implementations for operations that differ between platforms.
// Shared module - commonMain
expect class PlatformLogger() {
fun log(message: String)
}
// Shared module - androidMain
actual class PlatformLogger {
actual fun log(message: String) {
android.util.Log.d("App", message)
}
}
// Shared module - iosMain
actual class PlatformLogger {
actual fun log(message: String) {
platform.Foundation.NSLog(message)
}
}
This pattern lets you write shared logic against common interfaces while providing platform-appropriate implementations where the behavior must differ.
The KMM Library Ecosystem
A healthy ecosystem of multiplatform libraries makes KMM practical for real applications:
- Ktor: JetBrains' multiplatform HTTP client for networking. Supports serialization, authentication, and custom plugins
- SQLDelight: Generates type-safe Kotlin APIs from SQL statements. Works on Android (with SQLite) and iOS (with SQLite through Kotlin/Native)
- Kotlinx.serialization: Multiplatform serialization for JSON, Protocol Buffers, and other formats
- Kotlinx.coroutines: Structured concurrency for asynchronous operations across both platforms
- Koin: Lightweight dependency injection that works across KMM modules
Architecture Patterns
We typically structure KMM projects using a clean architecture approach where the shared module is divided into layers — domain, data, and presentation. The domain layer contains business entities and use cases. The data layer handles API calls and local storage. The presentation layer exposes state through observable patterns that both platforms can consume.
The key insight of KMM is that business logic does not change between platforms. Whether a user is on Android or iOS, the rules for processing a payment, validating an address, or calculating shipping costs are identical. KMM lets you write and test these rules once.
iOS Integration
The iOS integration is where KMM faces its biggest challenges. The shared Kotlin code compiles to an Objective-C compatible framework that Swift can consume. While this works, the interoperability is not seamless:
- Kotlin generics have limited mapping to Swift generics
- Kotlin coroutines require wrapper functions to work with Swift's async/await
- Kotlin sealed classes appear as class hierarchies in Swift rather than native enums
- IDE support for Kotlin code in Xcode is limited compared to Android Studio
These friction points are real but manageable. The community has developed patterns and libraries (like SKIE and KMP-NativeCoroutines) that smooth the interop. With each Kotlin release, the iOS integration improves.
When KMM Fits
KMM is ideal when the team has strong Kotlin expertise and wants native UIs on both platforms, when the application has significant business logic that would otherwise be duplicated, when the iOS team is comfortable consuming a Kotlin framework rather than writing all code in Swift, and when the organization values code consistency for business rules across platforms.
KMM is less suitable when the team is primarily a Swift and JavaScript team with no Kotlin experience, when the application is mostly UI with minimal shared logic, or when build time and toolchain complexity are primary concerns for a small team.
KMM vs. Flutter vs. React Native
Each approach serves different situations. Flutter provides the highest code sharing with a single UI for both platforms but uses a custom rendering engine that does not always match native platform patterns. React Native shares UI and logic using JavaScript but faces performance challenges with complex animations and heavy computation. KMM shares the least code by default but preserves the most native character on each platform.
For StrikingWeb clients who prioritize native user experience and have complex business logic, KMM consistently delivers the best results. For clients who prioritize development speed and can accept some native feel trade-offs, Flutter is often the better choice.
Getting Started
JetBrains provides excellent tooling for KMM through the KMM plugin for Android Studio. Creating a new KMM project generates the shared module, Android app, and iOS app with a working sample. From there, start by moving networking code into the shared module — it is typically the easiest layer to share and provides immediate benefits.
At StrikingWeb, we help teams evaluate the right mobile architecture for their specific requirements and build applications that deliver genuine native quality. Whether KMM, Flutter, or fully native development is the right approach depends on your team, timeline, and product requirements — and we are happy to help you make that decision.