Computer scienceProgramming languagesKotlinAdvanced featuresMultiplatform

Multiplatform overview

8 minutes read

Kotlin Multiplatform is a feature of the Kotlin language that lets you share common code across many platforms while allowing for platform-specific implementations. This approach aims to cut down repeated work by writing the shared logic once and reusing it on platforms such as Android, iOS, JVM, JavaScript, and native binaries.

Core philosophy

The main idea behind Kotlin Multiplatform is "write once, run everywhere," focusing on sharing business logic, not UI. By putting the common code into a shared module, platform-specific modules can implement interfaces or expect/actual declarations to make the experience right for each platform.

Here's a simple example of how you might set up a multiplatform project:

// Common module (shared code)
expect fun platformName(): String

fun createApplicationScreenMessage(): String {
    return "Kotlin Rocks on ${platformName()}"
}

// Android module
actual fun platformName(): String {
    return "Android"
}

// iOS module
actual fun platformName(): String {
    return "iOS"
}

In the shared module, we define an expect function platformName(). This function will have different implementations depending on the platform, which are defined using actual in the platform modules.

Kotlin Multiplatform does not make you share all code; it's flexible, letting you choose what is shared and what stays platform-specific. It’s a great tool for developers who want to use code again without losing the unique features of each platform.

Supported platforms and target architectures

Kotlin Multiplatform is a flexible technology that lets developers write code to be shared across many platforms. Here's a list of platforms Kotlin Multiplatform can target:

  • Android: Kotlin is top-notch for Android development, giving full access to the Android API.

  • iOS: Kotlin/Native compiles to native binaries for iOS devices.

  • JVM: Targets any device that runs Java Virtual Machine, including server-side and desktop applications.

  • JavaScript: Turns into JavaScript for web development, working with modern browsers.

  • Native Binaries: For systems like Windows, macOS, and Linux, aiming at various CPU architectures.

Regarding architectures, Kotlin Multiplatform supports:

  • ARM: Including ARM32 and ARM64, which covers most modern smartphones and tablets.

  • x86: Commonly used in desktop and laptop computers.

  • x86_64: The 64-bit version of x86, used a lot in current desktops and servers.

  • WebAssembly (experimental): A virtual machine designed for high-performance apps on web pages.

Here's how you might define targets in your build.gradle.kts file:

 kotlin {
    android()
    iosX64("ios") {
        binaries {
            framework()
        }
    }
    jvm()
    js {
        browser {
        }
        nodejs {
        }
    }
    linuxX64("linux")
    mingwX64("windows")
} 

Each function like android(), iosX64(), jvm(), js(), linuxX64(), and mingwX64() corresponds to a target platform. Kotlin Multiplatform then uses the right compiler backend to make the binaries for the chosen target, making sure your code can run well on each platform.

Project structure and common code

In a Kotlin Multiplatform project, the structure is made to make it easy to share code across platforms while allowing specific implementations. It typically includes a commonMain source set and various platform-specific source sets.

CommonMain source set

The commonMain directory has shared code that works for all platforms. It has the common business logic, data models, and interfaces that all platforms will use. The Kotlin Standard Library is available here, ensuring it works on all platforms.

 // commonMain/kotlin/Example.kt
expect class Platform() {
    fun platformName(): String
}

fun greet(): String = "Hello from ${Platform().platformName()}"
 

Platform-specific source sets

Each target platform has its own source set, like jvmMain for JVM, jsMain for JavaScript, and iosMain for iOS. These source sets have specific implementations for their platforms using the actual keyword to meet the expect demands from commonMain.

// jvmMain/kotlin/Platform.kt
actual class Platform actual constructor() {
    actual fun platformName(): String = "JVM"
}  

Organizing code for reuse

To use code again well, put as much as you can in commonMain. Only go to platform-specific source sets for unique APIs or when you need native libraries. Use interfaces and expect/actual declarations to separate platform-specific actions.

By using this setup, developers can write code once in commonMain and share it on many platforms, only changing where needed for specific platform tasks. This way, you have less duplicated code and a more efficient process for making multiplatform apps.

Building and dependency management

In a Kotlin Multiplatform project, you manage the build process with Gradle and the Kotlin Multiplatform plugin. This setup lets you set up source sets, dependencies, and tasks for each platform in your project targets.

Build process:

  1. Configure Source Sets: Set up common and platform-specific source sets. The common source set has shared code, and platform-specific sets have platform-dependent implementations.

  2. Apply Kotlin Multiplatform Plugin: In your build.gradle.kts, add the plugin to start using multiplatform features.

  3. Compile and Build: Gradle compiles the common and specific code separately and puts them together into a single output for each platform (like a JAR for JVM, or a framework for iOS).

Dependency management:

  • Common Dependencies: These dependencies are shared among all platforms. They go in the commonMain source set.

  • Platform-Specific Dependencies: Platform-specific libraries go in their respective source sets.

To include native libraries, you might use the cinterop tool from Kotlin/Native for interaction with C libraries. To add an iOS library, for example, you define a .def file and include it in your Gradle setup:

 kotlin {
    iosX64("ios") {
        binaries {
            framework {
                baseName = "MyFramework"
            }
        }
        compilations.getByName("main") {
            val myInterop by cinterops.creating {
                defFile(project.file("src/iosMain/c_interop/myInterop.def"))
            }
        }
    }
} 

In short, the Kotlin Multiplatform plugin and Gradle work together to streamline the build process and manage dependencies for multiplatform projects. By correctly setting up source sets and dependencies, you make sure your Kotlin codebase stays easy to manage across platforms.

Real-world applications and case studies

Kotlin Multiplatform is increasingly used in apps that share logic across multiple platforms but need platform-specific tasks where necessary.

Real-World Applications:

  • JetBrains Space: JetBrains itself has used Kotlin Multiplatform for Space, their team environment tool that shares business logic across iOS, Android, and web platforms.

  • Cash App by Square: This financial app uses Kotlin Multiplatform to share code between iOS and Android, making less work and more consistency between platforms.

Case Studies:

  1. Case Study: Cash App

    • Benefits: By using Kotlin Multiplatform, Square talked about lots of code reuse and consistent behavior on platforms. This led to quicker feature building and easier upkeep.

    • Challenges: At first, there were worries about the maturity of the multiplatform libraries and tools, but these have improved as the community has grown.

  2. Case Study: Yandex.Disk

    • Benefits: Yandex used Kotlin Multiplatform perfectly for sharing network logic, data storage, and business logic across their iOS and Android apps.

    • Challenges: They ran into some trouble with the lack of multiplatform libraries for certain tasks and the need to write platform-specific code for UI and some hardware features.

Code Snippet Example:

// Common Code
expect fun platformName(): String

fun createApplicationScreenMessage() : String {
    return "Kotlin Rocks on ${platformName()}"
}

// Android Specific Code
actual fun platformName(): String {
    return "Android"
}

// iOS Specific Code
actual fun platformName(): String {
    return "iOS"
} 

In this snippet, expect and actual keywords are used to set platform-specific functions for a common one that gives a message based on the platform.

Conclusion

Kotlin Multiplatform is a strong option for developers who want to share code across platforms without losing each platform's unique abilities and speed. By following the "write once, run everywhere" principle, Kotlin Multiplatform helps in reusing a lot of code, especially in business logic, but gives you the freedom to implement platform-specific features where needed. The technology can work with a range of platforms and architectures, covering most of the device ecosystem today.

The project's setup is designed to reuse as much code as possible with a commonMain source set for shared parts and platform-specific sets for specific tasks. Gradle and the Kotlin Multiplatform plugin make the build process and controlling dependencies smoother, leading to a codebase that's easier to maintain across different platforms.

Practical examples from JetBrains Space to Cash App by Square show the real advantages of Kotlin Multiplatform, like quicker development of features and the same behavior across platforms. However, developers need to be ready to tackle issues such as less mature multiplatform libraries and the need for platform-specific code, especially with UI and hardware stuff.

Overall, Kotlin Multiplatform is a powerful tool for today's app development, balancing shared logic with platform-specific detail. Its growing use and place in the market show it's a solid choice for developers who want to make their cross-platform development work better.

8 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo