What is the best way to structure your Kotlin multiplatform project?

Kotlin Multiplatform

Project Structure Strategies for Kotlin Multiplatform Android/iOS projects

Kotlin Multiplatform (KMP) is a way of writing cross-platform code in Kotlin. It is not about compiling all code for all platforms, and it doesn't limit you to a subset of common APIs. KMP provides mechanisms for writing platform-specific implementations for a common API.

Click here to find out more about Kotlin.

How to use Kotlin multiplatform to share code between Android/iOS

One of the common use-cases, and indeed my main use-case as a mobile app developer, for KMP is to share code between an Android and iOS project.

You have a few different options with KMP when it comes to deciding how to structure your projects.

Using an all in one approach to Kotlin multiplatform

You can combine your Android and iOS project into one project. They would live within the same directory and both be able to access and build the KMP shared code.

One of Kotlin's example projects has this structure:

https://github.com/Kotlin/kotlin-examples/tree/master/tutorials/mpp-iOS-Android

They also have a great codelab which walks you through setting up a project like this:

https://kotlinlang.org/docs/tutorials/native/mpp-ios-android.html

The main benefit of combining your Android and iOS project into one KMP project is how easy the shared code is to access. No matter which platform you are working on, you can access, update and build the shared code. This makes development fast, particularly for solo development teams.

However, this setup can be overwhelming for new team members joining a project as there is a lot of code to digest and understand.

For teams with separate Android and iOS developers, working this way can be difficult. Changes can get made in the shared code, which requires changes on both platforms. Meaning you have to work more in tandem, which can be difficult for resource scheduling.

How to use Kotlin multiplatform keeping projects separate

You could completely separate out your shared and platform projects. This involves writing build (Gradle) logic to distribute your shared code between projects.

Android Lib

KMP provides a gradle task for building a JVM JAR file for the Android code.

./gradlew [targetName]Jar

iOS Framework

To create the framework needed for iOS you can write a custom gradle task, for example:

task releaseFatFramework(type: FatFrameworkTask) { 
 group = LifecycleBasePlugin.BUILD_GROUP
 baseName = frameworkName 
 destinationDir = file("$buildDir/fat-framework/release") 
 from( 
 kotlin.targets.iosX64.binaries.getFramework("RELEASE"), 
 kotlin.targets.iosArm64.binaries.getFramework("RELEASE")
 ) 
}

This can vary based on your exact target configuration.

This method of using Kotlin multiplatform code is great for bigger teams. It has good separation of concerns and you can distribute the shared code in a more structured manner. You can also decide exactly when to release to the two platforms.For bigger teams, who have separate Kotlin engineers, this might be a helpful approach.The drawer back of this approach is that it could result in lots of CI configuration and boilerplate. Structuring your Kotlin multiplatform project with separate platform projects will probably take longer to setup.Having to have multiple projects open will cause frequent context switching during development. It will also take more cognitive load to understand the entire picture.

Click here to read how Kotlin multiplatform can affect your development culture.

How to share code within the iOS or Android platform using Kotlin Multiplatform

The middle ground approach to using Kotlin multiplatform to structure your mobile app development project would mean having the shared code within one of the platform projects.

This naturally (and arguably obviously) would fall within the Android project. This isn't necessary, but given it shares a language with Android, this would be my recommendation. It will reduce cognitive load and feel familiar to Android developers.

This will, however, shift the responsibility for the shared code onto your Android team. It is worth considering this point and making sure it's right for your team.

Within the Android project, you setup the shared code as a KMP module. IntelliJ has some helpful wizards for this.

This approach makes depending on the shared code, within Android, easy:

dependencies {
 // ...
 implementation project(':shared')
 // ...
}

You can then add a framework generation task for iOS, like the one detailed above.

I then integrated this framework generation into our CI strategy. Every evening, if the shared code has changed, we submit a pull request into the iOS project. This pull request updates the shared framework. This means your iOS team will be aware of the update and be able to merge the change quickly. It also fits into the iOS CI process, giving you warning of any test failures and therefore changes that are needed.

We use CircleCI (version 2.1), and this is the job configuration:

Job:

Commands:

Environment Variables:

FRAMEWORK_GIT_URL: The url to the git project you would like to commit the updated framework into. e.g. [email protected]:org/Project_iOS.git

FRAMEWORK_PATH: The path to the destination framework directory. No leading or trailing separator. e.g. Project

GITHUB_TOKEN: OAuth token for accessing github through hub command line

This approach reduces cognitive load and minimises the setup and boilerplate. It keeps the Kotlin code in one project, so not introducing more overhead to your iOS team. But, shifting the shared code responsibility to your Android team might not be the right approach for you. It means scheduling your resources differently.

How Brightec use Kotlin multiplatform to structure mobile app development projects

We currently use KMP to share the M (Model from MVVM) between Android and iOS. Meaning we share business logic, API integration and local data storage. These concepts don't often require platform-specific variations and share well between platforms.

We use Kotlin on our Android projects, and we generally have separate teams for each platform. So using the middle-ground approach fits our teams well. We try to schedule our Android work slightly ahead of iOS, to give time for the shared code to be started. This eases the iOS development and reduces the number of blockages.

Read our guide to creating tests for your mobile app development projects with Kotlin multiplatform.


Looking for something else?

Search over 400 blog posts from our team

Want to hear more?

Subscribe to our monthly digest of blogs to stay in the loop and come with us on our journey to make things better!