KMP #02: Merancang Struktur Weather App
Di artikel sebelumnya, kita sudah berhasil membuat project Kotlin Multiplatform pertama menggunakan Android Studio. Kita juga sudah mengenal struktur
commonMain, androidMain, dan iosMain, serta memahami bagaimana Kotlin Multiplatform memungkinkan kita berbagi logic antara Android dan iOS.Kalau teman-teman belum mengikuti artikel sebelumnya, saya sarankan membacanya terlebih dahulu supaya lebih mudah mengikuti tutorial kali ini.
Pada artikel ini, kita belum akan menulis banyak kode Kotlin. Sebelum mulai mengimplementasikan fitur, ada baiknya kita memahami terlebih dahulu aplikasi seperti apa yang ingin kita buat, bagaimana aplikasi mendapatkan data cuaca, dan bagaimana data tersebut akan direpresentasikan di dalam project.
Kita akan mulai merancang Weather App yang nantinya dapat berjalan di Android dan iOS dengan pendekatan:
Shared logic: Kotlin Multiplatform.
Android UI: Jetpack Compose.
iOS UI: SwiftUI.
Networking: Ktor Client.
Weather API: Open-Meteo.
Seperti biasa, kita akan mengembangkan aplikasi yang sama secara bertahap, bukan membuat project baru untuk setiap artikel.
Apa yang akan kita buat?
Bayangkan kita ingin membuat aplikasi cuaca sederhana. Ketika aplikasi dibuka, pengguna bisa mencari nama kota, memilih lokasi yang sesuai, lalu melihat kondisi cuaca saat ini dan prakiraan cuaca untuk tujuh hari ke depan.
Untuk versi awal, kita akan membuat tiga bagian utama:
Location Search — mencari kota berdasarkan nama.
Current Weather — menampilkan kondisi cuaca saat ini.
7-Day Forecast — menampilkan prakiraan cuaca harian selama tujuh hari.
Gambaran sederhananya seperti berikut.
Diagram ini menunjukkan hubungan antara fitur aplikasi dan API yang akan kita gunakan. Satu API digunakan untuk mencari lokasi, sedangkan API lainnya menyediakan data cuaca berdasarkan koordinat lokasi tersebut.
Untuk sementara, kita belum membahas detail UI Android dan iOS. Fokus kita adalah memastikan alur data dan struktur modelnya sudah jelas sebelum masuk ke implementasi.
Kenalan dengan Open-Meteo API
Untuk mendapatkan data cuaca, kita membutuhkan layanan yang menyediakan informasi cuaca melalui API.
Ada banyak layanan yang bisa digunakan, tetapi untuk tutorial ini saya memilih Open-Meteo.
Kenapa Open-Meteo?
Gratis untuk penggunaan yang sesuai dengan ketentuan layanannya.
Menyediakan data cuaca melalui REST API.
Tidak membutuhkan API key untuk penggunaan gratis yang memenuhi ketentuannya.
Menyediakan endpoint untuk pencarian lokasi dan prakiraan cuaca.
Menggunakan format response JSON yang mudah diproses menggunakan Kotlin.
Website resminya bisa teman-teman kunjungi di:
Ada dua endpoint utama yang akan kita gunakan dalam tutorial ini.
| Endpoint | Fungsi |
|---|---|
| Geocoding API | Mencari lokasi berdasarkan nama kota |
| Forecast API | Mengambil data cuaca berdasarkan koordinat |
Kita akan membahas keduanya satu per satu.
1. Geocoding API: Mencari Lokasi
Misalnya, pengguna mengetik Jakarta pada kolom pencarian aplikasi.
Masalahnya, API cuaca umumnya membutuhkan koordinat geografis berupa latitude dan longitude, bukan sekadar nama kota.
Di sinilah Geocoding API dibutuhkan.
Endpoint yang akan kita gunakan:
https://geocoding-api.open-meteo.com/v1/search
Kita bisa menambahkan beberapa query parameter untuk menentukan pencarian.
Contohnya:
GET https://geocoding-api.open-meteo.com/v1/search?name=Jakarta&count=5&language=id&format=jsonPenjelasan parameter:
| Parameter | Fungsi |
|---|---|
name | Nama lokasi yang dicari |
count | Jumlah maksimum hasil yang diminta |
language | Bahasa untuk informasi lokasi |
format | Format response, dalam hal ini JSON |
Jika request berhasil, API akan mengembalikan daftar lokasi yang cocok dengan pencarian.
Contoh response yang disederhanakan:
{
"results": [
{
"id": 1642911,
"name": "Jakarta",
"latitude": -6.2088,
"longitude": 106.8456,
"country": "Indonesia",
"admin1": "Jakarta"
}
],
"generationtime_ms": 0.2
}Catatan: JSON di atas merupakan ilustrasi response dengan struktur yang disederhanakan. Hasil aktual, termasuk jumlah lokasi dan informasi administratifnya, bergantung pada hasil pencarian API.
Perhatikan bahwa data yang kita butuhkan bukan hanya nama kota.
Ada beberapa informasi penting di dalam response tersebut:
name: nama lokasi.latitude: koordinat lintang.longitude: koordinat bujur.country: negara tempat lokasi berada.admin1: informasi administratif, jika tersedia.
Latitude dan longitude inilah yang akan digunakan untuk mengambil data cuaca.
Contohnya, jika pengguna memilih Jakarta, aplikasi akan menggunakan koordinat lokasi tersebut untuk meminta data cuaca dari Forecast API.
Hal ini juga penting untuk menghindari kesalahan ketika ada beberapa kota dengan nama yang sama.
Jadi, kita tidak bisa langsung menganggap bahwa satu nama kota selalu merujuk pada satu lokasi.
2. Forecast API: Mengambil Data Cuaca
Setelah pengguna memilih lokasi, kita bisa mengambil data cuaca berdasarkan koordinatnya.
Endpoint yang akan digunakan adalah:
https://api.open-meteo.com/v1/forecast
Untuk versi awal aplikasi, kita membutuhkan informasi berikut:
Suhu saat ini.
Kelembapan relatif.
Suhu yang dirasakan.
Kode kondisi cuaca.
Kecepatan angin.
Suhu maksimum dan minimum untuk prakiraan harian.
Kita juga ingin mendapatkan prakiraan untuk tujuh hari.
Contoh request:
GET https://api.open-meteo.com/v1/forecast?latitude=-6.2088&longitude=106.8456¤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&timezone=auto&forecast_days=7Supaya lebih mudah dipahami, berikut penjelasan parameter utamanya.
| Parameter | Fungsi |
|---|---|
latitude | Koordinat lintang lokasi |
longitude | Koordinat bujur lokasi |
current | Variabel cuaca untuk kondisi saat ini |
daily | Variabel untuk prakiraan harian |
timezone=auto | Menggunakan zona waktu lokasi |
forecast_days=7 | Meminta prakiraan untuk tujuh hari |
Kita menggunakan weather_code untuk mengetahui kategori kondisi cuaca, seperti cerah, berawan, atau hujan. Nantinya kode ini akan kita mapping menjadi teks dan ikon pada masing-masing UI.
Contoh response Forecast API
Response aktual memiliki lebih banyak informasi, termasuk metadata waktu dan satuan. Berikut contoh sederhana untuk menunjukkan struktur data yang akan kita olah:
{
"latitude": -6.2088,
"longitude": 106.8456,
"timezone": "Asia/Jakarta",
"current": {
"temperature_2m": 31.2,
"relative_humidity_2m": 68,
"apparent_temperature": 35.1,
"is_day": 1,
"precipitation": 0.0,
"weather_code": 2,
"wind_speed_10m": 12.5
},
"daily": {
"time": [
"2026-10-15",
"2026-10-16",
"2026-10-17"
],
"weather_code": [2, 3, 61],
"temperature_2m_max": [32.1, 31.5, 30.8],
"temperature_2m_min": [25.2, 24.9, 24.7]
}
}Angka suhu, kelembapan, dan nilai cuaca di atas hanya contoh ilustratif, bukan hasil pengambilan data real-time. Daftar tanggal juga dipersingkat agar mudah dibaca.
Ada dua bagian yang perlu kita perhatikan.
Pertama, current.
Bagian ini menyimpan informasi kondisi cuaca saat ini. Kita akan menggunakan data tersebut untuk menampilkan suhu utama, kondisi cuaca, kelembapan, dan kecepatan angin.
Kedua, daily.
Bagian ini berisi beberapa array yang saling berhubungan berdasarkan indeks. Misalnya, elemen pertama pada time, weather_code, temperature_2m_max, dan temperature_2m_min merujuk pada hari yang sama.
Untuk aplikasi kita, data tersebut nantinya akan diubah menjadi daftar prakiraan harian yang lebih mudah digunakan oleh UI.
Perlu diingat juga bahwa current merupakan kondisi cuaca terkini yang disediakan oleh API, bukan jaminan pengukuran langsung dari sensor di lokasi pengguna.
Memahami Alur Data Weather App
Sekarang kita sudah mengenal dua endpoint yang akan digunakan. Mari kita hubungkan keduanya dalam satu alur aplikasi.
Ada beberapa hal penting dalam alur ini.
Pertama, aplikasi tidak langsung meminta data cuaca ketika pengguna mengetik nama kota. Kita perlu mencari lokasi terlebih dahulu.
Kedua, pengguna harus bisa memilih lokasi yang sesuai apabila pencarian menghasilkan beberapa pilihan.
Ketiga, setelah lokasi dipilih, aplikasi dapat menggunakan koordinatnya untuk mengambil data cuaca.
Keempat, hasil API tidak akan langsung ditampilkan begitu saja. Data JSON perlu diparsing, dipetakan ke model aplikasi, lalu diteruskan ke UI Android atau iOS.
Bagian terakhir inilah yang akan kita persiapkan melalui desain model data.
3. Merancang Model Data
Sekarang kita masuk ke bagian Kotlin Multiplatform.
Kita sudah mengetahui informasi yang dibutuhkan aplikasi. Pertanyaannya, bagaimana sebaiknya kita merepresentasikan data tersebut dalam kode Kotlin?
Untuk versi awal, saya akan menggunakan tiga model utama:
LocationCurrentWeatherDailyForecast
Ketiganya akan ditempatkan pada shared module karena merupakan representasi data yang dibutuhkan oleh logic aplikasi di Android maupun iOS.
Location
Model Location merepresentasikan lokasi yang dipilih pengguna.
Contoh implementasi:
data class Location(
val id: Long?,
val name: String,
val country: String,
val adminArea: String?,
val latitude: Double,
val longitude: Double
)Penjelasan beberapa property:
id: identifier lokasi jika tersedia.name: nama kota atau lokasi.country: nama negara.adminArea: informasi administratif tambahan, jika tersedia.latitude: koordinat lintang.longitude: koordinat bujur.
Kenapa kita menggunakan Double untuk koordinat?
Karena latitude dan longitude bisa memiliki nilai desimal. Jika kita menggunakan integer, informasi koordinat akan kehilangan presisi.
Sementara itu, id dan adminArea dibuat nullable karena informasi tersebut tidak selalu tersedia dalam setiap sumber data.
CurrentWeather
Model berikutnya adalah CurrentWeather.
Model ini menyimpan informasi kondisi cuaca saat ini.
data class CurrentWeather(
val temperature: Double,
val apparentTemperature: Double,
val relativeHumidity: Int,
val isDay: Boolean,
val precipitation: Double,
val weatherCode: Int,
val windSpeed: Double
)Beberapa property mungkin sudah cukup familiar.
temperature merupakan suhu udara, sedangkan apparentTemperature merupakan suhu yang dirasakan berdasarkan perhitungan API.
relativeHumidity menunjukkan kelembapan relatif dalam persen.
isDay digunakan untuk membedakan kondisi siang dan malam berdasarkan data API.
Sementara itu, weatherCode akan menjadi dasar untuk menentukan deskripsi kondisi cuaca dan ikon yang ditampilkan pada UI.
Contohnya, kode cuaca tertentu bisa dipetakan menjadi teks seperti "Cerah", "Berawan", atau "Hujan".
Namun, mapping tersebut belum kita implementasikan sekarang. Kita akan membahasnya ketika mulai membangun UI cuaca.
Perlu diperhatikan bahwa nama property pada model aplikasi tidak harus sama persis dengan nama field pada response JSON. Kita bisa menggunakan mapper agar struktur API tidak terlalu terikat dengan struktur model domain.
DailyForecast
Model ketiga adalah DailyForecast.
data class DailyForecast(
val date: String,
val weatherCode: Int,
val temperatureMax: Double,
val temperatureMin: Double
)Model ini merepresentasikan prakiraan cuaca untuk satu hari.
Contohnya, satu objek DailyForecast dapat berisi tanggal, kode kondisi cuaca, suhu maksimum, dan suhu minimum.
Dengan struktur seperti ini, kita tidak perlu mengelola beberapa array secara terpisah ketika menampilkan daftar prakiraan cuaca.
Nantinya, data daily dari API akan dipetakan menjadi List<DailyForecast>.
4. Bagaimana Model Data Ini Digunakan?
Ketiga model tadi memiliki tanggung jawab masing-masing.
Diagram tersebut merupakan gambaran konseptual, bukan implementasi class yang sudah lengkap.
WeatherRepository nantinya bertanggung jawab menyediakan data yang dibutuhkan aplikasi. UI tidak perlu mengetahui detail URL, query parameter, ataupun struktur response dari Open-Meteo.
Sebagai contoh, UI cukup meminta data cuaca untuk lokasi yang dipilih. Detail bagaimana request dibuat dan bagaimana JSON diproses akan menjadi tanggung jawab layer data.
Pemisahan ini akan membuat shared module lebih mudah dikembangkan dan diuji.
5. Menentukan Struktur Shared Module
Sebelum mulai menambahkan dependency Ktor, kita juga perlu menentukan di mana kode-kode tersebut akan diletakkan.
Untuk tutorial ini, kita akan menggunakan struktur yang memisahkan domain, data, dan network.
Gambaran struktur yang akan kita kembangkan:
shared/
└── src/
├── commonMain/
│ └── kotlin/
│ └── com/example/weather/
│ ├── domain/
│ │ ├── model/
│ │ │ ├── Location.kt
│ │ │ ├── CurrentWeather.kt
│ │ │ └── DailyForecast.kt
│ │ └── repository/
│ │ └── WeatherRepository.kt
│ │
│ ├── data/
│ │ ├── remote/
│ │ │ ├── WeatherApiClient.kt
│ │ │ └── dto/
│ │ ├── mapper/
│ │ └── repository/
│ │ └── WeatherRepositoryImpl.kt
│ │
│ └── domain/
│ └── usecase/
│
├── androidMain/
└── iosMain/Struktur ini adalah target organisasi kode yang akan kita bangun secara bertahap. Tidak semua file perlu dibuat sekarang.
Ada tiga bagian penting yang perlu dipahami.
Domain layer
Berisi model domain dan kontrak repository. Nantinya, use case juga ditempatkan di sini untuk merepresentasikan operasi yang dibutuhkan aplikasi.
Data layer
Berisi implementasi repository, DTO untuk response API, serta mapper yang mengubah data dari API menjadi model domain.
Remote data source
Berisi kode untuk berkomunikasi dengan Open-Meteo API. Di sinilah WeatherApiClient akan menggunakan Ktor Client pada artikel berikutnya.
Semua bagian tersebut akan kita usahakan tetap berada di commonMain selama tidak membutuhkan API khusus platform.
Dengan begitu, logic yang sama dapat digunakan oleh aplikasi Android dan iOS.
6. Memisahkan DTO dan Domain Model
Mungkin teman-teman bertanya, kenapa kita membutuhkan DTO jika sudah memiliki model Location, CurrentWeather, dan DailyForecast?
DTO atau Data Transfer Object digunakan untuk merepresentasikan struktur data yang diterima dari API.
Sebagai contoh, response Open-Meteo menggunakan nama field seperti:
{
"temperature_2m": 31.2,
"apparent_temperature": 35.1,
"relative_humidity_2m": 68,
"weather_code": 2
}Sedangkan model domain kita menggunakan nama property yang lebih sesuai dengan kebutuhan aplikasi:
data class CurrentWeather(
val temperature: Double,
val apparentTemperature: Double,
val relativeHumidity: Int,
val isDay: Boolean,
val precipitation: Double,
val weatherCode: Int,
val windSpeed: Double
)Keduanya memiliki tujuan yang berbeda.
DTO mengikuti kontrak API, sedangkan domain model merepresentasikan data yang dibutuhkan oleh aplikasi.
Nantinya, mapper akan bertugas mengubah DTO menjadi domain model. Dengan cara ini, perubahan pada struktur response API tidak harus langsung memengaruhi UI Android dan iOS.
Kita juga dapat menambahkan aturan validasi dan transformasi data di tempat yang tepat.
7. Bagaimana Android dan iOS Menggunakan Data yang Sama?
Salah satu tujuan utama tutorial ini adalah berbagi logic tanpa menyatukan UI.
Karena itu, kita tidak akan membuat satu UI yang digunakan bersama oleh Android dan iOS.
Sebaliknya, kedua platform akan menggunakan model dan logic yang sama dari shared module.
Pada Android, data akan dikonsumsi melalui ViewModel dan ditampilkan menggunakan Jetpack Compose.
Pada iOS, data yang sama akan digunakan oleh wrapper atau observable object di sisi Swift, kemudian ditampilkan melalui SwiftUI.
Jadi, kita tidak perlu membuat ulang request API dan transformasi data hanya karena UI-nya berbeda.
Tentu saja, integrasi coroutine, StateFlow, dan Kotlin-to-Swift interop akan kita bahas pada artikel-artikel berikutnya.
Untuk sekarang, cukup pahami bahwa shared module menyediakan data dan logic, sedangkan setiap platform menentukan bagaimana data tersebut ditampilkan kepada pengguna.
8. Hal yang Perlu Diperhatikan Sebelum Coding
Sebelum menutup artikel ini, ada beberapa keputusan desain yang perlu kita ingat ketika mulai mengimplementasikan API.
Pertama, pencarian lokasi dapat menghasilkan lebih dari satu hasil.
Karena itu, aplikasi perlu menampilkan daftar lokasi dan membiarkan pengguna memilih hasil yang benar.
Kedua, koordinat merupakan bagian penting dari pemilihan lokasi.
Jangan hanya menyimpan nama kota. Latitude dan longitude dibutuhkan untuk meminta data cuaca yang sesuai.
Ketiga, response API bukan domain model.
Gunakan DTO dan mapper untuk memisahkan struktur response dari model yang digunakan aplikasi.
Keempat, perhatikan satuan data.
Suhu, kecepatan angin, dan variabel cuaca lainnya memiliki satuan yang perlu dipahami. Jangan mengasumsikan satuan tanpa memeriksa dokumentasi API.
Kelima, jangan mengikat UI pada format response API.
Baik Jetpack Compose maupun SwiftUI sebaiknya menggunakan data yang sudah diproses oleh shared module.
Dengan prinsip-prinsip tersebut, kita mempunyai fondasi yang lebih jelas sebelum mulai menulis networking code.
Kesimpulan
Pada artikel ketiga ini, kita sudah merancang fondasi Weather App yang akan dikembangkan sepanjang seri tutorial Kotlin Multiplatform.
Kita sudah membahas:
Open-Meteo Geocoding API untuk mencari lokasi.
Open-Meteo Forecast API untuk mendapatkan data cuaca.
Alur pencarian lokasi hingga menampilkan prakiraan cuaca.
Model domain
Location,CurrentWeather, danDailyForecast.Pemisahan DTO, mapper, repository, dan domain model.
Struktur awal shared module untuk Android dan iOS.
Kita memang belum menulis implementasi networking. Namun, sekarang kita sudah memahami data apa yang dibutuhkan dan bagaimana data tersebut akan mengalir di dalam aplikasi.
Pada artikel berikutnya, kita akan mulai masuk ke bagian yang lebih teknis: Setup Networking dengan Ktor Client di Shared Module.
Kita akan menambahkan dependency yang diperlukan, mengonfigurasi HTTP engine untuk Android dan iOS, lalu mulai membuat WeatherApiClient untuk mengambil data dari Open-Meteo.
Jadi, pastikan project dari artikel sebelumnya tetap tersimpan karena kita akan melanjutkan pengembangan dari project yang sama.
Referensi:
Open-Meteo: https://open-meteo.com/
Geocoding API: https://open-meteo.com/en/docs/geocoding-api
Forecast API: https://open-meteo.com/en/docs
Source code: Repository GitHub Weather App KMP akan menjadi tempat kita menyimpan progress project berdasarkan setiap artikel. Tambahkan link repository setelah source code artikel ini tersedia.
Sekian, terima kasih.

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