{"id":13822047,"url":"https://github.com/skydoves/retrofit-adapters","last_synced_at":"2025-05-15T16:09:36.338Z","repository":{"id":44925428,"uuid":"513128468","full_name":"skydoves/retrofit-adapters","owner":"skydoves","description":"🚆 Retrofit call adapters for modeling network responses using Kotlin Result, Jetpack Paging3, and Arrow Either.","archived":false,"fork":false,"pushed_at":"2025-02-02T02:31:26.000Z","size":512,"stargazers_count":538,"open_issues_count":1,"forks_count":18,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-03-31T21:45:11.391Z","etag":null,"topics":["android","arrow-kt","coroutines","either","jetpack","kotlin","kotlin-result","network","paging3","retrofit","retrofit2"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/skydoves.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"skydoves","custom":["https://www.paypal.me/skydoves","https://www.buymeacoffee.com/skydoves"]}},"created_at":"2022-07-12T12:13:10.000Z","updated_at":"2025-03-18T08:29:19.000Z","dependencies_parsed_at":"2024-01-15T15:46:29.406Z","dependency_job_id":"0e99c37e-86a7-48bd-8b81-ab22a1ec0a08","html_url":"https://github.com/skydoves/retrofit-adapters","commit_stats":{"total_commits":79,"total_committers":3,"mean_commits":"26.333333333333332","dds":0.03797468354430378,"last_synced_commit":"f76a1450cf9bcba9dfb0c57b20539d9b7f966e37"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Fretrofit-adapters","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Fretrofit-adapters/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Fretrofit-adapters/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skydoves%2Fretrofit-adapters/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/skydoves","download_url":"https://codeload.github.com/skydoves/retrofit-adapters/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247737770,"owners_count":20987718,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","arrow-kt","coroutines","either","jetpack","kotlin","kotlin-result","network","paging3","retrofit","retrofit2"],"created_at":"2024-08-04T08:01:39.782Z","updated_at":"2025-04-07T22:06:20.697Z","avatar_url":"https://github.com/skydoves.png","language":"Kotlin","funding_links":["https://github.com/sponsors/skydoves","https://www.paypal.me/skydoves","https://www.buymeacoffee.com/skydoves"],"categories":["Kotlin"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eRetrofit Adapters\u003c/h1\u003e\u003c/br\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://opensource.org/licenses/Apache-2.0\"\u003e\u003cimg alt=\"License\" src=\"https://img.shields.io/badge/License-Apache%202.0-blue.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://android-arsenal.com/api?level=19\"\u003e\u003cimg alt=\"API\" src=\"https://img.shields.io/badge/API-19%2B-brightgreen.svg?style=flat\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/skydoves/retrofit-adapters/actions/workflows/android.yml\"\u003e\u003cimg alt=\"Build Status\" src=\"https://github.com/skydoves/retrofit-adapters/actions/workflows/android.yml/badge.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/skydoves\"\u003e\u003cimg alt=\"Profile\" src=\"https://skydoves.github.io/badges/skydoves.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://skydoves.github.io/libraries/retrofit-adapters/html/index.html\"\u003e\u003cimg alt=\"Dokka\" src=\"https://skydoves.github.io/badges/dokka-retrofit-adapters.svg\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n🚆 Retrofit adapters for modeling network responses with Kotlin Result, Jetpack Paging3, and Arrow Either.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/24237865/178486849-1dd506a6-79d8-4cc5-a986-56c69b3693cb.png\"/\u003e\n\u003c/p\u003e\n\n## Sandwich\nIf you're interested in a more specified and lightweight Monad sealed API library for modeling Retrofit responses and handling exceptions, check out [Sandwich](https://github.com/skydoves/sandwich).\n\n\u003cimg align=\"right\" width=\"90px\" src=\"https://user-images.githubusercontent.com/24237865/178630165-76855349-ac04-4474-8bcf-8eb5f8c41095.png\"/\u003e\n\n## Kotlin's Result\n\nThis library allows you to model your Retrofit responses with [Kotlin's Result](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/) class. \n\n[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/retrofit-adapters-result.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%22retrofit-adapters-result%22)\n\u003cbr\u003e\n\nAdd the dependency below to your **module**'s `build.gradle` file:\n\n```gradle\ndependencies {\n    implementation \"com.github.skydoves:retrofit-adapters-result:1.0.13\"\n}\n```\n\n### ResultCallAdapterFactory\n\nYou can return [Kotlin's Result](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/) class to the Retrofit's service methods by setting `ResultCallAdapterFactory` like the below:\n\n```kotlin\nval retrofit: Retrofit = Retrofit.Builder()\n    .baseUrl(\"BASE_URL\")\n    .addConverterFactory(..)\n    .addCallAdapterFactory(ResultCallAdapterFactory.create())\n    .build()\n```\n\nThen you can return the `Result` class with the suspend keyword.\n\n```kotlin\ninterface PokemonService {\n\n  @GET(\"pokemon\")\n  suspend fun fetchPokemonList(\n    @Query(\"limit\") limit: Int = 20,\n    @Query(\"offset\") offset: Int = 0\n  ): Result\u003cPokemonResponse\u003e\n}\n```\n\nFinally, you will get the network response, which is wrapped by the `Result` class like the below:\n\n```kotlin\nviewModelScope.launch {\n  val result = pokemonService.fetchPokemonList()\n  if (result.isSuccess) {\n    val data = result.getOrNull()\n    // handle data\n  } else {\n    // handle error case\n  }\n}\n```\n\n### Empty Content Response\n\nYou can confine the response type as Unit when you need to handle empty body (content) API requests like the below:\n\n```kotlin\n@POST(\"/users/info\")\nsuspend fun updateUserInfo(@Body userRequest: UserRequest): Result\u003cUnit\u003e\n```\n\n### Unit Tests by Injecting TestScope\n\nYou can also inject your custom `CoroutineScope` into the `ResultCallAdapterFactory` and execute network requests on the scope.\n\n```kotlin\nval testDispatcher: TestDispatcher = UnconfinedTestDispatcher()\nval testScope = TestScope(testDispatcher)\nval retrofit: Retrofit = Retrofit.Builder()\n  .baseUrl(\"BASE_URL\")\n  .addConverterFactory(..)\n  .addCallAdapterFactory(ResultCallAdapterFactory.create(testScope))\n  .build()\n```\n\n\u003e **Note**: For more information about the Testing coroutines, check out the [Testing Kotlin coroutines on Android](https://developer.android.com/kotlin/coroutines/test).\n\n\u003cimg align=\"right\" width=\"130px\" src=\"https://user-images.githubusercontent.com/24237865/178630375-bedd3be4-8d1e-4ba4-bf25-2640a16fcf6c.png\"/\u003e\n\n## Jetpack's Paging\n\nThis library allows you to return the paging source, which is parts of the Jetpack's [Paging library](https://developer.android.com/topic/libraries/architecture/paging/v3-overview).\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/retrofit-adapters-paging.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%22retrofit-adapters-paging%22)\n\u003cbr\u003e\n\nAdd the dependency below to your **module**'s `build.gradle` file:\n\n```gradle\ndependencies {\n    implementation \"com.github.skydoves:retrofit-adapters-paging:\u003cversion\u003e\"\n}\n```\n\n### PagingCallAdapterFactory\n\nYou can return Jetpack's [PagingSource](https://developer.android.com/reference/kotlin/androidx/paging/PagingSource) class to the Retrofit's service methods by setting `PagingCallAdapterFactory` like the below:\n\n```kotlin\nval retrofit: Retrofit = Retrofit.Builder()\n    .baseUrl(\"BASE_URL\")\n    .addConverterFactory(..)\n    .addCallAdapterFactory(PagingCallAdapterFactory.create())\n    .build()\n```\n\nThen you can return the `NetworkPagingSource` class with the `@PagingKeyConfig` and `@PagingKey` annotations:\n\n```kotlin\ninterface PokemonService {\n\n  @GET(\"pokemon\")\n  @PagingKeyConfig(\n    keySize = 20,\n    mapper = PokemonPagingMapper::class\n  )\n  suspend fun fetchPokemonListAsPagingSource(\n    @Query(\"limit\") limit: Int = 20,\n    @PagingKey @Query(\"offset\") offset: Int = 0,\n  ): NetworkPagingSource\u003cPokemonResponse, Pokemon\u003e\n}\n```\n\n### PagingKeyConfig and PagingKey\n\nTo return the `NetworkPagingSource` class, you must attach the `@PagingKeyConfig` and `@PagingKey` annotations to your Retrofit's service methods.\n\n- **@PagingKeyConfig**: Contains paging configurations for the network request and delivery them to the call adapter internally. You should set the `keySize` and `mapper` parameters.\n- **@PagingKey**: Marks the parameter in the service interface method as the paging key. This parameter will be paged by incrementing the page values continuously.\n\n## PagingMapper\n\nYou should create a paging mapper class, which extends the `PagingMapper\u003cT, R\u003e` 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.\n\n```kotlin\nclass PokemonPagingMapper : PagingMapper\u003cPokemonResponse, Pokemon\u003e {\n\n  override fun map(value: PokemonResponse): List\u003cPokemon\u003e {\n    return value.results\n  }\n}\n```\n\nYou will get the network response, which is wrapped by the `NetworkPagingSource` class like the below:\n\n```kotlin\nviewModelScope.launch {\n  val pagingSource = pokemonService.fetchPokemonListAsPagingSource()\n  val pagerFlow = Pager(PagingConfig(pageSize = 20)) { pagingSource }.flow\n  stateFlow.emitAll(pagerFlow)\n}\n```\n\nFinally, 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](https://developer.android.com/topic/libraries/architecture/paging/v3-overview). \n\n\u003cimg align=\"right\" width=\"110px\" src=\"https://user-images.githubusercontent.com/24237865/178630401-9d4472e0-3da2-4e94-8ff9-ee8d7d089df2.svg\"/\u003e\n\n## Arrow's Either\n\nThis library allows you to model your Retrofit responses with [arrow-kt](https://github.com/arrow-kt/arrow)'s [Either](https://arrow-kt.io/docs/apidocs/arrow-core/arrow.core/-either/) class. \n\n[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/retrofit-adapters-arrow.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%22retrofit-adapters-arrow%22)\n\u003cbr\u003e\n\nAdd the dependency below to your **module**'s `build.gradle` file:\n\n```gradle\ndependencies {\n    implementation \"com.github.skydoves:retrofit-adapters-arrow:\u003cversion\u003e\"\n}\n```\n\n### EitherCallAdapterFactory\n\nYou can return [Arrow's Either](https://arrow-kt.io/docs/apidocs/arrow-core/arrow.core/-either/) class to the Retrofit's service methods by setting `EitherCallAdapterFactory` like the below:\n\n```kotlin\nval retrofit: Retrofit = Retrofit.Builder()\n    .baseUrl(\"BASE_URL\")\n    .addConverterFactory(..)\n    .addCallAdapterFactory(EitherCallAdapterFactory.create())\n    .build()\n```\n\nThen you can return the `Either` class with the suspend keyword.\n\n```kotlin\ninterface PokemonService {\n\n  @GET(\"pokemon\")\n  suspend fun fetchPokemonListAsEither(\n    @Query(\"limit\") limit: Int = 20,\n    @Query(\"offset\") offset: Int = 0\n  ): Either\u003cThrowable, PokemonResponse\u003e\n}\n```\n\nFinally, you will get the network response, which is wrapped by the `Either` class like the below:\n\n```kotlin\nviewModelScope.launch {\n  val either = pokemonService.fetchPokemonListAsEither()\n  if (either.isRight()) {\n    val data = either.orNull()\n    // handle data\n  } else {\n    // handle error case\n  }\n}\n```\n\n### Empty Content Response\n\nYou can confine the response type as Unit when you need to handle empty body (content) API requests like the below:\n\n```kotlin\n@POST(\"/users/info\")\nsuspend fun updateUserInfo(@Body userRequest: UserRequest): Either\u003cThrowable, Unit\u003e\n```\n\n### Unit Tests by Injecting TestScope\n\nYou can also inject your custom `CoroutineScope` into the `EitherCallAdapterFactory` and execute network requests on the scope.\n\n```kotlin\nval testDispatcher: TestDispatcher = UnconfinedTestDispatcher()\nval testScope = TestScope(testDispatcher)\nval retrofit: Retrofit = Retrofit.Builder()\n  .baseUrl(\"BASE_URL\")\n  .addConverterFactory(..)\n  .addCallAdapterFactory(EitherCallAdapterFactory.create(testScope))\n  .build()\n```\n\n\u003e **Note**: For more information about the Testing coroutines, check out the [Testing Kotlin coroutines on Android](https://developer.android.com/kotlin/coroutines/test).\n\n\u003cimg align=\"right\" width=\"90px\" src=\"https://user-images.githubusercontent.com/24237865/178630165-76855349-ac04-4474-8bcf-8eb5f8c41095.png\"/\u003e\n\n## Kotlin Serialization\n\nThis library allows you to deserialize your error body of the Retrofit response as your custom error class with [Kotlin's Serialization](https://kotlinlang.org/docs/serialization.html).\n\n\u003e For more information about setting up the plugin and dependency, check out [Kotlin's Serialization](https://kotlinlang.org/docs/serialization.html).\n\n[![Maven Central](https://img.shields.io/maven-central/v/com.github.skydoves/retrofit-adapters-serialization.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22com.github.skydoves%22%20AND%20a:%22retrofit-adapters-serialization%22)\n\u003cbr\u003e\n\nAdd the dependency below to your **module**'s `build.gradle` file:\n\n```gradle\ndependencies {\n    implementation \"com.github.skydoves:retrofit-adapters-serialization:\u003cversion\u003e\"\n}\n```\n\n### Deserialize Error Body\n\nYou can deserialize your error body with the `deserializeHttpError` extension and your custom error class. First, define your custom error class following your RESTful API formats as seen in the below:\n\n```kotlin\n@Serializable\npublic data class ErrorMessage(\n  val code: Int,\n  val message: String\n)\n```\n\nNext, gets the result of the error class to the `throwable` instance with the `deserializeHttpError` extension like the below:\n\n```kotlin\nval result = pokemonService.fetchPokemonList()\nresult.onSuccessSuspend {\n  Timber.d(\"fetched as Result: $it\")\n}.onFailureSuspend { throwable -\u003e\n  val errorBody = throwable.deserializeHttpError\u003cErrorMessage\u003e()\n}\n```\n\n## Find this repository useful? :heart:\nSupport it by joining __[stargazers](https://github.com/skydoves/retrofit-adapters/stargazers)__ for this repository. :star: \u003cbr\u003e\nAlso, __[follow me](https://github.com/skydoves)__ on GitHub for my next creations! 🤩\n\n# License\n```xml\nDesigned and developed by 2022 skydoves (Jaewoong Eum)\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskydoves%2Fretrofit-adapters","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskydoves%2Fretrofit-adapters","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskydoves%2Fretrofit-adapters/lists"}