{"id":13605674,"url":"https://github.com/GetStream/stream-result","last_synced_at":"2025-04-12T05:34:08.697Z","repository":{"id":150155919,"uuid":"621728952","full_name":"GetStream/stream-result","owner":"GetStream","description":"🚊 Railway-oriented library to easily model and handle success/failure for Kotlin, Android, and Retrofit.","archived":false,"fork":false,"pushed_at":"2024-05-28T17:55:02.000Z","size":554,"stargazers_count":83,"open_issues_count":12,"forks_count":0,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-05-29T03:47:41.309Z","etag":null,"topics":["android","coroutines","functional-programming","kotlin","kotlin-result","railway-oriented-programming","result","retrofit2"],"latest_commit_sha":null,"homepage":"https://getstream.github.io/stream-result/","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/GetStream.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-31T09:02:34.000Z","updated_at":"2024-06-07T02:26:29.945Z","dependencies_parsed_at":"2024-06-07T02:26:28.095Z","dependency_job_id":"8868f0f9-f57e-4f9a-9dcc-a25a40454c38","html_url":"https://github.com/GetStream/stream-result","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GetStream%2Fstream-result","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GetStream%2Fstream-result/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GetStream%2Fstream-result/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GetStream%2Fstream-result/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GetStream","download_url":"https://codeload.github.com/GetStream/stream-result/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223497982,"owners_count":17155235,"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","coroutines","functional-programming","kotlin","kotlin-result","railway-oriented-programming","result","retrofit2"],"created_at":"2024-08-01T19:01:01.441Z","updated_at":"2024-11-07T10:31:03.044Z","avatar_url":"https://github.com/GetStream.png","language":"Kotlin","funding_links":[],"categories":["Kotlin"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eStream Result\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=21\"\u003e\u003cimg alt=\"API\" src=\"https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/GetStream/stream-result/actions/workflows/android.yml\"\u003e\u003cimg alt=\"Build Status\" src=\"https://github.com/GetStream/stream-result/actions/workflows/android.yml/badge.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://us12.campaign-archive.com/?u=f39692e245b94f7fb693b6d82\u0026id=78f6f6dd04\"\u003e\u003cimg alt=\"Kotlin Weekly\" src=\"https://skydoves.github.io/badges/kotlin-weekly.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://androidweekly.net/issues/issue-565\"\u003e\u003cimg alt=\"Android Weekly\" src=\"https://skydoves.github.io/badges/android-weekly.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://getstream.io?utm_source=Github\u0026utm_medium=Github_Repo_Content_Ad\u0026utm_content=Developer\u0026utm_campaign=Github_Dec2022_StreamLog\u0026utm_term=DevRelOss\"\u003e\u003cimg src=\"https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/HayesGordon/e7f3c4587859c17f3e593fd3ff5b13f4/raw/11d9d9385c9f34374ede25f6471dc743b977a914/badge.json\" alt=\"Stream Feeds\"\u003e\u003c/a\u003e\n\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n🚊 Railway-oriented library to model and handle success/failure easily for Kotlin Multiplatform (Android, iOS, MacOS), and Retrofit for Android.\n\u003c/p\u003e\n\n## What's Railway-Oriented Programming?\n\nRailway Oriented Programming is a functional approach to handling success and errors in normalized ways, always allowing you to predict the result. This library helps you to implement Railway-Oriented models and functions in Kotlin and Android (especially with [Retrofit](https://github.com/square/retrofit)). Read [Railway Oriented Programming](https://fsharpforfunandprofit.com/rop/) if you want to learn more about ROP.\n\n\u003cp\u003e\n  \u003ca href=\"https://getstream.io/chat/sdk/android/\"\u003e\u003cimg alt=\"Logo\" src=\"https://user-images.githubusercontent.com/24237865/229043283-3584b713-42a4-4491-a26c-a06b68b57f0d.jpg\"/\u003e\u003c/a\u003e \u003cbr\u003e\n\u003c/p\u003e\n\n\u003ca href=\"https://getstream.io/chat/sdk/compose?utm_source=Github\u0026utm_medium=Github_Repo_Content_Ad\u0026utm_content=Developer\u0026utm_campaign=Github_Dec2022_Jaewoong_ChatGPT\u0026utm_term=DevRelOss\"\u003e\n\u003cimg src=\"https://user-images.githubusercontent.com/24237865/138428440-b92e5fb7-89f8-41aa-96b1-71a5486c5849.png\" align=\"right\" width=\"12%\"/\u003e\n\u003c/a\u003e\n\n## Use Cases\n\nYou'll find the use cases in the repositories below:\n- [Stream Chat SDK for Android](https://github.com/getStream/stream-chat-android): 💬 Android Chat SDK ➜ Stream Chat API. UI component libraries for chat apps. Kotlin \u0026 Jetpack Compose messaging SDK for Android chat.\n- [Stream Video SDK for Android](https://github.com/getStream/stream-video-android): 📲 Android Video SDK. Stream's versatile Core + Compose UI component libraries that allow you to build video calling, audio room, and, live streaming apps based on Webrtc running on Stream's global edge network.\n\n\u003cimg align=\"right\" width=\"90px\" src=\"https://user-images.githubusercontent.com/24237865/178630165-76855349-ac04-4474-8bcf-8eb5f8c41095.png\"/\u003e\n\n## Stream Result\n\nThis library provides a normalized result model, `Result`, representing the success or error of any business work.\n\n[![Maven Central](https://img.shields.io/maven-central/v/io.getstream/stream-result.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.getstream%22%20AND%20a:%22stream-result%22)\n\u003cbr\u003e\n\nAdd the dependency below to your **module**'s `build.gradle` file:\n\n```gradle\ndependencies {\n    implementation(\"io.getstream:stream-result:$version\")\n}\n```\n\nIf you're targeting on Kotlin Multiplatform, add the dependency below to your module's `build.gradle.kts` file:\n\n```kotlin\nsourceSets {\n    val commonMain by getting {\n        dependencies {\n            implementation(\"io.getstream:stream-result:$version\")\n        }\n    }\n}\n```\n\n## Result\n\nThis is a basic model to represent a normalized result from business work. This looks similar to [Kotlin's Result](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/), but Stream Result was designed to include more information about success and error and support more convenient functionalities to handle results. Result is basically consist of two detailed types below:\n\n- **Result.Success**: This represents your business's successful result, including a `value` property, a generic type of Result.\n- **Result.Failure**: This represents the failed result of your business result and includes a `value` property, the `Error` type.\n\nYou can simply create each instance of `Result` like the example below:\n\n```kotlin\nval result0: Result\u003cString\u003e = Result.Success(value = \"result\")\n\nval result1: Result\u003cString\u003e = Result.Failure(\n  value = Error.GenericError(message = \"failure\")\n)\n\nval result = result0 then { result1 }\nresult.onSuccess {\n  ..\n}.onError {\n  ..\n}\n```\n\n## Error\n\n`Result.Failure` has `Error` as a value property, which contains error details of your business work. Basically, `Error` consists of three different types of errors below:\n\n- **Error.GenericError**: Represents a normal type of error and only contains an error message.\n- **Error.ThrowableError**: Represents an exceptional type of error and contains a message and cause information.\n- **Error.NetworkError**: Represents a network error and contains status code, message, and cause information.\n\nYou can create each instance like the example below:\n\n```kotlin\nval error: Error = Error.GenericError(message = \"error\")\n\ntry {\n     .. \n} catch (e: Exception) {\n  val error: Error = Error.ThrowableError(\n    message = e.localizedMessage ?: e.stackTraceToString(), \n    cause = e\n  )\n}\n\nval error: Error = Error.NetworkError(\n  message = \"error\",\n  serverErrorCode = code,\n  statusCode = statusCode\n)\n```\n\n## Result Extensions\n\n**Stream Result** library useful extensions below to effectively achieve Railway Oriented Programming in Kotlin: \n\n### Result.then\n\nComposition the `Result` with a given `Result` from a lambda function.\n\n```kotlin\nval result0: Result\u003cString\u003e = Result.Success(value = \"result0\")\nval result1: Result\u003cInt\u003e = Result.Success(value = 123)\nval result = result0 then { result1 }\nresult.onSuccess { intValue -\u003e .. }\n```\n\n### Result.map, Result.mapSuspend\n\nReturns a transformed `Result` of applying the given function if the `Result` contains a successful data payload.\n\n```kotlin\nval result: Result\u003cString\u003e = Result.Success(value = \"result\")\nval mappedResult = result.map { 123 }\nmappedResult.onSuccess { intValue -\u003e }\n```\n\n### Result.flatMap, Result.flatMapSuspend\n\nReturns a transformed `Result` from results of the function if the `Result` contains a successful data payload. Returns an original `Result` if the `Result` contains an error payload.\n\n```kotlin\nval result: Result\u003cString\u003e = Result.Success(value = \"result\")\nval mappedResult = result.flatMap { Result.Success(value = 123) }\nmappedResult.onSuccess { intValue -\u003e }\n```\n\n\u003cimg align=\"right\" width=\"140px\" src=\"https://user-images.githubusercontent.com/24237865/205479526-5fa0b5f0-22df-4f02-ac0e-7a7a3e050cdb.png\"/\u003e\n\n## Stream Result Call Retrofit\n\n**Stream Result** library provides retrofit call integration functionalities to help you to construct a `Result` model easily from the network requests on Android with the same approaches of Railway Oriented Programming.\n\n[![Maven Central](https://img.shields.io/maven-central/v/io.getstream/stream-result.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22io.getstream%22%20AND%20a:%22stream-result%22)\n\nAdd the dependency below into your **module**'s `build.gradle` file:\n\n```gradle\ndependencies {\n    implementation(\"io.getstream:stream-result-call-retrofit:$version\")\n}\n```\n\n### RetrofitCallAdapterFactory and RetrofitCall\n\nYou can return `RetrofitCall` class as a return type on your Retrofit services by adding `RetrofitCallAdapterFactory` on your `Retrofit.Builder` like the example below:\n\n```kotlin\nval retrofit: Retrofit = Retrofit.Builder()\n  .baseUrl(..)\n  .addCallAdapterFactory(RetrofitCallAdapterFactory.create())\n  .build()\n\nval posterService: PosterService = retrofit.create()\n\ninterface PosterService {\n\n  @GET(\"DisneyPosters2.json\")\n  fun fetchPosterList(): RetrofitCall\u003cList\u003cPoster\u003e\u003e\n}\n```\n\n`RetrofitCall` class allows you to execute network requests easily like the example below:\n\n```kotlin\ninterface PosterService {\n\n  @GET(\"DisneyPosters2.json\")\n  fun fetchPosterList(): RetrofitCall\u003cList\u003cPoster\u003e\u003e\n}\n\nval posterService: PosterService = retrofit.create()\n\n// Execute a network request asynchronously with a given callback.\nposterService.fetchPosterList().enqueue { posters -\u003e\n  ..\n}\n\n// Execute a network request in a coroutine scope.\n// If you use coroutines, we'd recommend you to use this way.\nviewModelScope.launch {\n  val result = posterService.fetchPosterList().await()\n  result.onSuccess {\n    ..\n  }.onError {\n    ..\n  }\n}\n```\n\n### RetrofitCall Extensions\n\n`RetrofitCall` provides useful extensions for sequential works following Railway Oriented Programming approaches.\n\n#### RetrofitCall.doOnStart\n\nRun the given `function` before running a network request.\n\n```kotlin\nval result = posterService.fetchPosterList()\n  .doOnStart(viewModelScope) {\n    // do something..\n  }.await()\n```\n\n#### RetrofitCall.doOnResult\n\nRun the given `function` before running a network request.\n\n```kotlin\nval result = posterService.fetchPosterList()\n  .doOnStart(viewModelScope) {\n    // do something before running the call..\n  }\n  .doOnResult(viewModelScope) {\n    // do something after running the call..\n  }.await()\n```\n\n#### RetrofitCall.map\n\nMaps a `Call` type to a transformed `Call`.\n\n```kotlin\nval result = posterService.fetchPosterList()\n  .map { it.first() }\n  .await()\n```\n\nSo you can chain all the extensions sequentially like the example below:\n\n```kotlin\nval result = posterService.fetchPosterList()\n  // retry if the network request fails.\n  .retry(viewModelScope, retryPolicy)\n  // do something before running the network request.\n  .doOnStart(viewModelScope) {\n    // do something..\n  }\n  // do something after running the network request.\n  .doOnResult(viewModelScope) {\n    // do something..\n  }\n  // map the type of call.\n  .map { it.first() }\n  .await()\n\nresult.onSuccess {\n  // do something..\n}.onError {\n  // do something..\n}\n```\n\n#### RetrofitCall.retry\n\nRetry a network request following your `RetryPolicy`.\n\n```kotlin\nprivate val retryPolicy = object : RetryPolicy {\n  override fun shouldRetry(attempt: Int, error: Error): Boolean = attempt \u003c= 3\n\n  override fun retryTimeout(attempt: Int, error: Error): Int = 3000\n}\n\n\nval result = posterService.fetchPosterList()\n  // retry if the network request fails.\n  .retry(viewModelScope, retryPolicy)\n  .await()\n```\n\n### Custom Error Parser\n\nYou can customize the creating of `Error` from an error response according to your backend service by implementing your `ErrorParser` class. You can provide your custom `ErrorParser` to `RetrofitCallAdapterFactory`. If not, it will use a default `ErrorParser`, which uses [Kotlin Serialization](https://kotlinlang.org/docs/serialization.html) to decode json formats.\n\n```kotlin\ninternal class MyErrorParser : ErrorParser\u003cDefaultErrorResponse\u003e {\n\n  @Suppress(\"UNCHECKED_CAST\")\n  override fun \u003cT : Any\u003e fromJson(raw: String): T {\n    // use moshi or something that you can serialize from json response.\n  }\n\n  override fun toError(okHttpResponse: Response): Error {\n    // build Error with a given okHttpResponse.\n  }\n\n  override fun toError(errorResponseBody: ResponseBody): Error {\n    // build Error with a given errorResponseBody.\n  }\n}\n\nval retrofit: Retrofit = Retrofit.Builder()\n  .baseUrl(..)\n  .addConverterFactory(MoshiConverterFactory.create())\n  .addCallAdapterFactory(RetrofitCallAdapterFactory.create(\n    errorParser = MyErrorParser()\n  ))\n  .build()\n\n```\n\n\u003ca href=\"https://getstream.io/chat/compose/tutorial/?utm_source=Github\u0026utm_campaign=Devrel_oss\u0026utm_medium=StreamResult\"\u003e\u003cimg src=\"https://user-images.githubusercontent.com/24237865/146505581-a79e8f7d-6eda-4611-b41a-d60f0189e7d4.jpeg\" align=\"right\" /\u003e\u003c/a\u003e\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, __[contributors](https://github.com/skydoves)__ on GitHub for my next creations! 🤩\n\n# License\n```xml\nCopyright 2023 Stream.IO, Inc. All Rights Reserved.\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%2FGetStream%2Fstream-result","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGetStream%2Fstream-result","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGetStream%2Fstream-result/lists"}