Network calls in android Using retrofit and coroutines
Published: 2022-02-24 06:06:34

Making network calls is a common task in android.The commonly used Libraries are Retrofit and Volley.I am a huge fan of the retrofit Library because of the good documentation and it is very easy to setup and use.Today we are going to learn  how to do network calls using this simple library and coroutines.
Starting from version 2.6.0 Retrofit supports the concept of “suspend” functions.
Before we begin,we are going to understand a simple project that we are going to build.
We will be using mvvm architecture pattern to help you understand our today's topic.If you are not familiar with mvvm or need a refresh of this,please check this nice [article.](https://blog.mindorks.com/mvvm-architecture-android-tutorial-for-beginners-step-by-step-guide).

**Set up a new project with Kotlin and other dependencies required**
We will be using a single activity for the purpose of this tutorial.By default android  studio creates an empty activity for you to begin with.

**Add dependencies**
Add the following dependencies in your app level build.gradle.



 

```
//Lifecycle

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0-alpha03'
 implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.0-alpha03'
 implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0-alpha03'

//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'

//Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
//Coil
 implementation 'io.coil-kt:coil:1.4.0'
```


We have added the required dependencies and for the versions you can always change them as versions are being released with time

**Setting up the network package**
We are going to put our kotlin classes into packages to make things neat.First,let's set up the network package.

Create an Enum class,we will name it Status.Basically this class will represent the three states of the network call.So the `Status.kt` will look as below:



```
enum class Status {
    SUCCESS,
    ERROR,
    LOADING
}

```
Let's now create a Generic class and name it ApiModel.This class will contain information about our network call so that we can update our views accordingly.So  `ApiModel.kt` looks like below:



```
data class ApiModel<out T>(val status: Status, val data: T?, val message: String?) {
    companion object {
        fun <T> success(data: T): ApiModel<T> = ApiModel(status = Status.SUCCESS, data = data, message = null)

        fun <T> error(data: T?, message: String): ApiModel<T> =
            ApiModel(status = Status.ERROR, data = data, message = message)

        fun <T> loading(data: T?): ApiModel<T> = ApiModel(status = Status.LOADING, data = data, message = null)
    }
}
```
**Setting up our Model.**
We will be fetching our data for the current weather using the name of a city.So go ahead and obtain an api key from [here.](https://www.weatherapi.com/).If you find any problems please don't hesitate to reach out.
Now let's create our Pojo class to represent the data that we  will get back after our api call.So again our `WeatherResponse.kt`
looks like so:

```
data class WeatherResponse(
    var location: Location,
    var current: Current
)


```
Please note that i have not given the full content of the Location and Current classes but you can check them [here](https://github.com/Carlosokumu/AndroidNetworking/tree/master/app/src/main/java/com/example/androidnetworking/models) in the models package.

**Setting up Retrofit**
Since we will be using retrofit for networking,let us create some classes for this purpose.First we will create an Interface class that will be a retrofit service class.So go ahead and create a class name it  `ApiService.kt` then it should contain the following code.

```
interface ApiService {

    @GET("v1/current.json")
    suspend fun getCurrentWeather(@Query("key") key: String ="94d124c031ba4486b9c81847222402",@Query("q") cityName: String?): WeatherResponse

}

```
then go ahead and create an object class and name it `RetrofitBuilder.kt`.As the name suggests,this class will be responsible for a creating our retrofit instance using the builder pattern.Have the following code in it

```
object RetrofitBuilder {



    private fun getRetrofit(): Retrofit {
       

        val interceptor = HttpLoggingInterceptor()
        interceptor.level = when (BuildConfig.BUILD_TYPE) {
            "release" -> HttpLoggingInterceptor.Level.NONE
            else -> HttpLoggingInterceptor.Level.BODY
        }


        val okHttp= OkHttpClient.Builder()
            .addInterceptor(interceptor)
            .connectTimeout(15, TimeUnit.SECONDS)
            .readTimeout(15, TimeUnit.SECONDS)
            .build()

        return Retrofit.Builder()
            .baseUrl(Constants.REST_BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttp)
            .build()
    }

    val apiService: ApiService = getRetrofit().create(ApiService::class.java)


}

```
Since we will be using a Repository pattern,let's create a class and name it `MainRepository.kt` and  we will pass it our retrofit instance to the constructor.Have the following code in it also.

```
class MainRepository(private val retrofit: RetrofitBuilder) {
    suspend fun fetchCurrentWeather() = retrofit.apiService.getCurrentWeather()
}

```
**Setting up the ViewModel**
We are almost  getting to the View now 🙂.Go ahead and create a class  and name it `MainViewModel.kt`.As you have guessed, have the following  code in it also:

 

```
class MainViewModel(private val mainRepository: MainRepository) : ViewModel() {


    fun fetchCurrentWeather() = liveData(Dispatchers.IO) {
        emit(ApiModel.loading(data = null))
        try {
            emit(ApiModel.success(data = mainRepository.fetchCurrentWeather()))
        } catch (exception: Exception) {
            emit(ApiModel.error(data = null, message = exception.message ?: "Something went Wrong"))
        }
    }
}
```
You remember our Apimodel class we created earlier?,this is where we initialize our states.When loading,the data will be null,and if the call is sucessful,we have our data fetched into our value holder and also any error that occurred is  tapped.We will also create a Factory for our viewmodel so go ahead and create a class named `ViewmodelFactory.kt` and have the following code in it:

```
class ViewModelFactory(private val retrofit: RetrofitBuilder) : 

ViewModelProvider.Factory {



    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
            return MainViewModel(MainRepository(retrofit)) as T
        }
        throw IllegalArgumentException("Unknown class name")
    }

}
```
Let's now create the user interface.

**Setting up the User Interface**
First,let's understand how the interface will work,when a user enters the name of a city and the name is correct,the current weather conditions will be displayed in a CardView.
So `activity_main.xml`

```
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/coordinator_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <include
        android:id="@+id/appBar"
        layout="@layout/app_bar_layout" />


    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="@dimen/margin_normal"
        app:cardBackgroundColor="@color/white"
        app:cardCornerRadius="@dimen/margin_small">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">


            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="@dimen/margin_normal"
                    android:fontFamily="casual"
                    android:padding="@dimen/margin_normal"
                    android:text="Current Weather"
                    android:textColor="@color/black" />

                <Space
                    android:layout_width="0dp"
                    android:layout_height="0dp"
                    android:layout_gravity="center_vertical"
                    android:layout_weight="1"
                    android:minWidth="0dp" />

                <ImageView
                    android:id="@+id/imageIcon"
                    android:layout_width="44dp"
                    android:layout_height="44dp"
                    android:layout_margin="@dimen/margin_normal" />

            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/tempC"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="@dimen/margin_normal"
                    android:fontFamily="sans-serif-smallcaps"
                    android:padding="@dimen/margin_normal"
                    android:textColor="@color/black"
                    android:textSize="@dimen/margin_normal"
                    tools:text="20.3" />

                <Space
                    android:layout_width="0dp"
                    android:layout_height="0dp"
                    android:layout_gravity="center_vertical"
                    android:layout_weight="1"
                    android:minWidth="0dp" />

                <TextView
                    android:id="@+id/tempF"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="@dimen/margin_normal"
                    android:fontFamily="sans-serif-smallcaps"
                    android:padding="@dimen/margin_small"
                    android:textColor="@color/black"
                    android:textSize="@dimen/margin_normal"
                    tools:text="25.7" />


            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:fontFamily="sans-serif-thin"
                    android:padding="@dimen/margin_small"
                    android:textColor="@color/black"
                    android:textSize="@dimen/margin_normal"
                    tools:text="Temp(C)" />

                <Space
                    android:layout_width="0dp"
                    android:layout_height="0dp"
                    android:layout_gravity="center_vertical"
                    android:layout_weight="1"
                    android:minWidth="0dp" />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:fontFamily="sans-serif-thin"
                    android:padding="@dimen/margin_small"
                    android:textColor="@color/black"
                    android:textSize="@dimen/margin_normal"
                    tools:text="Temp(f)" />


            </LinearLayout>

            <TextView
                android:id="@+id/txtWeather"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:layout_margin="@dimen/margin_small"
                android:fontFamily="sans-serif-thin"
                android:textStyle="normal"
                tools:text="Cloudy" />
        </LinearLayout>

    </androidx.cardview.widget.CardView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

```
then `app_bar_layout.xml`

```
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:elevation="@dimen/app_bar_elevation"
    android:gravity="center_vertical">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_vertical"
        android:minHeight="@dimen/toolbar_height"
        app:contentInsetStart="@dimen/toolbar_inset_start"
        app:contentInsetStartWithNavigation="@dimen/toolbar_inset_start">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="@dimen/margin_small"
            android:gravity="center_vertical"
            android:orientation="vertical">

            <TextView
                android:id="@+id/title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="start"
                android:fontFamily="casual"
                android:singleLine="true"
                android:textSize="@dimen/toolbar_title_text_size"
                tools:text="Toolbar title" />

            <TextView
                android:id="@+id/subtitle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="start"
                android:singleLine="true"
                android:visibility="gone"
                tools:text="Toolbar subtitle"
                tools:visibility="visible" />

        </LinearLayout>

    </androidx.appcompat.widget.Toolbar>

</com.google.android.material.appbar.AppBarLayout>
```

And Finally our `MainActivity.kt` will look us below:

```
class MainActivity : AppCompatActivity() {


    private lateinit var searchView: SearchView
    private lateinit var searchItem: MenuItem
    private lateinit var viewModel: MainViewModel
    private lateinit var weatherIcon: ImageView
    private lateinit var txtWeather: TextView
    private lateinit var tempF: TextView
    private lateinit var tempC: TextView


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<Toolbar>(R.id.toolbar).inflateMenu(R.menu.menu_search)

        searchItem = findViewById<AppBarLayout>(R.id.appBar).findViewById<Toolbar>(R.id.toolbar).menu.findItem(
            R.id.search_item
        )
        searchView = searchItem.actionView as SearchView


        viewModel = ViewModelProviders.of(
            this,
            ViewModelFactory(RetrofitBuilder)
        ).get(MainViewModel::class.java)

        weatherIcon = findViewById(R.id.imageIcon)
        txtWeather = findViewById(R.id.txtWeather)
        tempF = findViewById(R.id.tempF)
        tempC = findViewById(R.id.tempC)

        searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
            override fun onQueryTextSubmit(query: String?): Boolean {
                viewModel.fetchCurrentWeather(query).observe(this@MainActivity,::onResponse)
                return true
            }

            override fun onQueryTextChange(newText: String?): Boolean {
                return true
            }

        })

    }

    private fun onResponse(apiModel: ApiModel<WeatherResponse>?) {
        when(apiModel?.status){
            Status.ERROR -> {
                Toast.makeText(this, apiModel.message, Toast.LENGTH_SHORT).show()
            }
            Status.SUCCESS -> {
                val data = apiModel.data
                weatherIcon.load("https:" + apiModel.data?.current?.condition?.icon)
                
                txtWeather.text = data?.current?.condition?.text
                tempC.text = data?.current?.temp_c.toString()
                tempF.text = data?.current?.temp_f.toString()
            }
            Status.LOADING -> {
                Toast.makeText(this, "Loading", Toast.LENGTH_SHORT).show()
            }
        }
    }


}
```

**Wrapping up**
This was  a  quick guide  on how you can use retrofit and coroutines in android to easily make an api call.You can check out the full code in my repository [here](https://github.com/Carlosokumu/AndroidNetworking).Happy Coding!