Kotlin Multiplatform (KMP) is an innovative cross-platform development technology created by JetBrains. It enables developers to share code seamlessly across various platforms while still allowing for platform-specific implementations when necessary. This flexibility makes Kotlin Multiplatform a powerful solution for building applications that run on multiple operating systems, including Android, iOS, and web applications. By leveraging KMP, developers can streamline their workflow and enhance productivity in cross-platform app development.
When exploring what is Kotlin Multiplatform, it's essential to understand its key features that make it a standout choice for cross-platform app development:
Shared Code: With Kotlin Multiplatform, developers can write a single codebase that is reusable across multiple platforms, significantly reducing redundancy and development time.
Platform-Specific Code: Kotlin Multiplatform allows for the creation of platform-specific code that is compiled only for the designated platform, ensuring optimal performance and user experience tailored to each environment.
Compilation: Kotlin Multiplatform compiles both the shared code and platform-specific code into platform-specific binaries, facilitating easy deployment across various platforms.
Interoperability: Kotlin Multiplatform's robust interoperability features empower developers to seamlessly access platform-specific APIs and frameworks, significantly enhancing the functionality and integration of applications across various platforms.
Kotlin Multiplatform has revolutionized cross-platform app development by enabling developers to share code across multiple platforms. This guide will help you set up your first Kotlin Multiplatform project for efficient cross-platform app development.
Open Android Studio
Go to File > Settings (on Windows/Linux) or Android Studio > Preferences (on macOS)
Navigate to Plugins
Search for "Kotlin Multiplatform"
Install the plugin and restart Android Studio
Now, your project has been created. If you look at the top menu, you'll notice that you can run both Android and iOS platforms directly from Android Studio, as shown in the image below.
If you run both the Android and iOS versions of the application, the results will be displayed as shown below.
Here is the structure of a Kotlin Multiplatform project, highlighting the distinction between shared and platform-specific code, and how to define supported platforms.
fun greeting() {
println("Hello, Kotlin Multiplatform!")
}
The Kotlin Multiplatform compiler generates platform-specific binaries from this code, enabling efficient cross-platform app development, though it cannot include platform-specific functions.
kotlin {
jvm() // JVM target
iosArm64() // iOS target
}
Here's how to access source sets in Gradle scripts using the Kotlin Multiplatform configuration block:
kotlin {
// Targets declaration:
// Source set declaration for cross-platform app development:
sourceSets {
commonMain {
// Configure the commonMain source set
}
}
}
In a multiplatform project with native and JS targets, the following code in commonMain doesn't compile:
// commonMain/kotlin/common.kt
// Doesn't compile in common code
fun greeting() {
java.io.File("greeting.txt").writeText("Hello, Multiplatform!")
}
As a solution, Kotlin introduces platform-specific source sets, commonly known as platform source sets. Each target is associated with its own platform source set that compiles exclusively for that target. For instance, the Android target has the corresponding androidMain source set, which compiles specifically for Android. In these source sets, Kotlin permits the use of platform-specific dependencies, such as Android libraries in androidMain. Here’s an example:
// androidMain/kotlin/android.kt
// You can use Android-specific dependencies in the `androidMain` source set
fun androidGreeting() {
val file = File(context.filesDir, "greeting.txt")
file.writeText("Hello, Kotlin Multiplatform!")
}
In simple Kotlin Multiplatform projects, you typically have only common and platform-specific code. The commonMain source set contains code shared across all declared targets, while platform-specific source sets, like jvmMain, compile code exclusively for their respective targets.
However, more granular code sharing is often necessary. For example, if you want to target all modern Apple devices and Android devices, you might define targets like this:
kotlin {
androidTarget()
iosArm64() // 64-bit iPhone devices
macosArm64() // Apple Silicon Macs
watchosX64() // Apple Watch devices
tvosArm64() // Apple TV devices
}
If you need to generate a UUID for Apple devices using the NSUUID API, you cannot place this function in commonMain, as it would cause errors when compiling for Android, where NSUUID is unavailable.
Instead of duplicating the code across each Apple-specific source set (e.g., iosArm64Main, macosArm64Main), you can utilize intermediate source sets. These source sets compile to some, but not all, targets, allowing for better code organization and reuse.
Kotlin automatically creates certain intermediate source sets, such as appleMain, which compiles only for Apple targets. This allows you to use Apple-specific APIs within appleMain, enabling you to add the randomUuidString() function there. During compilation, Kotlin combines all relevant source sets, including intermediate ones, for the specified target.
You can define the appleMain source set in the sourceSets block of your build.gradle.kts file. Here’s how to do it:
kotlin {
sourceSets {
val appleMain by creating {
// Specify dependencies and configurations for the appleMain source set
dependsOn(commonMain) // This allows appleMain to access common code
}
val iosArm64Main by getting {
dependsOn(appleMain) // Link iosArm64Main to appleMain
}
val macosArm64Main by getting {
dependsOn(appleMain) // Link macosArm64Main to appleMain
}
val watchosX64Main by getting {
dependsOn(appleMain) // Link watchosX64Main to appleMain
}
val tvosArm64Main by getting {
dependsOn(appleMain) // Link tvosArm64Main to appleMain
}
}
}
After defining the appleMain source set in your Gradle file, create the corresponding directory structure in your project. The typical structure would look like this:
/src
/commonMain
/kotlin
/appleMain
/kotlin
/iosArm64Main
/kotlin
/macosArm64Main
/kotlin
/watchosX64Main
/kotlin
vosArm64Main
/kotlin
You can now add any Apple-specific code to the appleMain source set.
For example, if you want to create a function that generates a UUID for Apple devices, you can do so in a Kotlin file within the appleMain/kotlin directory:
// appleMain/kotlin/AppleUtils.kt
import platform.Foundation.NSUUID
fun randomUuidString(): String {
return NSUUID().UUIDString()
}
When compiling a Kotlin Multiplatform project, Kotlin collects all relevant source sets labeled for a specific target and produces the corresponding binaries. For example, compiling for the JVM involves both commonMain and jvmMain.