Building Multiplatform Weather App using KMM : Part 2 — Fetching Data

We already had project setup & UI for each platform from Part 1. It is now time to display the real data that we fetch from our API source. The weather data will be taken from https://api.openweathermap.org/. It is a free source but we still need to register to get API Key.
The data will be return in Json format. So in order to get more organize information, it will be nice if we have Class for this model data.
@Serializable
data class WeatherAPIResponse (
@SerialName("coord" ) var coord : Coord? = Coord(),
@SerialName("weather" ) var weather : ArrayList<Weather> = arrayListOf(),
@SerialName("base" ) var base : String? = null,
@SerialName("main" ) var main : Main = Main(),
@SerialName("visibility" ) var visibility : Int? = null,
@SerialName("wind" ) var wind : Wind? = Wind(),
@SerialName("rain" ) var rain : Rain? = Rain(),
@SerialName("clouds" ) var clouds : Clouds? = Clouds(),
@SerialName("dt" ) var dt : Int? = null,
@SerialName("sys" ) var sys : Sys? = Sys(),
@SerialName("timezone" ) var timezone : Int? = null,
@SerialName("id" ) var id : Int? = null,
@SerialName("name" ) var name : String? = null,
@SerialName("cod" ) var cod : Int? = null
)
@Serializable
data class Coord (
@SerialName("lon" ) var lon : Double? = null,
@SerialName("lat" ) var lat : Double? = null
)
@Serializable
data class Weather (
@SerialName("id" ) var id : Int? = null,
@SerialName("main" ) var main : String? = null,
@SerialName("description" ) var description : String? = null,
@SerialName("icon" ) var icon : String? = null
)
@Serializable
data class Main (
@SerialName("temp" ) var temp : Double? = null,
@SerialName("feels_like" ) var feelsLike : Double? = null,
@SerialName("temp_min" ) var tempMin : Double? = null,
@SerialName("temp_max" ) var tempMax : Double? = null,
@SerialName("pressure" ) var pressure : Int? = null,
@SerialName("humidity" ) var humidity : Int? = null,
@SerialName("sea_level" ) var seaLevel : Int? = null,
@SerialName("grnd_level" ) var grndLevel : Int? = null
)
@Serializable
data class Wind (
@SerialName("speed" ) var speed : Double? = null,
@SerialName("deg" ) var deg : Int? = null,
@SerialName("gust" ) var gust : Double? = null
)
@Serializable
data class Rain (
@SerialName("1h" ) var h : Double? = null
)
@Serializable
data class Clouds (
@SerialName("all" ) var all : Int? = null
)
@Serializable
data class Sys (
@SerialName("type" ) var type : Int? = null,
@SerialName("id" ) var id : Int? = null,
@SerialName("country" ) var country : String? = null,
@SerialName("sunrise" ) var sunrise : Int? = null,
@SerialName("sunset" ) var sunset : Int? = null
)The most important step will be to create function that will retrive data from its source inside commonMain
class WeatherAPI {
private val apiUrl =
"https://api.openweathermap.org/data/2.5/weather?lat=45.5019&lon=-73.5674&appid=${API_KEY}"
@OptIn(DelicateCoroutinesApi::class)
fun getWeatherAPIData(
successFunction: (WeatherAPIResponse) -> Unit, failureFunction: (String) -> Unit) {
GlobalScope.launch(ApplicationDispatcher) {
try {
val url = apiUrl
val json = HttpClient().get(url) {}
Json.decodeFromString(WeatherAPIResponse.serializer(), json.bodyAsText())
.also(successFunction)
} catch (ex: Exception) {
failureFunction("${ex.message}")
}
}
}
}Since Ktor needs to be called on a coroutine, this launches a lambda using a dispatcher, ApplicationDispatcher. This needs to be defined before you can build the app without errors.
Now, we need to write ApplicationDispatcher class inside CommonMain using expect. By using expect we are stating that the application for this class will be platform specific
internal expect val ApplicationDispatcher: CoroutineDispatcherHence, we need to write the actual class inside shared/androidMain
internal actual val ApplicationDispatcher: CoroutineDispatcher = Dispatchers.DefaultAnd also inside shared/iosMain, defining the expected ApplicationDispatcher variable, but this time in iOS. It’s a bit more complicated than Android. Since iOS doesn’t support coroutines, any calls to dispatch to a coroutine will be dispatched to the main queue in iOS. This is for simplicity.
// 1
internal actual val ApplicationDispatcher: CoroutineDispatcher =
NsQueueDispatcher(dispatch_get_main_queue())
// 2
internal class NsQueueDispatcher(
private val dispatchQueue: dispatch_queue_t
) : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
dispatch_async(dispatchQueue) {
block.run()
}
}
}The getWeatherData() function is ready. We will be using getWeatherData function inside each platform later and present it for each UI.
To Be cont’d to Part 3






