Joel Denke
04/26/2024, 4:10 AMcombineFlowResults (
flowA, flowB, flowC, flowD
) { a, b, c, d ->
UiStateOf(a, b, c, d)
}.onStart(Uistate.Loading)
ephemient
04/26/2024, 5:35 AMkotlin.Result
.ephemient
04/26/2024, 5:41 AMJoel Denke
04/26/2024, 6:56 AMephemient
04/26/2024, 7:17 AMsealed interface Result<out T> {
data class Success<out T>(val value: T) : Result<T>
data class NetworkError(val exception: IOException) : Result<Nothing>
// etc.
modeled after what actually happens in your application, not just some untyped Throwable
as kotlin.Result
does
- or use Arrow Either or some other alternative which also allows for modeling thatephemient
04/26/2024, 7:18 AMkotlin.Result
exists for the sake of some generic infrastructure like Kotlin coroutinesJoel Denke
04/26/2024, 7:20 AMephemient
04/26/2024, 7:20 AMcombine
overload will work fineephemient
04/26/2024, 7:21 AMJoel Denke
04/26/2024, 7:21 AMephemient
04/26/2024, 7:21 AMJoel Denke
04/26/2024, 7:21 AMJoel Denke
04/26/2024, 7:22 AMJoel Denke
04/26/2024, 7:24 AMYoussef Shoaib [MOD]
04/26/2024, 8:45 AMzip
from Arrow or the result
DSL from ArrowJoel Denke
04/26/2024, 10:12 AMYoussef Shoaib [MOD]
04/26/2024, 10:33 AMresult {
UiStateOf(resultA.bind(), resultB.bind(), resultC.bind(), resultD.bind())
} // Type: Result<UiState>
Joel Denke
04/26/2024, 11:24 AMYoussef Shoaib [MOD]
04/26/2024, 11:25 AMJoel Denke
04/26/2024, 6:15 PMsuspend fun fetchCombinedData(): UIState {
return try {
val deferredResult1 = async { NetworkRepository1.fetchData1() }
val deferredResult2 = async { NetworkRepository2.fetchData2() }
val deferredResult3 = async { NetworkRepository3.fetchData3() }
val result1 = deferredResult1.await()
val result2 = deferredResult2.await()
val result3 = deferredResult3.await()
UIState.Success(listOf(result1, result2, result3))
} catch (e: Exception) {
UIState.Error("Error: ${e.message}")
}
}
Each async here I added to illustrate my Flow. Usually return Flow from each async here and not often combine all at once. Open for change mental model of something better. I usually have suspend at api layer and Flow in repository layer.
And in my case in Compose want combine these results into one ui state data class, with loading, success and error state. Aggregate all at once.Youssef Shoaib [MOD]
04/26/2024, 7:24 PMFlow<Result<Blah>>
, I'll represent them here with asyncs:
suspend fun fetchCombinedData(): UIState = result {
val deferred1 = async { ... }
val deferred2 = async { ... }
val deferred3 = async { ... }
UIState.Success(listOf(deferred1.await().bind(), deferred2.await().bind(), deferred3.await().bind()))
}.getOrElse { UIState.Error("Error: ${e.message}") }
Youssef Shoaib [MOD]
04/26/2024, 7:27 PMflowA.combine(flowB, flowC, flowD) { a, b, c, d ->
result {
UiStateOf(a.bind(), b.bind(), c.bind(), d.bind())
}.getOrElse { UiState.Error(e.message) }
}
Joel Denke
04/26/2024, 7:27 PMJoel Denke
04/26/2024, 7:28 PMYoussef Shoaib [MOD]
04/26/2024, 7:28 PMresult
builder also does try-catch and collects any exceptions, but it also allows calling .bind()
on Result
valuesYoussef Shoaib [MOD]
04/26/2024, 7:28 PMJoel Denke
04/26/2024, 7:30 PMJoel Denke
04/26/2024, 7:31 PMJoel Denke
04/26/2024, 7:33 PMYoussef Shoaib [MOD]
04/26/2024, 7:33 PMDataLayerError
, you can have handling for those errors without having to make them exceptionsYoussef Shoaib [MOD]
04/26/2024, 7:35 PMcollect
or combine
your flows, call your suspend
funs normally, and await
your asyncs, and then call bind()
on any of them that might have a domain error.Joel Denke
04/26/2024, 7:37 PMJoel Denke
04/26/2024, 7:38 PMYoussef Shoaib [MOD]
04/26/2024, 7:39 PMRaise<E>
allows you to throw an error of type E, so you simply would have to take in multiple Raise
values for every instance you have. I'd suggest checking the Arrow docs btw, they go into good detail with examplesJoel Denke
04/26/2024, 7:41 PMJoel Denke
04/26/2024, 7:42 PMJoel Denke
04/27/2024, 6:57 AMYoussef Shoaib [MOD]
04/27/2024, 12:16 PM