Twitter LinkedIn Github

JetBrains

Kotlin 1.2 brings with it experimental support for multi-platform projects and last week at KotlinConf we showed how you can now use Kotlin to target the Jvm, the Web, Android and iOS. The source code for the actual KotlinConf is availabe on GitHub, and while it’s a great example of multi-platform project, it’s got a lot of moving pieces and opening it for the first time can be overwhelming.


I’m currently preparing a two week road trip to Asia, and one of the things I’ll be talking about is precisely MPPs. So I’ve prepared a very simple sample project which is now available on GitHub, and in this blog post I’ll walk you through the different parts.

Project Structure

The project consists of three modules:

  • common module for shared code
  • js module targeting JavaScript
  • jvm module targeting the JVM

Common module

This module contains all common shared code, and should be limited to Kotlin.


In the case of a data classes, usually the implementation is the same whether targeting the JVM, JavaScript or other platforms. In our case, we have this as part of the package com.hadihariri.multiplatform.common.data.


Project Layout

expect and actual

There are times when we still need to share common code, but have different behaviours on different platforms, since the implementation touch platform-specific calls and/or libraries.


That’s where the expect and actual keywords come in to play and this is the module where we need to define them. If you recall C/C++, think of them as headers and implementations. We can mark any class or functions with the keyword expect to indicate that the code is present, but the actual implementation will be provided in each specific module. How we link these modules and make the compiler aware of it all, we’ll see in a bit.


In our case, we’re going to define a class called Date and a function called platformMessage, and place these in the com.hadihariri.multiplatform.common package.


Platform Specific Shared Code


The code itself would be

package com.hadihariri.multiplatform.common

import com.hadihariri.multiplatform.common.data.*


expect class Date() {
    fun getDate(): Int
    fun getMonth(): Int
    fun getFullYear(): Int
    fun getHours(): Int
    fun getMinutes(): Int
    fun getTime(): Number
}

expect fun platformMessage(message: Message)

Again, notice how there’s not implementation yet.

actual implementations

As soon as we do this, IntelliJ IDEA will display an error, along with an Intention, indicating that the implementation for these declarations are missing


Implement actual


We need to now implement these in their corresponding modules. That’s where platform specific modules come in.

Platform specific modules

Since we’re targeting Jvm and JavaScript, we have two platform specific modules. In each of these we’ll have Kotlin code that can talk and interop with platform libraries and languages. In addition we’ll have the actual implementations of our expect declarations declared in the common module.


In our case, this would be the implementations for the Data class and the platformMessage function

package com.hadihariri.multiplatform.common

import com.hadihariri.multiplatform.common.data.*

actual fun platformMessage(message: Message) {
    println("(JVM) [${message.priority}] ${message.priority}")

}
package com.hadihariri.multiplatform.common

actual class Date  {
    actual constructor()
    actual fun getDate(): Int {
        ...
    }
    ...
}

One important thing to note here: the code should be declared in the same package as it is defined in the common module. If we see our project structure we can see how we have a common and a jvm package defined in the jvm module


Jvm Structure


Linking modules with Gradle

One piece missing in this picture is how we link the Gradle projects together so that they correctly identify the dependencies between modules. It’s similar to the compile directive in Gradle which allows one project to be referenced by another

dependencies {
    compile project(':common')

except in this case, instead of compile, we use expectedBy

dependencies {
    expectedBy project(':common')

Starting a new MP project in IntelliJ IDEA

Currently multi-platform projects are only supported using Gradle. In IntelliJ IDEA, we can create the necessary modules using the New Module wizard


Wizard


We first start by creating a general Gradle module, and then add a module per target platform (which then defines the correct dependencies). Currently the expectedBy relationship needs to be set manually.


Once again, the complete source code for the sample project is available on GitHub