KMP #03: Setup Networking dengan Ktor Client di Kotlin Multiplatform
Di artikel sebelumnya kita sudah mulai merancang Weather App yang akan kita bangun menggunakan Kotlin Multiplatform (KMP). Kita juga sudah mengenal Open-Meteo API,
yang nantinya akan menjadi sumber data cuaca untuk aplikasi kita.
Nah, sekarang kita mulai masuk ke bagian yang cukup penting: bagaimana cara aplikasi mengambil data dari API dan menggunakan data tersebut di Android maupun iOS?
Kalau kita membuat aplikasi Android biasa, mungkin kita akan menggunakan Retrofit atau OkHttp. Sementara untuk iOS, kita bisa menggunakan URLSession.
Tetapi, salah satu tujuan kita menggunakan Kotlin Multiplatform adalah supaya business logic tidak perlu ditulis dua kali. Jadi, bagaimana kalau networking-nya juga kita buat sekali saja?
Di sinilah kita akan menggunakan Ktor Client.
Pada artikel ini kita akan menyiapkan Ktor Client di shared module, mengonfigurasikan engine untuk Android dan iOS, kemudian membuat WeatherApiClient yang bertugas mengambil data cuaca dari Open-Meteo API.
Seperti biasa, kita akan membangun project yang sama secara bertahap. Jadi, pastikan project dari artikel sebelumnya masih tersedia.
Apa yang akan kita buat?
Sebelum mulai coding, kita lihat dulu hasil yang ingin kita capai.
Pada akhir artikel ini, shared module kita akan memiliki HTTP client yang dapat digunakan untuk mengambil data forecast dari Open-Meteo API.
Gambaran arsitekturnya kurang lebih seperti berikut.
Ada beberapa hal yang perlu diperhatikan dari diagram tersebut.
Pertama, WeatherApiClient, konfigurasi HTTP client, dan model response akan kita letakkan di shared module. Artinya, kode tersebut dapat digunakan oleh Android maupun iOS.
Kedua, kita tetap membutuhkan engine yang sesuai dengan platform masing-masing. Android akan menggunakan OkHttp, sedangkan iOS menggunakan Darwin.
Ketiga, kita tidak akan membuat UI pada artikel ini. Fokus kita adalah menyiapkan fondasi networking yang nantinya bisa dipanggil oleh repository dan domain layer.
Dengan begitu, ketika kita mulai membuat UI Android menggunakan Jetpack Compose dan UI iOS menggunakan SwiftUI, keduanya bisa memanfaatkan logic networking yang sama.
Kenalan dengan Ktor Client
Ktor adalah framework Kotlin yang menyediakan berbagai kemampuan untuk membangun aplikasi berbasis jaringan, termasuk HTTP client yang mendukung Kotlin Multiplatform.
Dengan Ktor Client, kita dapat mengirim HTTP request dan menerima response dari server menggunakan API Kotlin yang konsisten.
Untuk project kita, ada tiga komponen utama yang perlu dipahami.
1. Ktor Client Core
ktor-client-core merupakan dependency utama untuk membuat HTTP client.
Kita akan menggunakan komponen ini untuk mengirim request ke Open-Meteo API, menentukan URL, serta menerima response dari server.
2. HTTP Engine
HTTP engine adalah implementasi yang menangani komunikasi HTTP pada platform tertentu.
Kita akan menggunakan:
OkHttp untuk Android.
Darwin untuk iOS.
Keduanya mempunyai implementasi berbeda, tetapi kita tetap dapat menggunakan API Ktor Client yang sama dari shared module.
3. ContentNegotiation dan kotlinx.serialization
API biasanya mengembalikan data dalam format JSON. Sementara itu, aplikasi kita akan lebih mudah mengolah data jika response tersebut sudah dikonversi menjadi Kotlin object.
Ktor menyediakan plugin ContentNegotiation untuk menangani proses konversi tersebut. Kita akan mengintegrasikannya dengan kotlinx.serialization.
Sebagai contoh, response JSON seperti berikut:
{
"latitude": -6.2,
"longitude": 106.8,
"current": {
"temperature_2m": 30.5,
"relative_humidity_2m": 72,
"weather_code": 2
}
}nantinya dapat diubah menjadi object Kotlin sehingga kita tidak perlu memproses JSON secara manual.
Dokumentasi resmi Ktor menjelaskan konfigurasi ContentNegotiation dan penggunaan kotlinx.serialization untuk melakukan deserialization response. Dokumentasi Ktor.
Let's Code!
Sekarang kita mulai menambahkan networking ke project Weather App.
1. Menambahkan dependency Ktor
Pertama, kita perlu menambahkan dependency yang dibutuhkan ke shared module.
Kalau project dari artikel sebelumnya menggunakan Gradle Version Catalog, buka file:
gradle/libs.versions.toml
Tambahkan konfigurasi berikut. Jika beberapa dependency sudah tersedia, kita cukup melengkapi konfigurasi yang belum ada.
[versions]
ktor = "3.3.0"
[libraries]
ktor-client-core = {
module = "io.ktor:ktor-client-core",
version.ref = "ktor"
}
ktor-client-content-negotiation = {
module = "io.ktor:ktor-client-content-negotiation",
version.ref = "ktor"
}
ktor-client-serialization-json = {
module = "io.ktor:ktor-serialization-kotlinx-json",
version.ref = "ktor"
}
ktor-client-okhttp = {
module = "io.ktor:ktor-client-okhttp",
version.ref = "ktor"
}
ktor-client-darwin = {
module = "io.ktor:ktor-client-darwin",
version.ref = "ktor"
}
[plugins]
kotlin-serialization = {
id = "org.jetbrains.kotlin.plugin.serialization",
version.ref = "kotlin"
}Catatan: versi 3.3.0 di atas merupakan contoh versi Ktor yang konsisten untuk konfigurasi tutorial ini, bukan klaim bahwa versi tersebut adalah versi terbaru. Gunakan versi yang kompatibel dengan Kotlin dan Gradle pada project teman-teman. Plugin serialization juga harus menggunakan versi Kotlin yang sesuai dengan plugin Kotlin yang sudah digunakan project.
Selanjutnya, buka file shared/build.gradle.kts.
Aktifkan plugin serialization pada blok plugins:
plugins {
alias(libs.plugins.kotlin.serialization)
}Kemudian tambahkan dependency pada source set yang sesuai:
kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.ktor.client.core)
implementation(
libs.ktor.client.content.negotiation
)
implementation(
libs.ktor.client.serialization.json
)
}
androidMain.dependencies {
implementation(libs.ktor.client.okhttp)
}
iosMain.dependencies {
implementation(libs.ktor.client.darwin)
}
}
}Perhatikan bahwa setiap dependency ditempatkan pada source set yang berbeda.
commonMain berisi dependency yang digunakan oleh kode bersama, sedangkan androidMain dan iosMain berisi engine khusus platform masing-masing.
Setelah selesai, lakukan Gradle Sync.
Jika project teman-teman menggunakan nama accessor Version Catalog yang berbeda, sesuaikan nama libs dengan konfigurasi yang sudah ada.
2. Membuat HTTP engine untuk setiap platform
Sekarang kita akan membuat abstraksi agar shared module dapat menggunakan engine HTTP tanpa harus mengetahui implementasi platform secara langsung.
Kita menggunakan mekanisme expect dan actual yang sudah diperkenalkan pada artikel sebelumnya.
Buat file berikut:
shared/src/commonMain/kotlin/com/example/weather/network/PlatformHttpEngine.kt
Sesuaikan package com.example.weather dengan package project teman-teman.
Isi file tersebut dengan kode berikut:
package com.example.weather.network
import io.ktor.client.engine.HttpClientEngine
expect fun createPlatformHttpEngine(): HttpClientEngineFungsi tersebut hanya mendefinisikan kontrak. Implementasinya akan disediakan oleh masing-masing platform.
Implementasi untuk Android
Buat file:
shared/src/androidMain/kotlin/com/example/weather/network/PlatformHttpEngine.kt
package com.example.weather.network
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.engine.okhttp.OkHttp
actual fun createPlatformHttpEngine(): HttpClientEngine {
return OkHttp.create()
}Di sini kita menggunakan OkHttp sebagai HTTP engine untuk Android.
Implementasi untuk iOS
Selanjutnya, buat file:
shared/src/iosMain/kotlin/com/example/weather/network/PlatformHttpEngine.kt
package com.example.weather.network
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.engine.darwin.Darwin
actual fun createPlatformHttpEngine(): HttpClientEngine {
return Darwin.create()
}Pada iOS, kita menggunakan Darwin engine yang memanfaatkan networking stack Apple.
Dengan konfigurasi ini, shared module mempunyai satu kontrak yang sama, tetapi implementasi engine-nya berbeda berdasarkan target platform.
Diagramnya dapat digambarkan seperti berikut.
Sampai sini kita sudah mempunyai fondasi untuk menggunakan HTTP engine yang sesuai dengan platform. Berikutnya kita akan membuat HTTP client yang menangani konfigurasi JSON.
3. Membuat konfigurasi HttpClient
Buat file baru:
shared/src/commonMain/kotlin/com/example/weather/network/HttpClientFactory.kt
Isi dengan kode berikut:
package com.example.weather.network
import io.ktor.client.HttpClient
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
fun createHttpClient(): HttpClient {
return HttpClient(
engine = createPlatformHttpEngine()
) {
install(ContentNegotiation) {
json(
Json {
ignoreUnknownKeys = true
}
)
}
}
}Mari kita bahas bagian penting dari kode tersebut.
HttpClient
Objek ini akan digunakan untuk mengirim HTTP request ke server.
createPlatformHttpEngine()
Fungsi ini akan menggunakan implementasi engine sesuai target yang sedang dibangun.
install(ContentNegotiation)
Plugin ini memungkinkan Ktor mengonversi response JSON menjadi object Kotlin berdasarkan serializer yang tersedia.
ignoreUnknownKeys = true
Konfigurasi ini memungkinkan serializer mengabaikan field JSON yang tidak didefinisikan dalam model Kotlin.
Hal ini cukup berguna ketika API mengembalikan field tambahan yang belum kita butuhkan. Namun, field yang wajib didefinisikan dalam model tetap harus tersedia, kecuali kita memberikan default value atau mengaturnya sebagai nullable.
Satu hal lagi: HttpClient sebaiknya digunakan kembali selama masa hidup komponen yang membutuhkannya, bukan dibuat ulang untuk setiap request. Kita akan membuat WeatherApiClient yang menerima instance client melalui constructor.
4. Mengenal Open-Meteo Forecast API
Sebelum membuat API client, kita perlu memahami endpoint yang akan digunakan.
Open-Meteo menyediakan forecast API yang dapat diakses tanpa API key untuk penggunaan yang sesuai dengan ketentuan layanannya.
Endpoint yang akan kita gunakan adalah:
https://api.open-meteo.com/v1/forecast
Untuk mengambil cuaca saat ini, kita dapat menggunakan parameter seperti berikut:
| Parameter | Fungsi |
|---|---|
latitude | Koordinat lintang lokasi |
longitude | Koordinat bujur lokasi |
current | Daftar variabel cuaca saat ini |
daily | Daftar variabel prakiraan harian |
forecast_days | Jumlah hari prakiraan |
timezone | Zona waktu untuk data waktu |
Contoh request untuk Jakarta:
https://api.open-meteo.com/v1/forecast?latitude=-6.2&longitude=106.8¤t=temperature_2m,relative_humidity_2m,apparent_temperature,is_day,precipitation,weather_code,wind_speed_10m&daily=weather_code,temperature_2m_max,temperature_2m_min&forecast_days=7&timezone=autoKita meminta data suhu, kelembapan, apparent temperature, kondisi siang atau malam, curah hujan, weather code, kecepatan angin, dan prakiraan suhu harian selama tujuh hari.
Dokumentasi lengkap parameter tersedia di Open-Meteo Forecast API.
Kenapa kita menggunakan latitude dan longitude?
Karena forecast API membutuhkan koordinat lokasi, bukan nama kota.
Pada artikel sebelumnya kita sudah membahas bahwa pencarian nama kota dilakukan melalui Geocoding API. Untuk sekarang kita akan menggunakan koordinat Jakarta sebagai contoh agar kita bisa fokus pada implementasi networking.
Nantinya, setelah fitur pencarian lokasi dibuat, koordinat tersebut akan berasal dari lokasi yang dipilih pengguna.
5. Membuat model response JSON
Sekarang kita akan membuat model Kotlin untuk menampung response dari Open-Meteo.
Buat file:
shared/src/commonMain/kotlin/com/example/weather/network/model/ForecastResponse.kt
package com.example.weather.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class ForecastResponse(
val latitude: Double,
val longitude: Double,
@SerialName("current")
val current: CurrentWeatherDto,
@SerialName("daily")
val daily: DailyForecastDto
)
@Serializable
data class CurrentWeatherDto(
@SerialName("temperature_2m")
val temperature: Double,
@SerialName("relative_humidity_2m")
val relativeHumidity: Int,
@SerialName("apparent_temperature")
val apparentTemperature: Double,
@SerialName("is_day")
val isDay: Int,
val precipitation: Double,
@SerialName("weather_code")
val weatherCode: Int,
@SerialName("wind_speed_10m")
val windSpeed: Double
)
@Serializable
data class DailyForecastDto(
val time: List<String>,
@SerialName("weather_code")
val weatherCodes: List<Int>,
@SerialName("temperature_2m_max")
val maxTemperatures: List<Double>,
@SerialName("temperature_2m_min")
val minTemperatures: List<Double>
)Ada dua konsep penting dalam model tersebut.
Pertama, annotation @Serializable memberi tahu kotlinx.serialization bahwa class tersebut dapat diproses oleh serializer.
Kedua, annotation @SerialName digunakan ketika nama field JSON berbeda dari nama property Kotlin yang kita inginkan.
Misalnya, JSON menggunakan temperature_2m, sedangkan kode Kotlin menggunakan nama temperature.
Untuk DailyForecastDto, data prakiraan harian dikembalikan sebagai beberapa array yang indeksnya saling berkaitan. Contohnya, elemen pertama pada time, maxTemperatures, minTemperatures, dan weatherCodes mewakili hari yang sama.
Perlu diperhatikan bahwa model ini sengaja hanya mencakup field yang kita minta pada query API sebelumnya. Jika teman-teman mengubah parameter request, model response mungkin perlu disesuaikan.
6. Membuat WeatherApiClient
Sekarang kita sampai pada bagian utama artikel ini.
Kita akan membuat WeatherApiClient yang bertanggung jawab mengirim request ke Open-Meteo dan mengonversi response JSON menjadi ForecastResponse.
Buat file:
shared/src/commonMain/kotlin/com/example/weather/network/WeatherApiClient.kt
package com.example.weather.network
import com.example.weather.network.model.ForecastResponse
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.plugins.ClientRequestException
import io.ktor.client.plugins.ServerResponseException
import io.ktor.client.request.get
import io.ktor.client.request.parameter
class WeatherApiClient(
private val httpClient: HttpClient
) {
suspend fun fetchForecast(
latitude: Double,
longitude: Double
): ForecastResponse {
return try {
httpClient.get(
"https://api.open-meteo.com/v1/forecast"
) {
parameter("latitude", latitude)
parameter("longitude", longitude)
parameter(
"current",
"temperature_2m," +
"relative_humidity_2m," +
"apparent_temperature," +
"is_day," +
"precipitation," +
"weather_code," +
"wind_speed_10m"
)
parameter(
"daily",
"weather_code," +
"temperature_2m_max," +
"temperature_2m_min"
)
parameter("forecast_days", 7)
parameter("timezone", "auto")
}.body()
} catch (e: ClientRequestException) {
throw IllegalStateException(
"Request ditolak oleh server: ${e.response.status}"
)
} catch (e: ServerResponseException) {
throw IllegalStateException(
"Server mengalami masalah: ${e.response.status}"
)
}
}
}Mari kita lihat beberapa bagian yang penting.
Constructor injection
class WeatherApiClient(
private val httpClient: HttpClient
)Kita menerima HttpClient melalui constructor. Dengan pendekatan ini, WeatherApiClient tidak perlu membuat HTTP client sendiri.
Selain membuat pengelolaan resource lebih jelas, pendekatan ini akan memudahkan kita ketika menulis unit test di artikel berikutnya.
Suspend function
suspend fun fetchForecast(
latitude: Double,
longitude: Double
): ForecastResponseNetwork request membutuhkan waktu dan tidak boleh memblokir thread yang menjalankan UI. Karena itu, fungsi ini menggunakan suspend agar dapat dipanggil melalui coroutine.
Mengirim request
httpClient.get(
"https://api.open-meteo.com/v1/forecast"
) {
parameter("latitude", latitude)
parameter("longitude", longitude)
}Ktor membantu membangun request beserta query parameter-nya. Kita tidak perlu menyusun seluruh URL secara manual.
Mengonversi response
.body<ForecastResponse>()Pada kode di atas kita menuliskan .body() tanpa generic type secara eksplisit. Tipe hasilnya dapat diinferensikan dari return type fungsi, yaitu ForecastResponse.
Ktor menggunakan konfigurasi ContentNegotiation dan serializer yang telah kita pasang untuk mengonversi response menjadi object Kotlin.
Error handling
Kita menangani dua jenis HTTP error: client error dan server error.
Namun, ini belum mencakup seluruh kemungkinan kegagalan. Misalnya, gangguan jaringan dan timeout tetap perlu ditangani oleh layer yang lebih tinggi. Pada artikel berikutnya, kita akan menyusun error handling yang lebih terstruktur ketika membuat repository dan domain layer.
7. Menggunakan WeatherApiClient
Kita sudah membuat HTTP engine, konfigurasi client, model response, dan API client.
Sekarang bagaimana cara menggunakannya?
Untuk sementara, kita dapat membuat instance client secara langsung. Nantinya, setelah repository dan dependency injection diperkenalkan, proses pembuatan object akan dikelola melalui struktur yang lebih rapi.
Contoh berikut merupakan penggunaan dari Kotlin code yang berjalan dalam coroutine:
import com.example.weather.network.HttpClientFactory
import com.example.weather.network.WeatherApiClient
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
fun main() {
val httpClient = HttpClientFactory.createHttpClient()
val apiClient = WeatherApiClient(httpClient)
CoroutineScope(
SupervisorJob() + Dispatchers.Default
).launch {
try {
val forecast = apiClient.fetchForecast(
latitude = -6.2,
longitude = 106.8
)
println(
"Temperatur: ${forecast.current.temperature}"
)
println(
"Weather code: ${forecast.current.weatherCode}"
)
println(
"Jumlah hari: ${forecast.daily.time.size}"
)
} catch (e: Exception) {
println("Gagal mengambil data: ${e.message}")
} finally {
httpClient.close()
}
}
}Catatan: contoh ini hanya menunjukkan pola pemanggilan, bukan entry point yang langsung bisa ditempel ke project Android atau iOS. Pada aplikasi Android, pemanggilan sebaiknya dilakukan dari coroutine yang dikelola lifecycle, misalnya melalui viewModelScope. Pada iOS, pemanggilan nantinya akan dihubungkan ke Swift melalui integration layer yang kita buat di fase iOS.
Jika menggunakan contoh coroutine mandiri seperti di atas, pastikan environment pemanggil menyediakan dependency kotlinx-coroutines-core. Jangan menambahkan main() ini ke source set produksi hanya untuk menguji API.
Untuk memastikan hasilnya benar-benar berfungsi, teman-teman bisa memanggil fetchForecast() dari unit test atau fungsi sementara yang sesuai dengan target project.
Jika request berhasil, kita akan memperoleh data suhu, weather code, dan prakiraan harian dari Open-Meteo.
Perlu diingat bahwa angka suhu dan kondisi cuaca dapat berubah sesuai waktu request dan koordinat yang digunakan. Jadi, jangan menganggap hasil yang diperoleh harus selalu sama dengan contoh.
Bagaimana Alur Request Kita Sekarang?
Setelah semua komponen dibuat, mari kita lihat bagaimana sebuah request berjalan dari aplikasi menuju API.
Dari diagram tersebut, ada beberapa hal yang perlu kita pahami.
Aplikasi tidak perlu mengetahui apakah request dikirim melalui OkHttp atau Darwin. Aplikasi cukup memanggil WeatherApiClient.
Selanjutnya, Ktor akan menggunakan engine yang sudah disediakan oleh platform. Response dari Open-Meteo kemudian dikonversi menjadi model Kotlin dan dikembalikan kepada pemanggil.
Inilah salah satu manfaat penting dari Kotlin Multiplatform: kita dapat berbagi implementasi networking dan pemrosesan data tanpa harus menyatukan UI Android dan iOS.
Android nantinya tetap menggunakan Jetpack Compose, sedangkan iOS tetap menggunakan SwiftUI.
Struktur Project Setelah Artikel Ini
Setelah mengikuti langkah-langkah di atas, struktur shared module kita kurang lebih menjadi seperti berikut.
weather-app-kmp/
├── shared/
│ └── src/
│ ├── commonMain/
│ │ └── kotlin/
│ │ └── com/example/weather/
│ │ └── network/
│ │ ├── PlatformHttpEngine.kt
│ │ ├── HttpClientFactory.kt
│ │ ├── WeatherApiClient.kt
│ │ └── model/
│ │ └── ForecastResponse.kt
│ │
│ ├── androidMain/
│ │ └── kotlin/
│ │ └── com/example/weather/
│ │ └── network/
│ │ └── PlatformHttpEngine.kt
│ │
│ └── iosMain/
│ └── kotlin/
│ └── com/example/weather/
│ └── network/
│ └── PlatformHttpEngine.kt
│
├── androidApp/
└── iosApp/Struktur di atas menggambarkan file yang kita tambahkan pada artikel ini. File lain dari template KMP sebelumnya tetap dipertahankan.
Perlu diingat bahwa path package harus konsisten dengan deklarasi package di setiap file.
Beberapa Hal yang Perlu Diperhatikan
Sebelum menutup artikel ini, ada beberapa catatan yang menurut saya cukup penting.
Jangan membuat HttpClient untuk setiap request
HttpClient mempunyai resource yang perlu dikelola. Karena itu, sebaiknya kita menggunakan kembali instance yang sama selama dibutuhkan dan menutupnya ketika sudah tidak digunakan.
Pada contoh kita, WeatherApiClient menerima instance tersebut dari luar melalui constructor.
Jangan mencampur networking dengan UI
Kita belum membuat Compose maupun SwiftUI di artikel ini. Dan memang belum perlu.
Networking sebaiknya berada di shared module sehingga dapat digunakan oleh repository dan use case. UI nantinya hanya perlu menerima state yang siap ditampilkan.
Error handling akan kita kembangkan lagi
Pada artikel ini kita baru menangani sebagian HTTP error. Kita belum membuat result type khusus, belum menangani seluruh kemungkinan network failure, dan belum memisahkan error API dari error domain.
Semua itu akan kita bahas ketika mulai membuat repository dan domain layer.
Model API tidak harus menjadi model domain
ForecastResponse yang kita buat sekarang merupakan DTO (Data Transfer Object) untuk merepresentasikan response dari API.
Nantinya kita akan membuat domain model yang lebih sesuai dengan kebutuhan aplikasi. Dengan begitu, perubahan format response API tidak harus langsung memengaruhi UI.
Kesimpulan
Pada artikel keempat ini kita sudah menyiapkan fondasi networking untuk Weather App menggunakan Ktor Client.
Kita sudah:
Menambahkan dependency Ktor Client dan kotlinx.serialization.
Menggunakan OkHttp sebagai engine untuk Android.
Menggunakan Darwin sebagai engine untuk iOS.
Mengonfigurasikan
HttpClientdenganContentNegotiation.Membuat model response JSON untuk Open-Meteo.
Membuat
WeatherApiClientuntuk mengambil forecast berdasarkan koordinat lokasi.
Sekarang shared module kita sudah memiliki komponen yang dapat digunakan untuk berkomunikasi dengan Open-Meteo API.
Namun, kita belum selesai. Saat ini WeatherApiClient masih langsung mengembalikan DTO dari API. Kita belum mempunyai repository yang memisahkan sumber data dari business logic, dan belum mempunyai use case untuk merepresentasikan kebutuhan aplikasi.
Nah, pada artikel berikutnya kita akan mulai merapikan bagian tersebut.
Kita akan membuat Repository dan Domain Layer, memisahkan DTO dari domain model, serta menambahkan error handling yang lebih terstruktur. Kita juga akan mulai menyiapkan unit test sederhana agar logic yang kita buat bisa diuji tanpa harus selalu memanggil API sungguhan.
Untuk source code, teman-teman bisa menggunakan repository project Weather App yang sama dan melanjutkan implementasinya dari artikel sebelumnya.
Source code: Repository Weather App KMP
Sekian, terima kasih.

0 Komentar
Post a Comment
Please leave your comments, critiques or suggestions.
Because your opinion means a lot for this blog :)