In modern software development, code reuse and platform consistency are key to efficiency and reducing maintenance costs. Kotlin Multiplatform (KMP) is a powerful solution that enables developers to share code across multiple platforms, including Android, iOS, web, and more. This post will guide you through understanding and implementing Kotlin Multiplatform, with detailed explanations and practical examples.
What is Kotlin Multiplatform (KMP)?
Kotlin Multiplatform (KMP) is a Kotlin feature that allows developers to write code that can be compiled to multiple platforms. Unlike other cross-platform solutions that often rely on a shared UI layer, KMP focuses on sharing business logic, data models, and other non-UI code. This results in native performance and platform-specific UIs, ensuring a high-quality user experience.
Why Use Kotlin Multiplatform?
- Code Reuse: Write code once and use it across multiple platforms, reducing development time and effort.
- Platform-Specific UIs: Create native user interfaces for each platform, ensuring optimal performance and user experience.
- Reduced Maintenance: Simplify maintenance by updating shared code in one place.
- Cost-Effective: Lower development costs by avoiding redundant code.
- Consistency: Ensure consistent behavior across different platforms by sharing core business logic.
Setting Up a Kotlin Multiplatform Project
Let’s walk through setting up a Kotlin Multiplatform project using IntelliJ IDEA:
Step 1: Install the Kotlin Multiplatform Plugin
First, ensure that you have the Kotlin Multiplatform plugin installed in IntelliJ IDEA.
- Go to File > Settings > Plugins.
- Search for “Kotlin Multiplatform” and install the plugin.
Step 2: Create a New Project
- Open IntelliJ IDEA and select Create New Project.
- Choose “Kotlin” in the left pane, and then select “Multiplatform App” in the right pane.
- Name your project (e.g., “MyKMPApp”) and choose a location.
Step 3: Configure Project Targets
IntelliJ IDEA will guide you through selecting the platforms you want to target. By default, it usually includes:
- Android
- iOS
- JVM (for desktop applications or server-side logic)
You can customize these based on your needs.
Step 4: Project Structure
The project structure includes several modules:
commonMain
: Contains the shared Kotlin code.androidMain
: Contains Android-specific code.iosMain
: Contains iOS-specific code.jvmMain
: Contains JVM-specific code.
Writing Shared Code in commonMain
The commonMain
module is where you write the code that will be shared across all platforms.
Example: Simple Greeting Function
Let’s create a simple function that generates a greeting:
// In commonMain/kotlin/Greeting.kt
package com.example.mykmpapp
class Greeting {
fun greet(): String {
return "Hello, Kotlin Multiplatform!"
}
}
Using Shared Code in Platform-Specific Modules
Now, let’s use the Greeting
class in our Android and iOS modules.
Android Implementation
In your Android module (androidMain
), create an activity and use the Greeting
class.
// In androidMain/kotlin/MainActivity.kt
package com.example.mykmpapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val greetingTextView: TextView = findViewById(R.id.greetingTextView)
val greeting = Greeting().greet()
greetingTextView.text = greeting
}
}
Update your activity_main.xml
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/greetingTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"/>
</LinearLayout>
iOS Implementation
For iOS, you’ll need to create a Swift view controller and use the Kotlin code. This involves using the generated Objective-C headers from the Kotlin code.
// In iOSApp/ViewController.swift
import UIKit
import shared
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let greeting = Greeting().greet()
let label = UILabel()
label.text = greeting
label.frame = CGRect(x: 0, y: 0, width: 300, height: 50)
label.center = view.center
label.textAlignment = .center
view.addSubview(label)
}
}
Handling Platform-Specific Implementations
Sometimes, you’ll need platform-specific implementations for certain functionalities. Kotlin provides the expect
and actual
keywords to handle this.
Example: Platform Name
First, define an expect
declaration in the commonMain
module:
// In commonMain/kotlin/Platform.kt
package com.example.mykmpapp
expect class Platform {
val name: String
}
Then, provide actual
implementations in the platform-specific modules:
// In androidMain/kotlin/Platform.kt
package com.example.mykmpapp
import android.os.Build
actual class Platform {
actual val name: String = "Android ${Build.VERSION.SDK_INT}"
}
// In iosMain/kotlin/Platform.kt
package com.example.mykmpapp
import platform.UIKit.UIDevice
actual class Platform {
actual val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}
Use this in your shared Greeting
class:
// In commonMain/kotlin/Greeting.kt
package com.example.mykmpapp
class Greeting {
fun greet(): String {
return "Hello, Kotlin Multiplatform on ${Platform().name}!"
}
}
Sharing Business Logic and Data Models
KMP excels at sharing business logic and data models. For instance, you can create data classes that represent your application’s data and share them across platforms.
Example: Data Class
// In commonMain/kotlin/Data.kt
package com.example.mykmpapp
data class DataModel(val id: Int, val name: String)
This data class can be used in both Android and iOS modules.
Using Libraries in KMP
Many Kotlin libraries are also multiplatform, making it easy to use them in your shared code. Some popular multiplatform libraries include:
- Kotlinx.serialization: For serializing and deserializing data.
- Kotlinx.coroutines: For asynchronous programming.
- Ktor: For building HTTP clients and servers.
Example: Using Kotlinx.serialization
Add the serialization dependency to your commonMain
build.gradle.kts
file:
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.6.0")
}
}
}
}
Mark your data class as serializable:
// In commonMain/kotlin/Data.kt
package com.example.mykmpapp
import kotlinx.serialization.Serializable
@Serializable
data class DataModel(val id: Int, val name: String)
Advanced Topics
- Dependency Injection: Use Koin or other DI frameworks to manage dependencies in your shared code.
- Testing: Write unit tests for your shared code using Kotlin test frameworks.
- UI Layer: While KMP doesn’t directly share UI, consider using frameworks like Compose Multiplatform to share UI logic.
Conclusion
Kotlin Multiplatform is a robust solution for sharing code across multiple platforms. By focusing on business logic and data models, you can create native applications with consistent behavior and reduced maintenance costs. With the practical examples and guidelines provided in this post, you are well-equipped to start exploring and implementing Kotlin Multiplatform in your projects. Embrace KMP to unlock the potential of code reuse and platform consistency in your development workflow.