Ideas Engineered for Tomorrow
We Engineer Services & Solutions for Your Business Needs
Home About
Products
Services
Hire
Industries
Consulting
Partners
Articles Careers Contact
Software Development

Kotlin Multiplatform: Share Code Across Android, iOS, and Web

Share business logic in Kotlin while keeping native UI on each platform — the pragmatic middle ground between fully native and fully cross-platform development.

💻 Mobile Development February 13, 2026 13 min read

Kotlin Multiplatform (KMP) is JetBrains' answer to the cross-platform question: what if you could share the boring parts (networking, data, business logic) while keeping native UI on each platform? Unlike Flutter or React Native, KMP doesn't touch your UI layer. You write Compose on Android, SwiftUI on iOS, and React on web — with shared Kotlin code underneath. It's now stable, production-ready, and used by companies like Netflix, Cash App, and VMware.

📋 Table of Contents

What KMP Actually Is (and Isn't)

KMP compiles Kotlin code to multiple targets: JVM (Android), native binary (iOS via Kotlin/Native), JavaScript (web), and even WebAssembly. The key insight: you write shared modules in Kotlin for business logic, networking, and data handling, then consume those modules from platform-specific code.

What KMP Is

What KMP Is Not

KMP Architecture

A typical KMP project has three source sets:

project/
├── shared/                       # KMP shared module
│   └── src/
│       ├── commonMain/           # Shared code (all platforms)
│       │   └── kotlin/
│       │       ├── data/         # Repositories, data sources
│       │       ├── domain/       # Use cases, business logic
│       │       ├── model/        # Data classes, DTOs
│       │       └── network/      # API clients (Ktor)
│       ├── androidMain/          # Android-specific implementations
│       │   └── kotlin/
│       │       └── platform/     # Android platform APIs
│       └── iosMain/              # iOS-specific implementations
│           └── kotlin/
│               └── platform/     # iOS platform APIs (expect/actual)
├── androidApp/                   # Android app (Compose UI)
│   └── src/main/
│       └── kotlin/
│           └── ui/               # Jetpack Compose screens
└── iosApp/                       # iOS app (SwiftUI)
    └── Sources/
        └── Views/                # SwiftUI views

The expect/actual Pattern

When shared code needs platform-specific behavior, KMP uses expect/actual declarations:

// commonMain — declare what you need
expect class PlatformContext

expect fun getPlatformName(): String

expect class SecureStorage(context: PlatformContext) {
    fun save(key: String, value: String)
    fun get(key: String): String?
}

// androidMain — provide Android implementation
actual typealias PlatformContext = android.content.Context

actual fun getPlatformName(): String = "Android ${Build.VERSION.SDK_INT}"

actual class SecureStorage actual constructor(
    private val context: PlatformContext
) {
    private val prefs = EncryptedSharedPreferences.create(/*...*/)

    actual fun save(key: String, value: String) {
        prefs.edit().putString(key, value).apply()
    }

    actual fun get(key: String): String? = prefs.getString(key, null)
}

// iosMain — provide iOS implementation
actual class PlatformContext  // No-op wrapper on iOS

actual fun getPlatformName(): String = UIDevice.currentDevice.systemName +
    " " + UIDevice.currentDevice.systemVersion

actual class SecureStorage actual constructor(context: PlatformContext) {
    actual fun save(key: String, value: String) {
        // Use iOS Keychain via SecurityFramework
        val data = value.encodeToByteArray().toNSData()
        SecItemAdd(mapOf(kSecClass to kSecClassGenericPassword, /*...*/) as CFDictionary, null)
    }

    actual fun get(key: String): String? {
        // Read from Keychain
    }
}

What to Share (and What Not To)

Share This Keep Platform-Specific
Data models and DTOsUI components
API clients and networkingNavigation
Business logic and validationPlatform permissions
Database queries (SQLDelight)Push notification handling
Serialization/deserializationPlatform-specific animations
State management / ViewModelsBiometric authentication
Analytics event definitionsDeep link handling

In practice, teams share 40-60% of their total codebase with KMP. That number may sound lower than Flutter's 90%+, but remember: the shared code is the business-critical code — the logic that must behave identically on every platform. Bugs fixed once are fixed everywhere.

The KMP Ecosystem

Need Library Notes
NetworkingKtor ClientJetBrains' HTTP client, multiplatform native
Serializationkotlinx.serializationJSON, Protobuf, CBOR — no reflection
DatabaseSQLDelightType-safe SQL, generates Kotlin from .sq files
Coroutineskotlinx.coroutinesFull multiplatform support including iOS
Date/Timekotlinx-datetimePlatform-independent date handling
DIKoinLightweight, KMP-native DI
Settings / KV storagemultiplatform-settingsSharedPreferences / NSUserDefaults wrapper
Image loadingCoil 3KMP support since v3
LoggingKermit (Touchlab)Multiplatform logging with platform formatters

The ecosystem has reached a critical mass where you can build a complete data layer without needing platform-specific implementations for most things. Ktor + kotlinx.serialization + SQLDelight covers 80% of what a typical app's shared module needs.

iOS Interop: The Real Challenge

KMP's biggest friction point is iOS integration. Kotlin/Native compiles to a framework that Swift code imports, but the interop isn't seamless:

Challenges

Solutions

// With SKIE — Kotlin suspend functions become Swift async
// Kotlin (shared module)
class OrderRepository(private val api: OrderApi) {
    suspend fun getOrders(): List<Order> = api.fetchOrders()
}

// Swift (iOS app) — SKIE generates async wrappers
// No manual bridging needed!
struct OrdersView: View {
    @State private var orders: [Order] = []

    var body: some View {
        List(orders) { order in
            OrderRow(order: order)
        }
        .task {
            do {
                orders = try await repository.getOrders()
            } catch {
                // handle error
            }
        }
    }
}
Practical advice: If your team doesn't have at least one developer comfortable with both Kotlin and Swift, KMP will be painful. The iOS interop issues are solvable, but they require someone who understands both worlds. If your team is all-JavaScript, React Native is a better fit. If they're all-Dart, Flutter.

Compose Multiplatform: Sharing UI Too

Compose Multiplatform extends JetBrains' story from shared logic to shared UI. You write Jetpack Compose code once and it renders on Android, iOS, desktop, and web. For iOS, it uses Skiko (a Skia-based rendering engine) — similar to Flutter's approach.

Approach Code Sharing UI on iOS Best For
KMP + Native UI40-60%SwiftUI (native)Best native feel
KMP + Compose Multiplatform80-95%Compose (rendered via Skia)Maximum sharing, branded UI

Compose Multiplatform for iOS is stable but newer than KMP itself. If you're already a Jetpack Compose shop and want the most code sharing possible from Kotlin, it's worth evaluating. If native UI feel on iOS is important, stick with KMP + SwiftUI.

Migration Strategy

You don't need to rewrite everything. KMP is designed for incremental adoption:

  1. Start with data models: Move your shared data classes to a KMP module. These are the easiest to share and have no platform dependencies
  2. Add networking: Replace your Retrofit/Alamofire clients with Ktor in the shared module. Both platforms consume the same API client
  3. Share business logic: Move validation rules, business calculations, and use cases to the shared module
  4. Add local storage: Replace Room/Core Data with SQLDelight for new features. Existing data stores can coexist with SQLDelight during migration
  5. Share ViewModels (optional): If your architecture supports it, share ViewModel logic. This gives the highest code sharing but requires the most interop work on iOS
Our experience at Pillai Infotech: The projects where KMP works best are those with complex business logic that must be identical across platforms — fintech calculations, health data processing, or rules engines. If your app is primarily UI with simple CRUD operations, the overhead of KMP may not justify the code sharing benefit. Evaluate honestly whether the shared logic is substantial enough to warrant the setup cost.

Frequently Asked Questions

Is Kotlin Multiplatform production-ready?

Yes. KMP has been stable since late 2023 and is used in production by Netflix, Cash App, Philips, VMware, and many others. Google officially recommends it for sharing business logic between Android and iOS.

Should I choose KMP or Flutter?

KMP if you want native UI on each platform (Compose + SwiftUI) and your team knows Kotlin. Flutter if you want maximum code sharing including UI and your team can learn Dart. See our framework comparison for a detailed analysis.

Does KMP add overhead to app size?

The Kotlin/Native runtime adds approximately 2-4 MB to the iOS app. On Android, the overhead is negligible since Kotlin is already the standard language. For most apps, this size increase is acceptable.

Can iOS developers work on KMP shared code?

Kotlin syntax is close enough to Swift that iOS developers can read and contribute to shared code. JetBrains' Fleet and IntelliJ both support KMP development. The learning curve is primarily around Kotlin-specific features like coroutines and extension functions.

What about Kotlin/Wasm and web support?

Kotlin/Wasm is in alpha and improving rapidly. For web targets, Kotlin/JS is stable and can share code with a React frontend. Full-stack Kotlin (Ktor server + KMP shared + Compose Multiplatform web) is becoming a viable architecture for teams all-in on Kotlin.

💻

Pillai Infotech LLP

We build cross-platform apps with Kotlin Multiplatform, sharing business logic while keeping native UI. Discuss your KMP project with our team.

Related Articles

Android App Development with Kotlin: 2026 Best Practices → Cross-Platform Mobile Frameworks Compared → iOS App Development with Swift: What's New in 2026 →