What is Kotlin Multiplatform? A Deep Dive into Cross-Platform App Development

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.

Key Features of Kotlin Multiplatform

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.

Setting Up Kotlin Multiplatform for Cross-Platform App Development in Android Studio

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.

1. Prerequisites for Kotlin Multiplatform Development:

  • Download and Install Android Studio (Latest Stable Version)
  • Install the latest version of Java Development Kit (JDK) from the official Oracle website (Android Studio usually includes this)
  • Ensure your system meets the requirements for cross-platform app development

2. Enable Kotlin Multiplatform Plugin:

  • Open Android Studio

  • Go to File > Settings (on Windows/Linux) or Android Studio > Preferences (on macOS)
    Android Studio Settings

  • Navigate to Plugins

  • Search for "Kotlin Multiplatform"

  • Install the plugin and restart Android Studio
    Kotlin Multiplatform Plugin

Create a New Kotlin Multiplatform Project in Android Studio

  • Open Android Studio
  • Click "New Project"
  • Select "Kotlin Multiplatform App"

Kotlin Multiplatform App

  • Choose a project name and location

Kotlin Multiplatform App Details

Kotlin Multiplatform Modules

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.

Kotlin Multiplatform Build

If you run both the Android and iOS versions of the application, the results will be displayed as shown below.

Kotlin Multiplatform Output

The generated Kotlin Multiplatform project consists of three primary modules:

  • Shared Module: In Kotlin Multiplatform, this module contains the shared logic that is common across all platforms, serving as the foundation for efficient cross-platform app development. It enables seamless code sharing and maintains consistency in your Kotlin Multiplatform project while facilitating robust cross-platform app development.
  • Android App Module: This module in your Kotlin Multiplatform project includes code that is specific to the Android platform and utilizes the shared module as a standard Android library. It demonstrates how Kotlin Multiplatform enables platform-specific implementations while maintaining cross-platform app development efficiency.
  • iOS App Module: As part of the Kotlin Multiplatform architecture, this module encompasses the iOS-specific code and employs the shared module as a regular Xcode framework, as specified in our project configuration for cross-platform app development. While there is an option to build the module as a CocoaPod in your Kotlin Multiplatform project, we will continue using the regular framework for the time being.

Overview of Kotlin Multiplatform Project Structure

Here is the structure of a Kotlin Multiplatform project, highlighting the distinction between shared and platform-specific code, and how to define supported platforms.

Kotlin Multiplatform Project Structure

Key Concepts in Kotlin Multiplatform for Cross-Platform App Development

  • Common Code in Kotlin Multiplatform: This is the shared Kotlin code located in the commonMain directory, which forms the foundation of cross-platform app development by compiling for multiple platforms. For example:
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.

  • Targets in Kotlin Multiplatform: Targets represent the platforms (e.g., JVM, JS, Android, iOS) that your cross-platform app development project will support. When using Kotlin Multiplatform, you declare targets in Gradle using:
kotlin {  
    jvm() // JVM target  
    iosArm64() // iOS target  
}  
  • Source Sets for Cross-Platform App Development: In Kotlin Multiplatform, source sets group source files with specific targets, dependencies, and compiler options. The commonMain source set is available in all Kotlin Multiplatform projects, while platform-specific source sets (e.g., jvmMain, iosMain) are created for each target to support comprehensive cross-platform app development.

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  
        }  
    }  
}  
  • Platform-Specific Source Sets: These allow the use of platform-specific APIs. For instance, jvmMain can utilize Java libraries, while iosMain can access iOS-specific APIs.

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!")  
}  
  • Intermediate Source Sets: These source sets compile to some, but not all, targets. They help avoid code duplication when sharing functionality across similar platforms, such as all Apple devices.

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.

Defining the appleMain Source Set in Your Kotlin Multiplatform Project

Create the appleMain Source Set

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  
        }  
    }  
}  

Create the Directory Structure

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  

Add Code to appleMain

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()  
}  

Compilation Process

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.