Banyak proyek modern menargetkan lebih dari satu platform: Android dan iOS, server dan web, desktop dan mobile. Menulis logika bisnis dua kali untuk setiap platform memboroskan waktu dan menimbulkan ketidaksesuaian. Kotlin Multiplatform (KMP) hadir untuk menjawab masalah ini dengan pendekatan berbagi kode selektif. Pada chapter ini kita akan memahami konsep KMP, struktur source set, mekanisme expect/actual, dan use case utamanya.

Apa itu KMP #

Kotlin Multiplatform adalah kemampuan kompilator Kotlin untuk menargetkan beberapa platform sekaligus dari satu proyek Gradle yang sama. Kita menulis logika umum sekali, lalu menyediakan implementasi spesifik per platform hanya untuk bagian yang berbeda — misalnya akses basis data, jaringan, atau UI.

KMP tidak menggantikan UI native. Pendekatannya jelas berbeda dari framework cross-platform seperti Flutter atau React Native: KMP berbagi logika bisnis, sementara UI tetap ditulis dengan toolkit native (Jetpack Compose di Android, SwiftUI di iOS, Compose Multiplatform untuk lintas platform).

Struktur Source Set #

Proyek KMP mengatur kode dalam source set. Dua source set utama:

  • commonMain: kode yang berlaku untuk semua target. Hanya boleh memakai API Kotlin stdlib yang lintas platform dan library KMP.
  • <platform>Main (mis. jvmMain, androidMain, iosMain, jsMain): kode khusus untuk satu target. Boleh memakai API spesifik platform (mis. java.util.* di jvmMain, Foundation di iosMain).

Struktur folder tipikal terlihat seperti berikut:

text Struktur source set KMP
proyek-kmp/
├── shared/
│   └── src/
│       ├── commonMain/
│       │   └── kotlin/com/example/Repository.kt
│       ├── androidMain/
│       │   └── kotlin/com/example/Platform.kt
│       ├── iosMain/
│       │   └── kotlin/com/example/Platform.kt
│       └── jvmMain/
│           └── kotlin/com/example/Platform.kt
└── build.gradle.kts

Mekanisme expect/actual #

Jika commonMain butuh akses ke fitur yang implementasinya berbeda per platform (mis. membaca file, mendapatkan waktu sistem, atau UUID), kita pakai mekanisme expect/actual:

kotlin commonMain: deklarasi expect
expect class PlatformInfo() {
    val nama: String
    fun detail(): String
}
kotlin androidMain: implementasi actual
actual class PlatformInfo {
    actual val nama = "Android"
    actual fun detail() = "Berjalan di Android ${android.os.Build.VERSION.RELEASE}"
}

Kompilator memverifikasi bahwa setiap expect di commonMain punya pasangan actual di setiap target. Jika ada target yang belum diisi, build gagal dengan pesan yang jelas.

Use Cases #

KMP paling sering dipakai untuk berbagi jenis kode berikut:

  • Logika bisnis: aturan domain, validasi, perhitungan
  • Akses data: repository, DAO, mappers
  • Networking: HTTP client (Ktor), serialization, API contract
  • Model data: data class, sealed class, enum yang dipakai lintas platform
  • Logging dan utilitas: helper yang tidak bergantung UI

KMP vs Full Cross-Platform Framework #

Berikut perbandingan pendekatan KMP dengan framework cross-platform penuh:

AspekKotlin MultiplatformFlutter / React Native
Apa yang dibagiLogika bisnisLogika + UI
UI nativeYa, full nativeTidak, render sendiri
Performa UISetara aplikasi nativeSetara, tapi tidak 100% native
Adopsi bertahapBisa, satu modul kapan sajaHarus pilih di awal proyek
Ekosistem libraryLibrary KMP + library nativePlugin framework sendiri

Mendemonstrasikan Konsep Source Set #

Karena KMP memerlukan konfigurasi Gradle khusus, kita tidak bisa menjalankan modul commonMain secara langsung dengan kotlinc. Tetapi kita bisa menyimulasikan konsepnya dalam satu file Kotlin/JVM biasa lewat interface dan implementasi:

kotlin
interface PlatformInfo {
    val nama: String
    fun detail(): String
}

class JvmPlatform : PlatformInfo {
    override val nama = "JVM"
    override fun detail() = "Bytecode berjalan di atas Java Virtual Machine"
}

class NativePlatform : PlatformInfo {
    override val nama = "Native"
    override fun detail() = "Dikompilasi menjadi biner asli via LLVM"
}

class JsPlatform : PlatformInfo {
    override val nama = "JS"
    override fun detail() = "Dikompilasi menjadi JavaScript untuk browser/Node.js"
}

fun main() {
    val target = listOf(JvmPlatform(), NativePlatform(), JsPlatform())
    println("Konsep source set platform pada KMP:")
    target.forEach { p ->
        println("  - ${p.nama}: ${p.detail()}")
    }
}

Output yang Dihasilkan:

text
Konsep source set platform pada KMP:
  - JVM: Bytecode berjalan di atas Java Virtual Machine
  - Native: Dikompilasi menjadi biner asli via LLVM
  - JS: Dikompilasi menjadi JavaScript untuk browser/Node.js

Mari kita bedah bagian pentingnya:

  1. interface PlatformInfo: kontrak umum yang setara dengan deklarasi expect di commonMain.
  2. class JvmPlatform : PlatformInfo: implementasi konkret yang setara dengan actual di jvmMain. Class NativePlatform dan JsPlatform berperan sebagai actual di target lainnya.
  3. val target = listOf(JvmPlatform(), ...): semua implementasi disatukan dalam satu list bertipe PlatformInfo, persis seperti saat kode commonMain memakai tipe expect dan runtime menyelesaikan ke actual yang sesuai.
  4. forEach { p -> ... }: pemanggilan method interface bersifat polimorfik — runtime memilih implementasi yang benar untuk setiap instance.

Referensi #

Referensi Lanjutan #

Selanjutnya #

Kita sudah memahami struktur internal KMP dan target kompilasi. Pertanyaan terakhir di section ini: di mana Kotlin benar-benar dipakai di dunia nyata, dan oleh siapa? Chapter Use Case Kotlin akan menjawab dengan memetakan tujuh bidang utama pemakaian Kotlin, lengkap dengan contoh perusahaan yang sudah mengadopsinya.