← All shorts
43Kotlin 1.9+2 min read

expect/actual: The Core of Shared Kotlin Code

expect/actual lets shared Kotlin code declare an API once and have each platform supply its own implementation.

Shared business logic still needs a handful of things only the platform knows: the OS version, a secure store, a UUID generator. expect/actual is Kotlin Multiplatform's mechanism for declaring that need once in common code and satisfying it separately on each target, with the compiler enforcing the contract.

#The old way

Before KMP, this meant two parallel classes with no shared type: a PlatformInfo object on Android built from android.os.Build, and a completely separate Swift PlatformInfo struct on iOS reading UIDevice. Any code that needed platform info had to be duplicated alongside it, once per codebase.

#The new way

expect class Platform() {
    val name: String
    val osVersion: String
}

fun buildUserAgent(): String {
    val platform = Platform()
    return "MyApp/1.0 (${platform.name}; ${platform.osVersion})"
}
actual class Platform actual constructor() {
    actual val name: String = "Android"
    actual val osVersion: String = android.os.Build.VERSION.RELEASE
}
actual class Platform actual constructor() {
    actual val name: String = "iOS"
    actual val osVersion: String = UIDevice.currentDevice.systemVersion
}

buildUserAgent() lives once in commonMain and compiles against whichever actual class Platform is on the classpath for the target being built.

#Why it matters

  • One call site (buildUserAgent()) instead of two divergent implementations that can silently drift apart
  • The compiler fails the build if an actual declaration is missing or its signature doesn't line up, not at runtime
  • Shared code can depend on platform behavior without depending on any platform framework directly

#Gotcha

expect/actual class declarations are still gated behind an opt-in on Kotlin 1.9.x — without @OptIn(ExperimentalMultiplatform::class) on the expect declaration, or -Xexpect-actual-classes in your compiler args, the build emits a warning (and on some toolchain configurations, an error) even though the code is otherwise correct. Function- and property-level expect/actual don't need this; only expect classes do.

kmpkotlincross-platform

Related shorts