When common code needs something only a platform can supply, it declares an expect signature with no body, and every target's source set provides a matching actual in the same package. BookNest's shared module asks each platform for its name:
expect fun platformName(): String // each target supplies its own
actual fun platformName(): String = "JVM " + System.getProperty("java.version")
actual fun platformName(): String = "JavaScript on Node.js " + js("process.version")The compiler checks the pairing per target. In a copy of the project with the JavaScript actual deleted, ./gradlew --continue :shared:compileKotlinJvm :shared:compileKotlinJs compiled the JVM target and stopped the JavaScript one:
> Task :shared:compileKotlinJs FAILED
e: file:///home/dev/lane-ch5/kmp-broken/shared/src/commonMain/.../Catalog.kt:34:1 The 'expect'
declaration 'platformName' has no 'actual' declaration in module '<commonMain> for JS'.
> Task :shared:compileKotlinJvm
BUILD FAILED in 19sA second experiment put "%.2f".format(book.price) into commonMain. The JVM target compiled it, because the JVM standard library has String.format(); the JavaScript target and the common metadata compilation both failed with Unresolved reference 'format' on receiver of type 'String'. So compile every target in CI, and see formatPrice() in Shared Data Layer for the portable way.
Functions and properties are stable; expect class and actual class are still Beta and print a warning unless you pass -Xexpect-actual-classes. JetBrains advises ordinary language features where they suffice: an interface in common code (say, BookCache), implemented per platform and passed in through a constructor or Koin, also allows a fake in commonTest.