πŸš† Retrofit adapters for modeling network responses with Kotlin Result, Jetpack Paging3, and Arrow Either.

Overview

Retrofit Adapters


License API Build Status Profile Dokka

πŸš† Retrofit adapters for modeling network responses with Kotlin Result, Jetpack Paging3, and Arrow Either.

Sandwich

If you're interested in a more specified and lightweight Monad sealed API library for modeling Retrofit responses and handling exceptions, check out Sandwich.

Kotlin's Result

This library allows you to model your Retrofit responses with Kotlin's Result class.

Maven Central

Add the dependency below to your module's build.gradle file:

dependencies {
    implementation "com.github.skydoves:retrofit-adapters-result:1.0.0"
}

ResultCallAdapterFactory

You can return Kotlin's Result class to the Retrofit's service methods by setting ResultCallAdapterFactory like the below:

val retrofit: Retrofit = Retrofit.Builder()
    .baseUrl("BASE_URL")
    .addConverterFactory(..)
    .addCallAdapterFactory(ResultCallAdapterFactory.create())
    .build()

Then you can return the Result class with the suspend keyword.

interface PokemonService {

  @GET("pokemon")
  suspend fun fetchPokemonList(
    @Query("limit") limit: Int = 20,
    @Query("offset") offset: Int = 0
  ): Result<PokemonResponse>
}

Finally, you will get the network response, which is wrapped by the Result class like the below:

viewModelScope.launch {
  val result = pokemonService.fetchPokemonList()
  if (result.isSuccess) {
    val data = result.getOrNull()
    // handle data
  } else {
    // handle error case
  }
}

Unit Tests by Injecting TestScope

You can also inject your custom CoroutineScope into the ResultCallAdapterFactory and execute network requests on the scope.

val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
val testScope = TestScope(testDispatcher)
val retrofit: Retrofit = Retrofit.Builder()
  .baseUrl("BASE_URL")
  .addConverterFactory(..)
  .addCallAdapterFactory(ResultCallAdapterFactory.create(testScope))
  .build()

Note: For more information about the Testing coroutines, check out the Testing Kotlin coroutines on Android.

Jetpack's Paging

This library allows you to return the paging source, which is parts of the Jetpack's Paging library.

Maven Central

Add the dependency below to your module's build.gradle file:

dependencies {
    implementation "com.github.skydoves:retrofit-adapters-paging:1.0.0"
}

PagingCallAdapterFactory

You can return Jetpack's PagingSource class to the Retrofit's service methods by setting PagingCallAdapterFactory like the below:

val retrofit: Retrofit = Retrofit.Builder()
    .baseUrl("BASE_URL")
    .addConverterFactory(..)
    .addCallAdapterFactory(PagingCallAdapterFactory.create())
    .build()

Then you can return the NetworkPagingSource class with the @PagingKeyConfig and @PagingKey annotations:

interface PokemonService {

  @GET("pokemon")
  @PagingKeyConfig(
    keySize = 20,
    mapper = PokemonPagingMapper::class
  )
  suspend fun fetchPokemonListAsPagingSource(
    @Query("limit") limit: Int = 20,
    @PagingKey @Query("offset") offset: Int = 0,
  ): NetworkPagingSource<PokemonResponse, Pokemon>
}

PagingKeyConfig and PagingKey

To return the NetworkPagingSource class, you must attach the @PagingKeyConfig and @PagingKey annotations to your Retrofit's service methods.

  • @PagingKeyConfig: Contains paging configurations for the network request and delivery them to the call adapter internally. You should set the keySize and mapper parameters.
  • @PagingKey: Marks the parameter in the service interface method as the paging key. This parameter will be paged by incrementing the page values continuously.

PagingMapper

You should create a paging mapper class, which extends the PagingMapper<T, R> interface like the below for transforming the original network response to the list of paging items. This class should be used in the @PagingKeyConfig annotation.

class PokemonPagingMapper : PagingMapper<PokemonResponse, Pokemon> {

  override fun map(value: PokemonResponse): List<Pokemon> {
    return value.results
  }
}

You will get the network response, which is wrapped by the NetworkPagingSource class like the below:

viewModelScope.launch {
  val pagingSource = pokemonService.fetchPokemonListAsPagingSource()
  val pagerFlow = Pager(PagingConfig(pageSize = 20)) { pagingSource }.flow
  stateFlow.emitAll(pagerFlow)
}

Finally, you should call the submitData method by your PagingDataAdapter to bind the paging data. If you want to learn more about the Jetpack's Paging, check out the Paging library.

Arrow's Either

This library allows you to model your Retrofit responses with arrow-kt's Either class.

Maven Central

Add the dependency below to your module's build.gradle file:

dependencies {
    implementation "com.github.skydoves:retrofit-adapters-arrow:1.0.0"
}

EitherCallAdapterFactory

You can return Arrow's Either class to the Retrofit's service methods by setting EitherCallAdapterFactory like the below:

val retrofit: Retrofit = Retrofit.Builder()
    .baseUrl("BASE_URL")
    .addConverterFactory(..)
    .addCallAdapterFactory(EitherCallAdapterFactory.create())
    .build()

Then you can return the Either class with the suspend keyword.

interface PokemonService {

  @GET("pokemon")
  suspend fun fetchPokemonListAsEither(
    @Query("limit") limit: Int = 20,
    @Query("offset") offset: Int = 0
  ): Either<Throwable, PokemonResponse>
}

Finally, you will get the network response, which is wrapped by the Either class like the below:

viewModelScope.launch {
  val either = pokemonService.fetchPokemonListAsEither()
  if (either.isRight()) {
    val data = either.orNull()
    // handle data
  } else {
    // handle error case
  }
}

Unit Tests by Injecting TestScope

You can also inject your custom CoroutineScope into the EitherCallAdapterFactory and execute network requests on the scope.

val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
val testScope = TestScope(testDispatcher)
val retrofit: Retrofit = Retrofit.Builder()
  .baseUrl("BASE_URL")
  .addConverterFactory(..)
  .addCallAdapterFactory(EitherCallAdapterFactory.create(testScope))
  .build()

Note: For more information about the Testing coroutines, check out the Testing Kotlin coroutines on Android.

Find this repository useful? ❀️

Support it by joining stargazers for this repository. ⭐
Also, follow me on GitHub for my next creations! 🀩

License

Designed and developed by 2022 skydoves (Jaewoong Eum)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
You might also like...
Kotlin Multiplatform project that gets network data from Food2Fork.ca
Kotlin Multiplatform project that gets network data from Food2Fork.ca

Food2Fork Recipe App This is the codebase for a Kotlin Multiplatform Mobile course. [Watch the course](https://codingwithmitch.com/courses/kotlin-mult

One-stop-shop for Social Network integrations

Roguin One stop shop for Social Network integrations Use the same code for Google, Facebook and Twitter What is Roguin Social Network integrations can

An example of a test task for creating a simple currency converter application for the Android platform. The app is developed using Kotlin, MVI, Dagger Hilt, Retrofit, Jetpack Compose.
An example of a test task for creating a simple currency converter application for the Android platform. The app is developed using Kotlin, MVI, Dagger Hilt, Retrofit, Jetpack Compose.

Simple Currency Converter Simple Currency Converter Android App by Isaev Semyon An example of a test task for creating a simple currency converter app

An android application for generating random quotes for learning Room, Jetpack Navigation and Lifecycles, Retrofit
An android application for generating random quotes for learning Room, Jetpack Navigation and Lifecycles, Retrofit

Random-Quote-Generator An android application for generating random quotes for learning Room, Jetpack Navigation and Lifecycles, Retrofit MAD Score Te

Includes jetpack compose, navigation, paging, hilt, retrofit, coil, coroutines, flow..
Includes jetpack compose, navigation, paging, hilt, retrofit, coil, coroutines, flow..

Nextflix-Composable A Clean Architecture App to show use of multi-module-architecture in a Jetpack Compose. The modules are as follow: app: Presentati

Android News Reader app. Kotlin Coroutines, Retrofit and Realm
Android News Reader app. Kotlin Coroutines, Retrofit and Realm

News Reader Android News Reader app Code that follows Packt Publishing Kotlin in Practice Video Course Example of Kotlin Coroutine usage, with Realm a

Architecture With MVI using Kotlin, Coroutines, Retrofit and Unit test
Architecture With MVI using Kotlin, Coroutines, Retrofit and Unit test

Architecture With MVI using Kotlin, Coroutines, Retrofit and Unit test MVI (Model-View-Intent) streamlines the process of creating and developing appl

UserLoc - A API call using Retrofit to obtain list of users details and show on UI in recycler view and google map
UserLoc - A API call using Retrofit to obtain list of users details and show on UI in recycler view and google map

UserLoc This uses a API call using Retrofit to obtain list of users details and

[Android-Kotlin] MVVM, ViewModel, LiveData, Observer, DataBinding, Repository, Retrofit, Dagger example

SimpleMvvmDaggerKotlin [Android-Kotlin] MVVM, ViewModel, LiveData, Observer, DataBinding, Repository, Retrofit, Dagger example [Image1 : User informat

Comments
  • List data not serialized

    List data not serialized

    Please complete the following information:

    • Library Version [e.g. v1.0.1]
    • Affected Device(s): Google Pixel 4

    Describe the Bug: Api response with list, not serialized return empty list

    Expected Behavior: response with list returned to presentation layer

    JSON

    {
      "status": "OK",
      "code": "200",
      "data:": [
        {
          "id": "1ddb439d-30e8-4898-bd6f-6959e3a829ef",
          "name": "Indonesia",
          "prefix": "+62",
          "flagImageUrl": "https://img.geonames.org/flags/x/id.gif"
        },
        {
          "id": "65ab068a-002b-4571-863c-d08e03579901",
          "name": "United States",
          "prefix": "+1",
          "flagImageUrl": "https://img.geonames.org/flags/x/us.gif"
        }
      ],
      "meta": {
        "count": 2,
        "limit": 10,
        "offset": 0
      }
    }
    

    Implementation

    Retrofit

                    Retrofit.Builder()
                        .baseUrl(Secured.getBaseUrlApi())
                        .client(okHttpClient)
                        .addCallAdapterFactory(NetworkResponseAdapterFactory())
                        .addCallAdapterFactory(CoroutineCallAdapterFactory())
                        .addConverterFactory(ScalarsConverterFactory.create())
                        .addConverterFactory(GsonConverterFactory.create(gson()))
                        .build()
    

    Api Client

        @GET("country-codes/v1")
        suspend fun getCountryCode(
            @Query("limit") limit: Int,
            @Query("offset") offset: Int,
        ): Result<ApiPaging<CountryCodeData?>?>
    
    data class ApiPaging<T>(
        @field:SerializedName("status") override val status: String? = null,
        @field:SerializedName("code") override val code: String? = null,
        @field:SerializedName("data") override val data: List<T?>? = null,
        @field:SerializedName("meta") override val meta: ApiPagingMeta? = null,
    ) : ApiResponseData<List<T?>?, ApiPagingMeta>
    
    data class CountryCodeData(
        @field:SerializedName("prefix") val prefix: String? = null,
        @field:SerializedName("name") val name: String? = null,
        @field:SerializedName("id") val id: String? = null,
        @field:SerializedName("flagImageUrl") val flagImageUrl: String? = null
    )
    

    Data Repo

        override suspend fun getCountryCode(
            payload: CountryCodePayload
        ): Result<List<CountryCodeData?>?> {
            return api.getCountryCode(payload.limit, payload.offset).mapSuspend {
                it?.data //this always empty
            }
        }
    
    opened by JosephSanjaya 3
  • Make Json instance configurable

    Make Json instance configurable

    Change

    Make Json instance configurable when deserializing JSON data

    Types of changes

    What types of changes does your code introduce?

    • [x] Enhancement (non-breaking change which adds functionality)
    opened by rayworks 2
  • Allows setting Unit type for the empty content (204, 205)

    Allows setting Unit type for the empty content (204, 205)

    Allows setting Unit type for the empty content (204. 205).

    @GET("/users/info")
    suspend fun updateUserInfo(userRequest: UserRequest): Result<Unit>
    
    opened by skydoves 0
  • Introduce a new retrofit-adapters-serialization module

    Introduce a new retrofit-adapters-serialization module

    Guidelines

    Introduce a new retrofit-adapters-serialization module.

    deserializeErrorBody allows you to deserialize the error body to your custom model.

    opened by skydoves 0
Releases(1.0.5)
  • 1.0.5(Dec 1, 2022)

    What's Changed

    • Bump Kotlin to 1.7.21, dokka to 1.7.20, and binary compatibility by @skydoves in https://github.com/skydoves/retrofit-adapters/pull/12
    • Bump Arrow-kt to 1.1.3 by @skydoves in https://github.com/skydoves/retrofit-adapters/pull/14

    Full Changelog: https://github.com/skydoves/retrofit-adapters/compare/1.0.4...1.0.5

    Source code(tar.gz)
    Source code(zip)
  • 1.0.4(Oct 3, 2022)

    What's Changed

    • Allows setting Unit type for the empty content (204, 205) by @skydoves in https://github.com/skydoves/retrofit-adapters/pull/10
    • Update AGP to 7.3.0 and Kotlin to 1.7.20 by @skydoves in https://github.com/skydoves/retrofit-adapters/pull/11

    Full Changelog: https://github.com/skydoves/retrofit-adapters/compare/1.0.3...1.0.4

    Source code(tar.gz)
    Source code(zip)
  • 1.0.3(Sep 15, 2022)

    What's Changed

    • Migrated arrow, result, serialization, and test modules to pure Kotlin package by @skydoves in https://github.com/skydoves/retrofit-adapters/pull/9

    Full Changelog: https://github.com/skydoves/retrofit-adapters/compare/1.0.2...1.0.3

    Source code(tar.gz)
    Source code(zip)
  • 1.0.2(Sep 1, 2022)

  • 1.0.1(Jul 21, 2022)

    πŸŽ‰ A new version 1.0.1 has been released! πŸŽ‰

    What's Changed

    • Implement Result extensions by @skydoves in https://github.com/skydoves/retrofit-adapters/pull/1
    • Implement Either Extensions by @skydoves in https://github.com/skydoves/retrofit-adapters/pull/2
    • Bump AGP, dependency versions and apply spotless by @skydoves in https://github.com/skydoves/retrofit-adapters/pull/3
    • Refactor exception handling samples by @skydoves in https://github.com/skydoves/retrofit-adapters/pull/4
    • Introduce a new retrofit-adapters-serialization module by @skydoves in https://github.com/skydoves/retrofit-adapters/pull/5
    • Refactor examples to use deserializeHttpError by @skydoves in https://github.com/skydoves/retrofit-adapters/pull/6

    New Contributors

    • @skydoves made their first contribution in https://github.com/skydoves/retrofit-adapters/pull/1

    Full Changelog: https://github.com/skydoves/retrofit-adapters/compare/1.0.0...1.0.1

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Jul 12, 2022)

Owner
Jaewoong Eum
Android Developer Advocate @GetStream πŸ₯‘ β€’ GDE for Android β€’ Open Source Software Engineer ❀️ β€’ Love coffee, music, magic tricks, and writing poems.
Jaewoong Eum
Arrow-Maven-Template - A template project for Arrow with Maven. It defines an application and a couple small examples

Arrow-Maven-Template A template project for Arrow on Maven. See the official doc

Ξ›RROW 0 Jan 11, 2022
Parallax scrolling either by offset or automatically.

ParallaxScrollingView Parallax Scrolling View. automatic scrolling with different speeds minimal integration gpu accelerated supports vector drawables

Jan Rabe 100 Nov 21, 2022
Functional Kotlin & Arrow based library for generating and verifying JWTs and JWSs

kJWT Functional Kotlin & Arrow based library for generating and verifying JWTs and JWSs. JWS JWT The following Algorithms are supported: HS256 HS384 H

Peter vR 31 Dec 25, 2022
A simple and easy adapter for RecyclerView. You don't have to make adapters and view holders anymore. Slush will help you.

ν•œκ΅­μ–΄ No more boilerplate adapters and view holders. Slush will make using RecyclerView easy and fast. The goal of this project is to make RecyclerView,

SeungHyun 26 Sep 13, 2022
Exercises for Functional Programming learning in Kotlin with Arrow

Exercises for Functional Programming in Kotlin with Arrow-kt Exercises and practice code for Functional Programming learning in Kotlin with Arrow Requ

Jack Lo 3 Nov 11, 2021
:balloon: A lightweight popup like tooltips, fully customizable with an arrow and animations.

Balloon ?? A lightweight popup like tooltips, fully customizable with arrow and animations. Including in your project Gradle Add below codes to your r

Jaewoong Eum 2.9k Jan 9, 2023
:balloon: A lightweight popup like tooltips, fully customizable with an arrow and animations.

Balloon ?? A lightweight popup like tooltips, fully customizable with arrow and animations. Including in your project Gradle Add below codes to your r

Jaewoong Eum 1.8k Apr 27, 2021
Arrow Endpoint offers a composable Endpoint datatype, that allows us easily define an Endpoint from which we can derive clients, servers & documentation.

Arrow Endpoint Arrow Endpoint offers a composable Endpoint datatype, that allows us easily define an Endpoint from which we can derive clients, server

Ξ›RROW 23 Dec 15, 2022
Arrow Endpoint offers a composable Endpoint datatype, that allows us easily define an Endpoint from which we can derive clients, servers & documentation.

Arrow Endpoint Arrow Endpoint offers a composable Endpoint datatype, that allows us easily define an Endpoint from which we can derive clients, server

Ξ›RROW 8 Oct 11, 2021
Android App Module - Activity Result Contracts

Activity Result Contract with async & await (Asynchronous) - Android App Module App Features Activity Result Contract : ActivityResultContracts Permis

Alvin Setiawan 1 Feb 1, 2022