Kotlin Multiplatform Intro
Reviewed & published by Brayan K
Kotlin Multiplatform (KMP) lets you write core logic once and run it on Android, iOS, the JVM, and beyond — sharing models, networking, and business rules while keeping native UI where it belongs.
Part of the free Kotlin course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
By the end you'll understand the commonMain shared module, the expect/actual mechanism, and what's worth sharing versus keeping platform-specific.
What You'll Learn in This Lesson
- What Kotlin Multiplatform is
- The commonMain shared source set
- Declaring APIs with expect
- Implementing them with actual
- Platform source sets (androidMain, iosMain)
- What to share vs keep native
1️⃣ The Shared Module (commonMain)
Code in commonMain compiles for every target. Pure Kotlin logic, data models, and serialization live here and are reused unchanged across platforms.
// === commonMain ===
// Shared code that compiles for every target platform.
@kotlinx.serialization.Serializable
data class Greeting(val message: String)
// Pure Kotlin business logic — shared everywhere.
class Greeter {
fun greet(name: String): Greeting {
return Greeting("Hello, $name, from shared code!")
}
}One Greeter, used by every platform — no copy-paste between a Swift version and a Kotlin version.
2️⃣ expect / actual
When you need platform-specific behavior, declare an expect in common code and provide an actual in each platform source set such as androidMain and iosMain.
// === commonMain ===
// 'expect' declares an API the shared code can use,
// but leaves the implementation to each platform.
expect fun platformName(): String
fun describePlatform(): String = "Running on ${platformName()}"
// === androidMain ===
// 'actual' provides the Android-specific implementation.
actual fun platformName(): String = "Android"
// === iosMain ===
// 'actual' provides the iOS-specific implementation.
actual fun platformName(): String = "iOS"Your turn. Replace the TODO in commonMain, then reason about each platform's result.
// 🎯 YOUR TURN — replace the TODO in commonMain.
// === commonMain ===
// 1) Declare a common API that each platform must implement.
// 👉 replace TODO with: expect fun appVersion(): String
TODO
fun versionBanner(): String = "App version: ${appVersion()}"
// === androidMain ===
actual fun appVersion(): String = "1.0-android"Mini-Challenge: A Shared Formatter
Write a pure function for commonMain and explain why it needs no platform code.
// 🎯 MINI-CHALLENGE: a shared formatter.
//
// In commonMain:
// 1. Write a pure Kotlin function format(price: Double): String
// that returns "$" followed by the price (e.g. "$9.99").
// 2. Explain (in a comment) why this can live in commonMain
// and be reused unchanged on Android and iOS.
//
// ✅ format(9.99) -> "$9.99" (same result on every platform)
// === commonMain ===
fun format(price: Double): String {
// your code here
return ""
}Common Errors (and the fix)
- Missing an actual — Every target compiling an expect must provide an actual, or it won't compile.
- Platform APIs in commonMain — Calling Android or iOS APIs directly in common code fails; hide them behind expect/actual.
- Wrong source set — Put shared logic in commonMain, platform code in androidMain/iosMain.
- Signature mismatch — An actual must match its expect signature exactly.
Pro Tips
- 💡 Share the logic, not the UI (at first): start by sharing models and business rules; add Compose Multiplatform later.
- 💡 Use multiplatform libraries: kotlinx.serialization and Ktor work in commonMain.
- 💡 Keep expect/actual small: minimize the platform surface to maximize reuse.
📋 Quick Reference — Kotlin Multiplatform
| Concept | Meaning / Kotlin |
|---|---|
| commonMain | Shared source set for all targets |
| androidMain / iosMain | Platform-specific source sets |
| expect fun f(): T | Common API, no body |
| actual fun f(): T | Per-platform implementation |
| Typically shared | Models, logic, networking, serialization |
| Often platform-specific | Native UI and direct OS APIs |
🎉 Lesson Complete!
- ✅ KMP shares Kotlin across Android, iOS, JVM and more
- ✅ commonMain holds platform-agnostic code
- ✅ expect declares an API; actual implements it
- ✅ Platform source sets hold native specifics
- ✅ Share logic and models; keep native UI per platform
- ✅ You've completed the Android & Modern Kotlin track!
Practice quiz
What does Kotlin Multiplatform (KMP) let you do?
- Run JavaScript only
- Share Kotlin code across platforms like Android, iOS, and the JVM
- Replace Gradle
- Write apps in Java
Answer: Share Kotlin code across platforms like Android, iOS, and the JVM. KMP lets you write common logic once and target multiple platforms.
Which source set holds code shared across all platforms?
- androidMain
- iosMain
- commonMain
- sharedTest
Answer: commonMain. commonMain contains platform-agnostic code shared by every target.
What is the 'expect' keyword used for?
- To declare an API in common code that each platform must implement
- To throw an exception
- To import a library
- To define a coroutine
Answer: To declare an API in common code that each platform must implement. expect declares a common API; platforms provide it with actual.
What does the 'actual' keyword provide?
- A test stub
- A platform-specific implementation of an expect declaration
- A default value
- A null check
Answer: A platform-specific implementation of an expect declaration. actual supplies the concrete, per-platform implementation of an expect declaration.
Where would an iOS-specific implementation typically live?
- commonMain
- androidMain
- jvmTest
- iosMain
Answer: iosMain. iosMain holds the iOS-specific actual implementations and platform code.
Which of these is commonly shared in a KMP project?
- Per-platform UI widgets only
- Business logic, networking, and data models
- Android XML layouts
- iOS storyboards
Answer: Business logic, networking, and data models. Shared modules typically hold business logic, models, networking, and serialization.
What is usually NOT fully shared and stays platform-specific?
- Data classes
- Pure Kotlin algorithms
- Native UI and platform APIs
- String constants
Answer: Native UI and platform APIs. Native UI and direct platform APIs are typically implemented per platform (though Compose Multiplatform can share UI).
An expect declaration without a matching actual on a target causes what?
- It silently does nothing
- A compile error for that target
- A runtime crash only
- Automatic code generation
Answer: A compile error for that target. Every target compiling the common code must provide an actual, or it fails to compile.
Which build tool orchestrates a typical Kotlin Multiplatform project?
- Maven only
- npm
- Gradle with the Kotlin Multiplatform plugin
- Make
Answer: Gradle with the Kotlin Multiplatform plugin. KMP projects are configured with Gradle and the Kotlin Multiplatform plugin.
What is a key benefit of sharing code with KMP?
- It removes the need for testing
- Write core logic once and reuse it, reducing duplication and bugs
- It compiles to assembly
- It bans platform-specific code
Answer: Write core logic once and reuse it, reducing duplication and bugs. Sharing common logic cuts duplication and keeps behavior consistent across platforms.
Continue this course
- Previous: Networking with Retrofit
- Next: Local Persistence with Room — Entities, DAOs and reactive Flow queries over SQLite with the Room library
- Quick reference: Kotlin cheat sheet
Frequently asked questions
What problem does Kotlin Multiplatform solve?
It lets you write code once in Kotlin and reuse it across platforms — Android, iOS, desktop JVM, web, and more — instead of duplicating the same business logic in Swift, Kotlin, and JavaScript. You typically share the non-UI layers (models, networking, validation, business rules) while keeping native UI per platform, which reduces duplication and keeps behavior consistent.
How are KMP projects organized into source sets?
Code lives in source sets. commonMain holds platform-agnostic code shared by every target. Platform source sets like androidMain, iosMain, and jvmMain hold code specific to those platforms, including the actual implementations and access to native APIs. A Gradle build with the Kotlin Multiplatform plugin wires these source sets to their targets.
What are expect and actual?
They're the mechanism for platform-specific behavior behind a shared API. In commonMain you write an expect declaration — a function, property, or class signature with no body. Each platform source set provides a matching actual with the real implementation. Common code calls the expect declaration normally; the compiler links it to the right actual per target. Every target must supply an actual, or compilation fails.
What can and can't I share?
You can share pure Kotlin logic, data models, serialization, validation, and (with libraries like Ktor) networking. Truly platform-specific things — direct OS APIs, sensors, and traditionally native UI — stay per platform, exposed through expect/actual when needed. Note that Compose Multiplatform increasingly lets you share UI too, but classic KMP focuses on sharing the logic layer.