KMP #01: Membuat Project Kotlin Multiplatform Pertama dengan Android Studio

Di artikel sebelumnya kita sudah berkenalan dengan Kotlin Multiplatform dan melihat bagaimana KMP memungkinkan kita berbagi business logic antara Android dan iOS, sementara UI tetap menggunakan teknologi native masing-masing platform.

Kalau teman-teman belum membaca artikel sebelumnya, saya sarankan mulai dari sana terlebih dahulu karena di artikel ini kita akan langsung masuk ke project.

Kita akan menggunakan Kotlin Multiplatform IDE Plugin langsung dari Android Studio.

Catatan: Tutorial ini menggunakan pendekatan native UI. Jadi nantinya Android akan menggunakan Jetpack Compose, sedangkan iOS akan menggunakan SwiftUI. Kita tidak menggunakan Compose Multiplatform untuk shared UI.

Apa yang akan kita buat?

Di akhir artikel ini, kita akan mempunyai project dengan struktur kurang lebih seperti berikut:


weather-app-kmp/
├── androidApp/
│   └── ...
├── shared/
│   └── src/
│       ├── commonMain/
│       ├── androidMain/
│       └── iosMain/
└── iosApp/
    └── ...

Untuk sekarang project-nya masih sangat sederhana. Belum ada API. Belum ada Ktor. Belum ada database. Belum ada Weather App.

Kita hanya akan memastikan bahwa satu project KMP dapat menjalankan kode Kotlin yang berbeda untuk Android dan iOS. Nantinya project inilah yang akan kita kembangkan sedikit demi sedikit sampai menjadi Weather App. Sebelum mulai, pastikan beberapa tools berikut sudah tersedia.

Android Studio

Kita akan menggunakan Android Studio sebagai IDE utama. Kotlin Multiplatform saat ini didukung secara resmi di Android Studio dan IntelliJ IDEA. Kotlin juga menyediakan Kotlin Multiplatform IDE Plugin untuk membantu development, termasuk project setup dan pengecekan environment.

Kotlin Multiplatform Plugin

Buka Android Studio > Settings > Plugins. Kemudian cari: Kotlin Multiplatform. Install plugin tersebut dan restart Android Studio jika diminta.

Plugin ini tersedia melalui JetBrains Marketplace dan memang ditujukan untuk development Kotlin Multiplatform di Android Studio.

Xcode

Karena target kita adalah Android dan iOS, development iOS membutuhkan macOS dan Xcode.

Jalankan Xcode setidaknya sekali setelah instalasi supaya initial setup dan komponen yang diperlukan selesai.

Kotlin Multiplatform juga melakukan preflight check terhadap environment iOS dan Xcode.

Let's Code!

Sekarang kita mulai membuat project.

1. Buka Android Studio

Jalankan Android Studio. Dari welcome screen, pilih: New Project. Kalau Android Studio sedang membuka project lain, kita juga bisa menggunakan:

File > New > Project

Setelah Kotlin Multiplatform Plugin terinstall, seharusnya kita bisa menemukan template Kotlin Multiplatform di New Project dialog.

Kotlin sendiri sekarang mendokumentasikan pembuatan project KMP langsung dari IDE dengan memilih Kotlin Multiplatform sebagai template.

2. Pilih Kotlin Multiplatform

Pada daftar template, pilih: Kotlin Multiplatform. Kemudian kita akan melihat konfigurasi project. Untuk tutorial ini kita hanya membutuhkan:

  • Android
  • iOS

Kita tidak membutuhkan Desktop atau Web karena fokus tutorial ini adalah aplikasi mobile Android dan iOS.

3. Gunakan Native UI

Ini bagian yang cukup penting. Pada konfigurasi iOS, pastikan kita menggunakan opsi: Do not share UI. Kenapa? Karena pada tutorial ini kita ingin mempelajari pola:

Shared Kotlin Logic
        │
        ├── Android → Jetpack Compose
        │
        └── iOS → SwiftUI

Jadi yang kita share adalah logic, bukan UI. Pendekatan ini memang merupakan salah satu use case utama Kotlin Multiplatform: shared logic dengan UI native di masing-masing platform.

Struktur Project

Setelah project selesai dibuat dan Gradle melakukan sync, kita akan mendapatkan beberapa bagian penting. Secara konseptual strukturnya seperti ini:

flowchart LR ROOT["weather-app-kmp"] ROOT --> SHARED["shared"] ROOT --> ANDROID["androidApp"] ROOT --> IOS["iosApp"] SHARED --> COMMON["commonMain"] SHARED --> ANDROID_MAIN["androidMain"] SHARED --> IOS_MAIN["iosMain"] ANDROID --> ANDROID_UI["Jetpack Compose"] IOS --> IOS_UI["SwiftUI"]

shared adalah tempat kita nantinya meletakkan kode yang dapat digunakan oleh Android dan iOS. Sedangkan androidApp dan iosApp merupakan aplikasi pada masing-masing platform. Struktur ini sejalan dengan konsep KMP di mana Android app menggunakan shared module sebagai dependency, sedangkan iOS app menggunakan shared Kotlin framework.

Mengenal commonMain

Sekarang kita masuk ke bagian paling penting dalam project KMP. Buka:

shared
└── src
    └── commonMain

commonMain  adalah tempat untuk menulis kode Kotlin yang bisa digunakan oleh semua target yang mendukung source set tersebut. Misalnya nanti kita mempunyai:

  • Weather
  • WeatherRepository
  • GetWeatherUseCase
  • WeatherApiClient

Maka kode-kode tersebut nantinya akan kita letakkan di commonMain. Secara sederhana:

flowchart LR COMMON["commonMain"] COMMON --> ANDROID["Android"] COMMON --> IOS["iOS"] ANDROID --> AUI["Jetpack Compose"] IOS --> IUI["SwiftUI"]

Inilah salah satu konsep utama KMP. Kita tidak perlu membuat ulang business logic untuk Android dan iOS. Cukup satu kali di shared module.

Mengenal androidMain

Selanjutnya:

shared
└── src
    └── androidMain
Source set ini khusus untuk Android. Misalnya kita membutuhkan API Android tertentu yang tidak tersedia di iOS, implementasinya dapat ditempatkan di sini.

Contohnya:

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

Nantinya kita akan menggunakan konsep ini bersama dengan expect.

Mengenal iosMain

Kemudian:

shared
└── src
    └── iosMain

Ini merupakan source set untuk kode khusus iOS. Misalnya:

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

Jadi sekarang kita memiliki tiga area utama:

flowchart TB COMMON["`commonMain Shared Kotlin Code`"] COMMON --> ANDROID["`androidMain Android-specific`"] COMMON --> IOS["`iosMain iOS-specific`"] ANDROID --> ANDROID_APP["androidApp"] IOS --> IOS_APP["iosApp"]

Mengenal expect dan actual

Sekarang kita masuk ke konsep penting dalam Kotlin Multiplatform: expect dan actual.

Bayangkan kita mempunyai fungsi: platformName(). Kita ingin fungsi ini dipanggil dari commonMain.

Tetapi hasilnya harus berbeda antara Android dan iOS. Kita bisa menggunakan expect dan actual.

Expect

Buat file: shared/src/commonMain/kotlin/Platform.kt

Kemudian: expect fun platformName(): String

Di sini kita hanya mendefinisikan bahwa fungsi tersebut diharapkan tersedia. Belum ada implementasinya untuk masing - masing platform.

actual untuk Android

Kemudian buat implementasi pada: shared/src/androidMain/kotlin/Platform.kt

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

actual untuk iOS

Kemudian pada shared/src/iosMain/kotlin/Platform.kt kita buat:

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

Sekarang hubungan ketiganya menjadi:

flowchart LR EXP["`commonMain expect fun platformName()`"] AND["`androidMain actual fun platformName() Android`"] IOS["`iosMain actual fun platformName() iOS`"] EXP -.-> AND EXP -.-> IOS

Jadi commonMain mengetahui bahwa fungsi platformName() tersedia. Tetapi implementasinya diserahkan kepada masing-masing platform.

Kenapa expect dan actual dibutuhkan?

Karena tidak semua API tersedia dengan cara yang sama pada setiap platform.

Contohnya:

  • Location
  • Camera
  • File System
  • Bluetooth
  • Notifications
  • Device Information

Kita mungkin ingin mempunyai interface atau API yang sama dari shared code. Tetapi implementasinya bisa berbeda antara Android dan iOS.

Contohnya:

flowchart TB COMMON["commonMain"] COMMON --> EXPECT["expect LocationProvider"] EXPECT --> ANDROID["`Android actual Fused Location Provider`"] EXPECT --> IOS["`iOS actual Core Location`"]

Dengan pendekatan ini, shared code tetap dapat menggunakan abstraction yang sama tanpa harus mengetahui detail API platform.

Namun perlu diingat:

expect/actual bukan berarti semua kode harus dibuat menggunakan mekanisme ini.

Gunakan ketika memang ada bagian yang membutuhkan implementasi platform-specific.

Untuk business logic biasa, kita tetap bisa menulis Kotlin biasa di commonMain.

Menjalankan Project di Android

Sekarang waktunya memastikan project kita benar-benar bisa dijalankan. Pilih Android emulator dari device selector Android Studio. Kemudian jalankan. Jika project berhasil dibuat dengan benar, Android application akan dijalankan pada emulator.

Pada tahap ini UI-nya masih sangat sederhana. Tidak masalah, karena tujuan artikel ini memang bukan membuat UI Weather App. Kita hanya memastikan environment dan project KMP kita sudah berjalan.

Menjalankan Project di iOS

Untuk iOS, kita membutuhkan macOS dan Xcode. Pastikan simulator iPhone tersedia. Dari Android Studio, pilih iOS run configuration atau simulator yang tersedia. Kemudian jalankan.

Android Studio dan Kotlin Multiplatform Plugin menyediakan run configuration untuk target yang tersedia, sehingga kita dapat menjalankan target Android maupun iOS dari development environment yang sama.

Jika semuanya berhasil, aplikasi akan muncul pada iOS Simulator.

Jadi, Apa yang Sebenarnya Kita Share?

Sampai sini mungkin muncul pertanyaan:

"Kalau UI-nya berbeda, sebenarnya apa yang dibagikan?"

Jawabannya adalah logic. Nantinya arsitektur Weather App kita kurang lebih akan seperti ini:

flowchart TB subgraph SHARED["shared"] DOMAIN["`Domain Model + Use Case`"] DATA["`Data Repository`"] NETWORK["`Network Ktor Client`"] DOMAIN --> DATA DATA --> NETWORK end subgraph ANDROID["Android"] AVM["ViewModel"] AUI["Jetpack Compose"] AUI --> AVM end subgraph IOS["iOS"] VM["ObservableObject"] IUI["SwiftUI"] IUI --> VM end AVM --> DOMAIN VM --> DOMAIN NETWORK --> API["Open-Meteo API"]

Android dan iOS akan memiliki UI dan platform layer masing-masing.

Tetapi bagian seperti:

  • Model
  • Repository
  • Use Case
  • Network
  • API handling
  • Business logic

akan kita usahakan berada di shared module.

Project Kita Sekarang

Setelah artikel ini selesai, project kita masih sederhana. Tetapi fondasinya sudah benar. Strukturnya kira-kira:

weather-app-kmp/
├── shared/
│   └── src/
│       ├── commonMain/
│       ├── androidMain/
│       └── iosMain/
│
├── androidApp/
│   └── ...
│
└── iosApp/
    └── ...
Dan konsep yang sudah kita pelajari:
Kotlin Multiplatform
        │
        ├── commonMain
        │      └── Shared logic
        │
        ├── androidMain
        │      └── Android-specific
        │
        └── iosMain
               └── iOS-specific

Kesimpulan

Pada artikel kedua ini kita sudah berhasil membuat project Kotlin Multiplatform menggunakan Android Studio + Kotlin Multiplatform IDE Plugin, tanpa menggunakan KMP web wizard.

Kita juga sudah mengenal struktur dasar:

  • commonMain
  • androidMain
  • iosMain
  • androidApp
  • iosApp

Selain itu kita sudah mencoba konsep expect dan actual untuk menyediakan implementasi platform-specific.

Yang paling penting, kita sudah menentukan pendekatan yang akan digunakan untuk seluruh tutorial: Shared logic, native UI.

Android akan menggunakan Jetpack Compose, Sedangkan iOS akan menggunakan SwiftUI. Sementara logic aplikasi akan kita letakkan di shared. Pada artikel berikutnya kita mulai masuk ke bagian yang lebih menarik.

Kita akan mulai merancang Weather App, mengenal Open-Meteo API, melihat request dan response JSON, kemudian menentukan model data yang akan digunakan di shared module.

Jadi setelah project-nya berhasil dijalankan, jangan langsung dibuang. 😄

Kita akan terus mengembangkan project yang sama sampai menjadi aplikasi Weather App yang benar-benar bisa berjalan di Android dan iOS.

Source code: akan saya sertakan di repository GitHub project ini dan menggunakan branch/tag berdasarkan artikel agar lebih mudah mengikuti progress tutorial.

Sekian, terima kasih.

0 Komentar

Post a Comment

Don't be a Silent Reader!
Please leave your comments, critiques or suggestions.
Because your opinion means a lot for this blog :)

← Previous Post Next Post → Home