skip to content

What are `expect` and `actual` functions in Kotlin Multiplatform, and how do you use them to expose platform-specific behavior through a common API?

level: juniorimportance: must knowfreq 70%

answer

  1. expect = bodiless declaration in commonMain
  2. actual = real body per target source set
  3. compiler: one actual per expect, every target
  4. signatures must match exactly
  5. defaults/type params only on expect side

basics

~20 s

In shared code you write expect fun with no body, like a promise. Each platform (Android, iOS, JVM, JS) provides the real actual fun. Common code calls the function without knowing which platform fills it in.

solid answer

~40 s

`expect`/`actual` is Kotlin Multiplatform's mechanism for declaring a single API in `commonMain` and supplying a per-target implementation. In `commonMain` you write a bodiless `expect fun platformName(): String`. In each target source set (`androidMain`, `iosMain`, `jvmMain`, etc.) you write `actual fun platformName(): String { ... }` with a real body. The signatures must match: same name, parameters, return type, and visibility. The compiler enforces that every `expect` has exactly one matching `actual` per target it compiles for. Common code calls `platformName()` directly and the linker binds the right `actual` per platform. This keeps business logic in shared code while delegating only the platform-touching parts (system clock, UUID, file paths) to native implementations.

code

kotlin · 10 lines
kotlin
// commonMain/Platform.kt
expect fun platformName(): String

fun greeting(): String = "Hello from " + platformName()

// androidMain/Platform.android.kt
actual fun platformName(): String = "Android"

// iosMain/Platform.ios.kt
actual fun platformName(): String = "iOS"

go deeper

for a junior

Can explain expect = declaration, actual = implementation, and write a basic pair.

for a middle

Knows the matching rules and that the compiler enforces one actual per target.

for a senior

Contrasts expect/actual with interface+DI and picks the right tool per use case.

for a principal

Reasons about API surface, evolution, and where platform seams belong in a large KMP module graph.

## The problem it solves Kotlin Multiplatform (KMP) lets you share Kotlin code across targets (Android/JVM, iOS/Native, JS, Wasm). Most logic is platform-agnostic, but some operations need platform APIs (e.g., generating a UUID, reading the current time, getting a device name). `expect`/`actual` lets the **common** code depend on a declaration while each **platform** supplies the implementation. ## How it works In `commonMain` you declare an **expected** function with **no body**: ```kotlin // commonMain expect fun platformName(): String ``` The `expect` keyword means "this exists; some target will provide it." In each platform source set you provide the **actual** function with the `actual` keyword and a real body: ```kotlin // androidMain actual fun platformName(): String = "Android " + android.os.Build.VERSION.SDK_INT // iosMain actual fun platformName(): String = platform.UIKit.UIDevice.currentDevice.systemName() ``` ## Matching rules The `actual` declaration must **structurally match** the `expect`: identical name, parameter list (types and order), return type, and compatible visibility/modifiers. Type parameters and default parameter values are declared **only on the `expect` side** — repeating a default value on `actual` is an error. ## Compiler enforcement The compiler verifies that **every `expect` has exactly one matching `actual`** in each target's compilation. A missing actual fails to compile; an extra or non-matching one also fails. This guarantee is the whole point: you can never ship a target that forgot to implement a shared declaration. ## Where it fits vs. alternatives `expect`/`actual` is one way to inject platform code. The other common approach is a plain **interface in common + per-platform implementation provided via DI or a factory**. Use `expect`/`actual` for thin, leaf-level platform calls; prefer interfaces when you want testability/mocking or multiple implementations. ## Key keywords/APIs - `expect` — declares the contract in `commonMain`. - `actual` — supplies the body per target source set (`androidMain`, `iosMain`, `jsMain`, …). - Hierarchical source sets (e.g., a shared `nativeMain`) can host one `actual` for several native targets.

  • What happens if you forget the `actual` for one target?
    That target fails to compile with an error saying the expected declaration has no actual; common and the other targets are unaffected.
  • Can common code see the platform-specific return value type?
    Only the declared return type from the `expect` signature. Common code can't reference platform-only types unless they're surfaced through the common signature.

expect is a job posting in the shared office; each branch office (platform) must hire someone (actual) who exactly fits the description.

saying these in an interview costs you the question

  • Thinking `expect` functions can have a body in commonMain
  • Putting the implementation in commonMain instead of a platform source set
  • Claiming the actual can have a different signature
  • Believing it's a runtime reflection mechanism rather than compile-time binding

context