Understanding The Basics of Kotlin Multiplatform Projects with Kotlin 1.3

Simone Civetta
Nov 30, 2018 · 6 min read

With the recently announced Kotlin 1.3 comes a massive overhaul of Kotlin Multiplatform and a new DSL for creating projects capable of sharing code between different platforms, such as Kotlin for JVM, Kotlin for JavaScript and Kotlin/Native.

In this article we’ll go through the key aspects of the new API and explain how you can use it to create projects capable of sharing code between Android and iOS.

These contents are adapted from the talk on Cross-Platform Modules with Kotlin/Native presented at Xebia’s Mois du Kotlin in October.

Image for post
Image for post

The new Kotlin Multiplatform API relies on a brand new Kotlin plugin for Gradle, which allows to express shared code and dependencies effectively.

In order to use the new Kotlin plugin, you simply have to add it as a dependency in your buildscript:

buildscript {
ext.kotlin_version = '1.3.0'
repositories {
jcenter()
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
}

dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

And now you‘re able to add the kotlin extension to your build.gradle file:

kotlin {
// ... your definitions here
}

The 4 Main Concepts

  1. SourceSet
  2. Target
  3. Preset
  4. Compilation

Let’s go through each one of them.

1. SourceSet

For instance, given a simple project, you’d have the following structure.

├── build.gradle
└── src
├── commonMain // ← SourceSet
└── ... // ← Other SourceSets

In the example above, commonMain is the SourceSet which contains all the logic which is shared across your project. For instance, here you would insert the code that would run on both your iOS and Android modules.

On the other hand, iosMain is the SourceSet which contains the implementation which will be specific to your iOS framework.

Please note that one does not simply create a folder in your project’s srcand assume you’re creating a SourceSet. In fact, SourceSets must follow a specific naming imposed by Kotlin Multiplatform: we’ll detail how you can define SourceSets yourself in the second part of the article. But first, we’ll start getting familiar with the other three concepts.

2. Target

In the build.gradle file, you’ll define the targets of your app by adding elements to the targets block. Those elements are called presets, and are the subject of the following section.

kotlin {
targets {
// Your targets will go here
}
}

3. Preset

kotlin {
targets {
fromPreset(presets.iosArm64, 'ios')
}
}

4. Compilation

Depending on your goal, your target can use different compilations. For instance, in case you’re running your project tests, you’ll make use of the test compilation. Otherwise, in case you simply want to compile your sources, you’ll run the main compilation.

Compilations also allow you to provide a fine-grained configuration depending on the context. In other words, you can apply compilation-specific parameters to your target when building.

kotlin {
targets {
fromPreset(presets.iosX64, 'iosSim') {
compilations.main.foo = "bar1"
compilations.test.foo = "bar2"
}
}
}

SourceSet = Target + Compilation

A SourceSet is actually a folder that must match a combination of target name + compilation. In other words, assuming you have defined an ios target name and two compilations, main and test, you’ll be able to use two specific SourceSets in your src folder: iosMain and iosTest. As you might guess, files stored in those folders will be specific to those platforms and will only be available when building an iOS module or an iOS test bundle respectively.

commonMain, along with commonTest, are supported by default and are meant to contain files which should be compiled for all your supported platforms.

The case for the src/main folder

Well, it’s not going anywhere for now and you’ll still need it as it will still contain your Java files (which are not supported by platforms other than the JVM) and the AndroidManifest.xml file for Android. In other words, don’t remove your manifest from the traditional src/main folder or your project will not compile.

Target Presets for Android and iOS libraries

Configuring the Preset on Android

targets {
fromPreset(presets.android, 'androidLibrary')
}

Configuring the Preset on iOS

For instance, you can define a variable that will take the value of the platform we are building against. This can be done with Xcode, by adding a custom setting in the Build Settings pane of your iOS project and then calling Gradle from a custom build script.

You can proceed as follows.

1. From the Build Settings pane in Xcode, let’s first add a new Setting called “KOTLIN_DEVICE”, the value of which will depend on the architecture and be as follows:
- “iosSim” for “Any iOS Simulator SDK”
- “ios” for “Any iOS SDK”

2. It’s now time to add a new “Run Script” phase from the Build Phases pane in Xcode, which will call Gradle and thus run the compilation of the sources. In this phase you should also make sure to pass the KOTLIN_DEVICE setting, in order to configure the preset accordingly. The full command is as follows.

cd "<MY/KOTLIN/PROJECT>";
./gradlew linkDebugFrameworkIos -Pkotlin.device="$KOTLIN_DEVICE"

3. Finally, in the gradle script, you can add a condition which will define which preset will be applied: in case you are running an iOS Simulator, the preset value will be presets.iosX64, presets.iosArm64 otherwise.

Please note the configuration of the compilation in the preset: in case you want to create an iOS framework you should setup the output by calling the outputKind method on the main compilation (compilations.main).

targets {
// Read the "kotlin.device" variable passed by Xcode
def isSim = findProperty("kotlin.device") == "iosSim"

// Apply the preset according to the device
def iosPreset = isSim ? presets.iosX64 : presets.iosArm64
fromPreset(iosPreset, 'ios') {
// Configure the main compilation for building an iOS framework
compilations.main.outputKinds('FRAMEWORK')
}
}
Image for post
Image for post

Conclusion

In a future article we’ll be going a step further and show how can you define dependencies between targets.

Find all technical articles by Xebia on blog.xebia.fr

Publicis Sapient Engineering

Publicis Sapient Engineering (anciennement Xebia) est la…

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store