Current issues with Kotlin Multiplatform and how to fix them

Recently I started to play around with Kotlin Multiplatform, which is still in an experimental state. Before I could start to do something productive I ran into several issues and wanted to inform about how to fix them, in case you also encounter them.
Setting the Android SDK path
The first issue you will automatically encounter after creating the project is:
Please fix the ‘sdk.dir’ property in the local.properties file
Just head over to Android Studio and open your local.properties
and copy the sdk.dir
value from there to the corresponding file in the Multiplatform project. You can now continue to build your project.
Renaming the Android source set
The project is created with a few source sets commonMain
,commonTest
, iosMain
etc. However, the Android packages are named main
and test
which I found very confusing — why not androidMain
? Sadly, a simple renaming of the package will lead to build errors.
Additionally, I opened the app/build.gradle
and put this into the android {}
block:
sourceSets {
main {
manifest.srcFile 'src/androidMain/AndroidManifest.xml'
java.srcDirs = ['src/androidMain/java']
res.srcDirs = ['src/androidMain/res']
}
}
telling Gradle where it would find the Android code instead.
Writing tests for classes in the common module
It’s nice to be able to write code that is getting shared between Android and iOS. But I need to test this code, too. Like I’m used from IntelliJ I pressed ⌘+⇧+T inside a class to create a test for this class.
I read that I need to use kotlin-test
—it’s already added to the classpath, but there is no option to create a “Kotlin test” and all other testing frameworks suggested to use are wrong. Well, I just chose one and pressed continue. Luckily, you can simply start typing @Test
and IntelliJ will ask you to import the correct library kotlin.test.Test
. But then I noticed it created my test in the test
package, which is the package for Android tests, instead of the commonTest
package.
Lesson learned: Create all your common tests manually in the correct package right now.
Executing tests
After I finally wrote a test of course I wanted to execute it — but this would be too simple if it just worked!
In my case I added the kotlinx-serialization
library and when trying to run a test for the common module my build failed with
Task :app:compileDebugAndroidTestKotlinAndroid FAILED
e: /mpp-test/app/src/commonTest/kotlin/sample/SampleTests.kt: (3, 16): Unresolved reference: serialization
I quickly noticed it failed when compiling Android test resources, but I just wanted to run common code, it has nothing to do with Android 🤷♂
After some searching I found this is currently a bug targeted to be resolved with Kotlin 1.3.40. For the time you explicitly need to add all Multiplatform dependencies meant for androidMain
to androidTest
, too.
After I fixed that I ran into another problem: Right clicking on commonTest
and selecting “Run all tests” gave me:
No tasks available
I was like “well ok, then let’s run the test by right clicking on the test class and select “Run” from here”.
This worked, however, it ended with:
Test events were not received
Looking at the console output it tried to run Exeuting task ‘-tests “sample.sampleTests"'
. It tried to execute… what?
Again, after looking through open Multiplatform bugs, I found the solution:
Go to Preferences | Build, Execution, Deployment | Build Tools | Gradle | Runner
, Run tests using
and selectPlatform Test Runner
instead of Gradle Test Runner
— you can now run your tests.
Gradle wrapper is missing
Tried to run anything from command line with Gradle? Currently no Gradle wrapper is added to new projects. For me I went to another project and copied gradlew
, gradlew.bat
and the directory gradle/wrapper
to the Multiplatform project. Afterwards you can use ./gradlew build
and stuff like that.
Unable to run app with Ktor as a dependency
If you want to do some networking then you will want to use Ktor. I added all dependencies for Multiplatform and tried to run my app. It failed with
FAILURE: Build failed with an exception.* What went wrong:
Execution failed for task ‘:app:transformResourcesWithMergeJavaResForDebug’.
> More than one file was found with OS independent path ‘META-INF/ktor-http.kotlin_module’
Add this to your android {}
block:
packagingOptions {
exclude 'META-INF/*.kotlin_module'
}
Using Ktor with Kotlinx.serialization on Kotlin/Native
So, you can add a JsonFeature
to Ktor, which enables Ktor to automatically parse some JSON retrieved from an API call to some entity:
install(JsonFeature) { serializer = KotlinxSerializer() }
If you now make a HTTP call with Ktor and define the return type of a function to be your entity, Ktor will do the rest for you by itself:
suspend fun doHttpCall(): YourEntity {
return client.get { ... }
}
This worked fine when I tested it with Android. When implementing the iOS part, using Kotlin/Native, it threw an exception:
Obtaining serializer from KClass is not available on native due to the lack of reflection. “ + “Use .serializer() directly on serializable class.
This is currently a TODO in Kotlin/Native. So, just alter your method to something like this:
val response = client.get<HttpResponse> { ... }
val jsonBody = response.readText()
return Json.parse(YourEntity.serializer(), jsonBody)
Be careful with singletons
Kotlin/Native’s concurrency model is designed so it freezes object graphs to guarantee immutability. You can read more about that here.
At the end it states under which circumstances objects are automatically frozen. Definitely I should’ve read that beforehand.
I created a Kotlin object
named ServiceLocator
which should serve me as a very simple dependency injection tool. I put my Ktor HTTP client inside and then got an exception:
kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen io.ktor.client.request.HttpRequestPipeline
which was caused by exactly this issue. However, it took me a long time to figure that out.
You need to add @ThreadLocal
to your object
or move it out of the singleton. Adding @ThreadLocal
however may have unwanted side effects, so be careful with using that and don’t see it as the ultimate solution.
That were some issue that took me some time to find and resolve 😉 Happy Multiplatform coding.