Creating a shared module

At the core of Kotlin multiplatform is shared code. To start building our project, we'll begin by creating a new Gradle module that will contain our shared Kotlin code:

  1. First, we'll create a new, empty Gradle project within IntelliJ. When we're finished, we should have a project structure that looks similar to the following:
  root
.gradle/
.idea/
gradle/
build.gradle
gradlew
gradlew.bat
settings.gradle

  1. We now want to update build.gradle so that we can define classpath dependencies, which will be needed by other modules. The resulting build.gradle file should look something like this:
    buildscript {
    ext {
    kotlin_version = '1.3.31'
    }

    repositories {
    google()
    jcenter()
    mavenCentral()
    }
    dependencies {
    classpath 'com.android.tools.build:gradle:3.2.0'
    classpath "org.jetbrains.kotlin:kotlin-gradle-
    plugin:
    $kotlin_version"
    }
    }

    allprojects {
    repositories {
    google()
    jcenter()
    maven { url 'https://dl.bintray.com/kotlin/kotlin' }
    }
    }

    With this in place, our Android project will have the Gradle plugins it needs once that project is added.

    1. Next, within this new project directory, we're going to create another Gradle module named :core to store our shared Kotlin code. This module should have the following structure:
    root/core
    build.gradle
    src/commonMain/kotlin
    src/androidMain/kotlin
    src/iosMain/kotlin
    src/jsMain/kotlin

    This :core module is where we will configure which platforms our multiplatform project will target, and it's where we will define common Kotlin types, interfaces, and models. Within the src/ directory, we've defined source sets that will correspond to each of our target platforms, and a common source set to define the common types.

    To ensure that our root-level project recognizes the new :core module, we will update settings.gradle to include the new module:

    // settings/gradle
    include
    'core'

    As we continue adding new modules to our project, we'll be updating settings.gradle accordingly.

    1. Now that our :core module is in place, we will update core/build.gradle to define our multiplatform build targets using the kotlin-multiplatform Gradle plugin, as follows:
    plugins {id "org.jetbrains.kotlin.multiplatform" }

    kotlin {
    js()
    jvm("android")
    // Change to iosArm64 (or iosArm32) to
    // build library for iPhone device
    iosX64("ios") {
    binaries { framework("core") }
    }

    sourceSets {
    commonMain {
    dependencies {
    implementation kotlin('stdlib-common')
    }
    }
    androidMain {
    dependencies {
    implementation kotlin('stdlib')
    }
    }
    iosMain { }
    jsMain {
    dependencies {
    implementation kotlin('stdlib-js')
    }
    }
    }
    }

    Notice at the top of core/build.gradle that we've added plugins {id "org.jetbrains.kotlin.multiplatform" }. This plugin gives us access to Kotlin multiplatform configuration functions that control how our multiplatform project should be built. Within the kotlin {} block, we define each of our build targets using the various platform-specific functions:

      • js()
      • jvm("android")
      • iosX64("ios")

    These indicate to the compiler what build targets we are expecting. Finally, within the sourceSets {} block, we can define further configuration and dependencies for specific platforms. Notice that we've added Kotlin standard library dependencies for each build target. By providing platform-specific dependencies, we can ensure that we have common Kotlin functionality in the source files for each platform.

    1. With our build configuration in place, it's time to start adding some shared Kotlin code. To begin, we'll create a new Kotlin file, core/src/commonMain/kotlin/Sample.kt, and this file will define a Platform class to be available across all source sets:
    expect object Platform {
    val name: String
    }

    The Platform object contains a single property, name, which we will use to provide platform-specific names from our shared :core module. This is possible because of the expect modifier we've added to the declaration of Platform. By using expect, we indicate to the compiler that each of our target source sets should contain a platform-specific implementation of the Platform type.

    1. Now that we've added an expect Platform object, we will create the platform-specific implementations within our other source sets, starting with Android. We'll create a new file, core/src/androidMain/kotlin/AndroidSample.kt, and define Platform for our Android source set. The following code shows this:
    // core/src/androidMain/kotlin/AndroidSample.kt
    actual object
    Platform {
    actual val name: String = "Android"
    }

    Notice that our declaration of Platform looks very similar to the original, but with two key differences:

    • We've added the actual modifier to the declaration of Platform.
    • We've added the actual modifier to the declaration of the name property.

    The actual modifier is how the compiler knows which actual types to match to any expect types that have been defined in our common source sets.

    Now, we'll repeat this process for our iOS and JavaScript targets:

    1. First, we'll add the actual modifier for our iOS sources:
    // core/src/iosMain/kotlin/iosSample.kt
    actual object
    Platform {
    actual val name: String = "iOS"
    }
    1. Now, we'll add the actual modifier for our JavaScript sources:
    // core/src/jsMain/kotlin/JsSample.kt
    actual object
    Platform {
    actual val name: String = "Javascript"
    }

    With those added files in place, we now have a Platform object that can be used generically from any shared code, but will be swapped out with platform-specific implementations at compile time for each target. The expect and actual modifiers allow us to define types similarly to how we might define an interface, and then provide actual implementations for each platform based on the needs of that platform.

    Now that our :core module is in place, we'll start adding platform-specific projects that will consume our common code. We'll start by adding the Android project.