Implementing Ktor APIs in an Android app using Jetpack Compose, MVVM and Clean Architecture principles.

Mario Manhique
4 min readJun 17, 2023

--

Did you know that u don’t need Retrofit to implement a Ktor API in an Android Application?

Ktor is a flexible and lightweight framework for building asynchronous servers and clients in Kotlin. It can be used for implementing APIs in Android apps as well. Ktor provides its own HTTP client, which you can use to make API requests without relying on Retrofit.

Kotlin is a modern programming language that runs on the Java Virtual Machine (JVM) and can also be compiled to JavaScript or native code. It was developed by JetBrains and was first released in 2011. Kotlin is designed to be expressive, concise, and interoperable with existing Java code.

Here’s a step-by-step on how to implement Ktor APIs in an Android app using Jetpack Compose, MVVM and Clean Architecture principles:

In this specific tutorial, we will be using a movie api which only contains a title and a description of the movie,”If that makes sense (: ”.

Step 1. Set up your Android project

  • Create a new Android project in Android Studio with the desired package structure.
  • Add the necessary dependencies to your app’s build.gradle file:
implementation "io.ktor:ktor-client-android:$ktorVersion"
implementation "io.ktor:ktor-client-json:$ktorVersion"
implementation "io.ktor:ktor-client-serialization:$ktorVersion"
implementation "io.ktor:ktor-client-logging:$ktorVersion"

Step 2. Define your API models

  • Create a package named data.remote and define the API models for movies.
package com.example.movieapp.data.remote.model

import kotlinx.serialization.Serializable

@Serializable
data class Movie(
val title: String,
val description: String
)

Step 3. Create the KtorApiClient

  • Create a package, such as data.remote.api, and create a KtorApiClient class.
  • In the KtorApiClient class, create an instance of HttpClient from Ktor and configure it with necessary features, such as JSON serialization and logging.
  • Implement methods in the KtorApiClient class to make API requests using Ktor's HTTP client.
  • Use the Ktor client to handle HTTP requests and receive responses. You can use the HttpClient's get, post, put, or delete functions, and handle the responses accordingly.
package com.example.movieapp.data.remote.api

import com.example.movieapp.data.remote.model.Movie
import io.ktor.client.HttpClient
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.client.features.logging.LogLevel
import io.ktor.client.features.logging.Logging
import io.ktor.client.request.get
import io.ktor.http.URLBuilder
import io.ktor.http.takeFrom

class KtorApiClient {

private val httpClient = HttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer()
}
install(Logging) {
level = LogLevel.ALL
}
}

suspend fun getMovies(): List<Movie> {
val url = URLBuilder().apply {
takeFrom("https://api.example.com/movies") // Replace with your API endpoint
}
return httpClient.get<List<Movie>>(url.build())
}
}

Step 4. Implement the Repository

  • Create a package named data.repository.
  • Create a MovieRepository class that interacts with the KtorApiClient.
package com.example.movieapp.data.repository

import com.example.movieapp.data.remote.api.KtorApiClient
import com.example.movieapp.data.remote.model.Movie

class MovieRepository(private val apiClient: KtorApiClient) {

suspend fun getMovies(): List<Movie> {
return apiClient.getMovies()
}
}

Step 5. Implement the ViewModel

  • Create a package named presentation.viewmodel.
  • Create a MovieViewModel class that extends ViewModel and interacts with the repository.
  • In the ViewModel, inject the Repository instance and implement methods to interact with the repository and retrieve the required data.
  • Use Kotlin coroutines or Flow for asynchronous operations and handle the data asynchronously.
package com.example.movieapp.presentation.viewmodel

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.movieapp.data.remote.model.Movie
import com.example.movieapp.data.repository.MovieRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class MovieViewModel(private val repository: MovieRepository) : ViewModel() {

private val _movies = MutableStateFlow<List<Movie>>(emptyList())
val movies: StateFlow<List<Movie>> = _movies

init {
fetchMovies()
}

private fun fetchMovies() {
viewModelScope.launch(Dispatchers.IO) {
val fetchedMovies = repository.getMovies()
_movies.emit(fetchedMovies)
}
}
}

Step 6. Implement the UI with Jetpack Compose

  • Create Jetpack Compose UI components in a package named presentation.ui.
  • Create a Composable function that displays a list of movies:
  • Use the ViewModel to observe and access the data from the repository, and update the UI accordingly.
  • Use Jetpack Compose’s rememberViewModel() or viewModel() functions to retrieve an instance of the ViewModel and observe the data using collectAsState()(On the next step).
package com.example.movieapp.presentation.ui

import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import com.example.movieapp.data.remote.model.Movie

@Composable
fun MovieList(movies: List<Movie>) {
LazyColumn {
items(movies) { movie ->
MovieItem(movie)
}
}
}

@Composable
fun MovieItem(movie: Movie) {
// Compose UI for displaying a single movie item
}

Step 7. Connect the UI to the ViewModel

  • In your Activity or Fragment, use the setContent {} function to set the content view to your Jetpack Compose UI.
class MainActivity : AppCompatActivity() {
private val movieViewModel: MovieViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MovieAppTheme {
val movies by movieViewModel.movies.collectAsState()
MovieList(movies)
}
}
}
}

Remember to replace the API endpoint with the the actual API endpoint you’re working with, and also implement the UI components and of course choose your own styling.

If you managed to get this far, thank for reading and I hope you enjoyed it.

One last thing, please #Follow.

--

--

Mario Manhique

I am self-taught Android developer. I dedicate most of my time developing Android applications using Jetpack Compose