skip to content

Multiplatform Libraries

What you are allowed to depend on in shared code: the portable stdlib subset, the kotlinx libraries, and third-party libraries that ship per-platform implementations. Library availability is usually the deciding factor in whether KMP fits a project.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

explore

questions

16

In a Kotlin Multiplatform project, what does it mean that the standard library is available in commonMain, and which kinds of APIs can you safely call there?

level: juniorimportance: must knowfreq 60%

answer

  1. commonMain = shared, compiles to all targets
  2. Common stdlib: collections, kotlin.math, Random, text, io basics
  3. No java.io.File, no full reflection in common
  4. expect/actual bridges to platform APIs
  5. Only lightweight KClass is common

basics

~20 s

commonMain is shared code that compiles to every target. There you can use the common part of Kotlin's standard library — lists, maps, math, random, strings, numbers — but not platform-only things like Java's File or full Java reflection.

solid answer

~30 s

Kotlin Multiplatform splits source into source sets: commonMain holds platform-agnostic code, and platform source sets (jvmMain, jsMain, nativeMain) hold target-specific code. The Kotlin standard library ships a common subset visible from commonMain: collections (List, Set, Map and their builders), kotlin.math (sin, sqrt, PI, abs), kotlin.random.Random, kotlin.text/string utilities, kotlin.sequences, and basic kotlin.io like println/readLine plus the kotlin.io.encoding Base64. What is NOT in commonMain: JVM-only java.io.File, java.util.*, and full kotlin.reflect beyond KClass (only ::class and KClass are common). If you need a platform API, you declare an expect function in common and provide an actual on each target.

code

kotlin · 11 lines
kotlin
// commonMain — all of this compiles on JVM, JS, Native
import kotlin.math.sqrt
import kotlin.random.Random

fun stats(xs: List<Double>): Pair<Double, Double> {
    val mean = xs.average()
    val variance = xs.map { (it - mean) * (it - mean) }.average()
    return mean to sqrt(variance)
}

fun roll() = Random.nextInt(1, 7)

go deeper

for a junior

Knows commonMain is shared code and can name a few common stdlib pieces like List and kotlin.math.

for a middle

Lists the concrete common APIs and correctly excludes java.io/java.util and full reflection.

for a senior

Explains the source-set model and uses expect/actual to bridge when an API is not common.

for a principal

Reasons about library API surface portability and would pick multiplatform libraries (kotlinx-io, Okio) over hand-rolled expect/actual for IO.

## What commonMain is A Kotlin Multiplatform (KMP) module is organized into **source sets**. `commonMain` contains code shared by **all** targets; `jvmMain`, `jsMain`, `iosMain`, etc. contain target-specific code. Code in `commonMain` is compiled once per target, so it may only reference APIs that exist on every target. ## The common stdlib subset Kotlin's standard library is itself multiplatform. The portion visible from `commonMain` includes: - **Collections**: `List`, `MutableList`, `Set`, `Map`, plus builders `listOf`, `mutableListOf`, `setOf`, `mapOf`, `buildList { }`, `buildMap { }`, and the whole operator family (`map`, `filter`, `fold`, `groupBy`, `associate`). - **kotlin.math**: `sin`, `cos`, `sqrt`, `abs`, `min`, `max`, `pow`, constants `PI` and `E`. This is the multiplatform replacement for `java.lang.Math`. - **kotlin.random.Random**: `Random.nextInt()`, `Random.nextDouble()`, `Random(seed)` for a reproducible generator, and `Random.Default`. - **kotlin.text**: `String` methods, `StringBuilder`, regex via `Regex`, `trim`, `split`, `format` (common subset). - **kotlin.sequences**: lazy `Sequence`, `asSequence`, `generateSequence`. - **kotlin.io basics**: `println`, `print`, `readln`/`readLine`, and `kotlin.io.encoding.Base64`. ## What is NOT common - `java.io.File`, `java.nio`, `java.util.*` — JVM-only, only usable from `jvmMain`. - **Full reflection**: `kotlin.reflect.full` (members, supertypes, calling) is JVM-only. From common you only get the **lightweight** `KClass` obtained via `obj::class` or `Type::class`, with `simpleName`/`qualifiedName` (the latter not on JS/Native). ## Bridging to platform APIs When common code needs something platform-specific, use the **expect/actual** mechanism: ```kotlin // commonMain expect fun currentTimeMillis(): Long // jvmMain actual fun currentTimeMillis(): Long = System.currentTimeMillis() ``` The `expect` declaration is a contract with no body; each target supplies an `actual`. This keeps `commonMain` pure while still reaching platform code.

  • If commonMain code needs to read a file, what do you do?
    Declare an expect function (or use a multiplatform IO library like kotlinx-io / Okio), and provide an actual per target — java.io.File only exists in jvmMain.
  • Is kotlin.math.sqrt the same as java.lang.Math.sqrt?
    kotlin.math is the common API; on JVM it delegates to java.lang.Math, but it also exists on JS and Native, which is why you use it in commonMain.

commonMain is the lowest common denominator menu every restaurant location can cook; platform source sets are the local specials.

saying these in an interview costs you the question

  • Claims java.io.File or java.util.* are available in commonMain
  • Thinks the entire JDK is reachable from shared code
  • Says full reflection works everywhere in KMP
  • Confuses commonMain (shared) with a platform source set

context

open as a page

Which core kotlinx libraries are commonly added to commonMain in a Kotlin Multiplatform project, and what does each provide?

level: juniorimportance: must knowfreq 70%

basics

~10 s

The main ones are kotlinx-coroutines-core for async work, kotlinx-serialization for turning objects to/from JSON, kotlinx-datetime for dates and times, and kotlinx-io for reading and writing bytes. All work in shared code.

open as a page

In Kotlin Multiplatform, what do the `expect` and `actual` keywords do, and why does a library use them to back one common API across targets?

level: juniorimportance: must knowfreq 70%

basics

~10 s

expect declares a shared API in common code with no body; each platform (Android, iOS, JS) provides an actual implementation. So you call one function everywhere and the right platform version runs.

open as a page

Explain Kotlin's read-only vs. mutable collection interfaces (List vs MutableList, etc.) and how the collection builders like buildList relate to them.

level: middleimportance: must knowfreq 70%

basics

~20 s

List, Set and Map are read-only views — they have no add/remove. MutableList, MutableSet, MutableMap add modification methods. buildList gives you a mutable list to fill in a lambda and returns it as a read-only List.

open as a page

How does kotlinx-serialization work in common code, and why is it suitable for multiplatform while reflection-based libraries are not?

level: middleimportance: must knowfreq 55%

basics

~20 s

You mark a class with @Serializable and a compiler plugin generates the conversion code at build time. Because it doesn't use runtime reflection, it works on platforms like iOS and JS where reflection isn't available.

open as a page

How does kotlin.random.Random work in the common stdlib, and how would you produce reproducible random sequences across platforms?

level: middleimportance: should knowfreq 45%

basics

~10 s

kotlin.random.Random is the multiplatform random generator. Use Random.nextInt() for one-off values, and create Random(seed) with a fixed seed to get the same sequence every run, on any platform.

open as a page

What does it mean that kotlinx libraries are published 'with -metadata', and how does Gradle resolve the right artifact for each target?

level: middleimportance: should knowfreq 45%

basics

~10 s

A multiplatform library publishes a small common piece plus one piece per platform. Gradle reads extra metadata that lists all these pieces and automatically picks the correct one for each target you build.

open as a page

How is `Dispatchers.Main` from kotlinx.coroutines backed differently on Android, iOS, and JS, and what must Android consumers add for it to work?

level: middleimportance: should knowfreq 55%

basics

~20 s

Dispatchers.Main is one common name, but each platform runs it on its own UI loop: Android's main Handler, iOS's main run loop, JS's event loop. On Android you must add the kotlinx-coroutines-android artifact so the real Main dispatcher is present.

open as a page

Ktor's `HttpClient` is a common API, but you must pick an engine per target. Explain the engine model and name appropriate engines for Android, iOS, and JS.

level: middleimportance: should knowfreq 50%

basics

~20 s

Ktor gives you one common HttpClient, but the actual network calls run through a pluggable engine you choose per platform: OkHttp or CIO on Android, Darwin on iOS, Js on the web. You configure the client in shared code and supply the engine per target.

open as a page

Walk through kotlin.math as the multiplatform alternative to java.lang.Math, including how it handles integer rounding, NaN, and division edge cases.

level: seniorimportance: should knowfreq 35%

basics

~20 s

kotlin.math gives you sqrt, sin, pow, abs, PI and so on for every platform, so commonMain doesn't need java.lang.Math. It follows IEEE-754: things like 0.0/0.0 give NaN, and there are safe rounding helpers like roundToInt.

open as a page

When should you use a Sequence instead of eager collection operators in commonMain, and what are the cross-platform performance and laziness implications?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Collection operators like map/filter build a new list at each step. A Sequence is lazy: it processes one element through all steps before moving on, so it avoids intermediate lists and can stop early. Use sequences for long pipelines or large/infinite data.

open as a page

What multiplatform constraints apply when using kotlinx-coroutines-core and kotlinx-io in common code, especially around dispatchers and IO primitives across targets?

level: seniorimportance: should knowfreq 30%

basics

~10 s

Coroutines work in common code, but some thread-based dispatchers and blocking calls only make sense on the JVM. kotlinx-io gives platform-neutral Buffer/Source/Sink so shared code can read and write bytes without java.io.

open as a page

Why does Kotlin Multiplatform need kotlinx-datetime, and what is the difference between Instant, LocalDateTime, and using a TimeZone?

level: seniorimportance: should knowfreq 40%

basics

~10 s

java.time only exists on the JVM, so KMP provides kotlinx-datetime. Instant is an exact moment in time; LocalDateTime is wall-clock date and time with no zone; a TimeZone converts between them.

open as a page

SQLDelight generates a common database API but needs a platform `SqlDriver`. Name the drivers per platform and explain how the generated `Database` is constructed from one.

level: seniorimportance: should knowfreq 40%

basics

~20 s

SQLDelight turns your .sq files into a common typed database API, but you must hand it a platform-specific SqlDriver to actually talk to SQLite: Android, native (iOS), and a web/JS driver. You build the driver per platform and pass it into the generated Database.

open as a page

What reflection capabilities are available from commonMain, and how do you design shared code that needs type information without depending on JVM-only kotlin.reflect.full?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

In shared code you only get lightweight class tokens via ::class (a KClass), with names and type checks. Listing properties or calling members needs JVM-only reflection, so for portable code you avoid reflection and use serialization plugins or type tokens instead.

open as a page

When designing a KMP library's public surface, when should you use `expect`/`actual` versus a plain interface with platform-supplied implementations? What are the tradeoffs?

level: principalimportance: nice to knowfreq 25%

basics

~20 s

Use expect/actual when there is exactly one natural implementation per platform that callers shouldn't choose. Use a plain interface injected per platform when you want multiple implementations, easy mocking, or looser coupling. Interfaces are more flexible; expect/actual is more rigid but compiler-enforced.

open as a page