Pengenalan KMP

Kotlin Multiplatform untuk logika bersama lintas JVM, JS, dan Native dengan expect/actual.

Daftar isi

Satu basis kode Kotlin dapat dimanfaatkan oleh lebih dari satu jenis platform. Kotlin Multiplatform (KMP) adalah teknologi dari JetBrains yang mengompilasi kode Kotlin ke beberapa target, yaitu JVM, JavaScript, dan kode native, sementara logika yang sama cukup ditulis satu kali. Topik ini membahas struktur source set yang menopang pembagian tersebut, mekanisme expect dan actual untuk bagian yang memang berbeda di tiap platform, serta pertimbangan kapan KMP layak dipakai. Konsepnya diuji dengan sebuah fungsi logika bersama yang dikompilasi dan dijalankan nyata di JVM.

Struktur source set di KMP#

Source set adalah kelompok berkas sumber yang dikompilasi bersama untuk satu tujuan. Pada proyek KMP, source set bernama commonMain menyimpan kode yang dibagikan ke seluruh target, sedangkan source set seperti jvmMain, jsMain, dan nativeMain menyimpan kode yang hanya berlaku untuk targetnya masing-masing. Satu berkas di commonMain tidak dikompilasi satu kali, melainkan dikompilasi ulang oleh compiler setiap target, sehingga hasilnya berupa bytecode JVM untuk target JVM, berkas JavaScript untuk target JS, dan biner untuk target Native.

text
src/
├── commonMain/kotlin/
│   └── ValidasiBerkas.kt      ← kode bersama untuk semua target
├── jvmMain/kotlin/
│   └── NamaPlatformJvm.kt     ← kode khusus JVM, misalnya program desktop
├── androidMain/kotlin/
│   └── NamaPlatformAndroid.kt ← kode khusus Android
└── jsMain/kotlin/
    └── NamaPlatformJs.kt      ← kode khusus JavaScript

Aturan pentingnya terletak pada isi commonMain. Kode di sana hanya boleh menggunakan pustaka standar Kotlin dan library multiplatform, yaitu library yang dirancang untuk berjalan di semua target, sehingga pemanggilan java.io.File atau API Android tidak diperbolehkan di dalamnya. Batasan ini bukan kekurangan sesaat, melainkan jaminan bahwa kode bersama benar-benar dapat dikompilasi di mana saja. Apabila sebagian pekerjaan membutuhkan API platform, pekerjaan itu dipindahkan ke source set platformnya melalui mekanisme yang dibahas di bawah.

Android dan program desktop sama-sama menerima bytecode, tetapi plugin KMP modern memberi Android source set tersendiri bernama androidMain, sehingga penyesuaian ke arah Android tidak menumpuk di jvmMain. Pembagian ini membuat pohon src tumbuh sebanding dengan banyaknya target yang disasar. Proyek yang baru memulai biasanya cukup dengan commonMain dan satu source set platform, lalu menambah sisanya ketika target baru benar-benar disasar.

Logika bersama yang dijalankan di JVM#

Fungsi yang hanya menggunakan pustaka standar secara otomatis menjadi kandidat kode bersama. Contoh berikut memuat aturan validasi nama berkas, yaitu jenis logika yang kebutuhannya identik di aplikasi desktop, server, maupun ponsel. Fungsi tersebut mengembalikan null apabila nama memenuhi aturan, atau pesan error apabila tidak, dan seluruh pemrosesannya hanya menyentuh tipe dasar Kotlin.

kotlin ValidasiBerkas.kt
fun validasiNamaBerkas(nama: String): String? {
    if (nama.isEmpty()) return "nama berkas kosong"
    if (nama.contains('/')) return "nama berkas tidak boleh memuat garis miring"
    if (!nama.contains('.')) return "ekstensi belum dituliskan"
    return null
}

fun main() {
    for (nama in listOf("laporan.txt", "laporan", "", "a/b.txt")) {
        val pesan = validasiNamaBerkas(nama)
        println(if (pesan == null) "OK   $nama" else "GAGAL $nama: $pesan")
    }
}
bash
# compiler Kotlin dan pustaka standar yang dipakai jalur ini
export PATH=/home/kal/my-project/tools/kotlinc/bin:/home/kal/my-project/tools/jdk-merged/bin:$PATH
export STDLIB=/home/kal/my-project/tools/kotlinc/lib/kotlin-stdlib.jar
# kompilasi ValidasiBerkas.kt, lalu jalankan hasilnya di JVM
kotlinc ValidasiBerkas.kt -d out.jar && java -cp "out.jar:$STDLIB" ValidasiBerkasKt
text Keluaran
OK   laporan.txt
GAGAL laporan: ekstensi belum dituliskan
GAGAL : nama berkas kosong
GAGAL a/b.txt: nama berkas tidak boleh memuat garis miring

Keluaran di atas dihasilkan di JVM biasa, tetapi tidak ada satu baris pun di dalam berkas itu yang khusus untuk JVM. Fungsi validasiNamaBerkas dapat dipindahkan ke commonMain tanpa perubahan apa pun, dan compiler target JS maupun Native akan menerimanya dengan baik. Inilah definisi praktis kode bersama pada KMP, yaitu kode yang tidak menyentuh API platform sehingga tidak perlu ditulis dua kali.

Fungsi murni semacam ini juga mudah diuji. Pengujian untuk aturan validasi cukup ditulis sekali di source set common, lalu dijalankan oleh pengujian setiap target di lingkungannya masing-masing. Aturan yang lolos di JVM dengan demikian teruji juga di target lain yang menjalankan pengujian yang sama, sehingga keyakinan terhadap kode bersama tidak berhenti pada berhasilnya kompilasi.

Mekanisme expect dan actual#

Bagian yang memang berbeda di tiap platform ditangani pasangan kata kunci expect dan actual. Kata kunci expect mendeklarasikan fungsi, properti, atau kelas di source set common tanpa memberikan isinya, sedangkan setiap target wajib menyediakan implementasi actual dengan tanda tangan yang sama. Kode bersama kemudian cukup memanggil nama dari deklarasi expect tersebut, dan compiler yang menghubungkannya dengan implementasi platform yang tepat.

kotlin Ilustrasi: expect di source set common dan actual di tiap target
// tidak-dijalankan
// deklarasi di commonMain, tanpa isi
expect fun namaPlatform(): String

// implementasi di jvmMain
actual fun namaPlatform(): String = "JVM"

// implementasi di jsMain
actual fun namaPlatform(): String = "JS"

Contoh di atas menggabungkan tiga bagian dalam satu tampilan agar ringkas, padahal masing-masing berada di berkas dan source set tersendiri. Compiler memeriksa pasangannya secara ketat, yaitu setiap deklarasi expect harus punya actual di semua target, dan tanda tangannya harus cocok sampai ke tipenya. Bila satu target lupa menyediakannya, kompilasi gagal sebelum program sempat dijalankan. Bentuk yang didukungnya juga luas, yaitu fungsi, properti, kelas, interface, hingga objek, sehingga permukaan API platform yang kompleks pun tetap dapat dibungkus dengan pola yang sama.

Kapan KMP layak dipakai#

Pemisahan kode ke source set menambah struktur proyek, sehingga manfaatnya perlu ditimbang sejak awal. KMP paling menguntungkan ketika logika yang sama dibutuhkan oleh beberapa aplikasi, misalnya aturan validasi, penghitungan statistik nilai ujian, atau pengelolaan catatan pengeluaran pribadi. Sebaliknya, proyek yang hanya menyasar satu platform tidak memperoleh apa pun dari pembagian ini.

SituasiPilihan yang lazim
Aturan validasi dan logika bisnis dipakai beberapa aplikasiTulis satu kali di commonMain
Akses API platform seperti kamera atau penyimpananDeklarasikan expect, isi actual per target
Antarmuka penggunaBisa ikut dibagikan melalui Compose Multiplatform, tetapi banyak proyek tetap menulis UI per platform
Proyek dengan satu target sajaCukup proyek Kotlin biasa tanpa source set tambahan

Kebiasaan yang sehat ketika memulai adalah membagikan logika lebih dahulu dan menunda keputusan soal UI. Lapisan logika biasanya paling stabil dan paling sedikit menyentuh API platform, sehingga porsi berbaginya paling besar. Dengan urutan itu, proyek tetap dapat dirilis di satu platform sementara porsi bersamanya bertambah sedikit demi sedikit.

Biayanya juga perlu disebutkan. Setiap target yang ditambahkan membawa toolchain, pengujian, dan kebiasaan platformnya masing-masing, sehingga waktu build serta beban pemeliharaan bertambah. Ekosistem library multiplatform juga belum selengkap pustaka JVM, sehingga sebagian kebutuhan masih ditulis sendiri atau dibungkus melalui expect dan actual. Pertimbangan itu menjadikan KMP layak diambil ketika porsi logika bersamanya besar, dan layak ditunda ketika porsinya kecil.

Ringkasan#

  • Kotlin Multiplatform mengompilasi satu basis kode Kotlin ke beberapa target, yaitu JVM, JavaScript, dan Native, dengan membagi kode ke source set common dan platform.
  • Kode di commonMain hanya boleh menggunakan pustaka standar dan library multiplatform, sehingga logika seperti validasi nama berkas berjalan identik di semua target.
  • Pasangan expect dan actual memisahkan deklarasi bersama dari implementasi per platform, dan compiler menegakkan kecocokan tanda tangannya.
  • KMP paling menguntungkan untuk logika bersama antaraplikasi, sedangkan proyek dengan satu target tidak membutuhkannya.

Referensi#