Loadable is available at Maven Central, so make sure you add it to your project-level build.gradle
:
allprojects {
repositories {
mavenCentral()
}
}
Then, import the library into your module (replacing "<version>" by the latest version that's available):
dependencies {
implementation("com.jeanbarrossilva.loadable:loadable:<version>")
}
The main interface in this library, Loadable
, represents three different stages of asynchronously-loaded content.
Stage in which the content is being loaded and, therefore, is temporarily unavailable.
Stage in which the content has been successfully loaded. Holds the content itself.
Stage in which the content has failed to load and threw an error. Holds a
Throwable
.
Loadable relies heavily on Kotlin's
coroutines and
Flow
s,
offering functions and extensions that can, in turn, convert any (literally,
Any
) structure or, specifically,
Flow
s
into loadable data.
Suppose, for example, that we have an app in which we want to display a sequence of numbers fetched from the server, and also inform whether it's loading or if it has failed:
CoroutineScope(Dispatchers.IO).launch {
loadableFlow { // this: LoadableScope<Int>
// getNumbersFlow is a suspending network call that returns Flow<Int>.
getNumbersFlow()
// loadTo is a terminal operator that turns Flow<Int> into a Flow<Loadable<Int>> and emits
// all of its Loadables to the outer loadableFlow.
.loadTo(this)
}
.collect {
withContext(Dispatchers.Main) {
when (it) {
is Loadable.Loading -> display("Loading...")
is Loadable.Loaded -> display(it.content)
is Loadable.Failed -> display(it.error.message)
}
}
}
}
In this scenario, loadableFlow
was used purely because it has a lambda through which we can
collect other Flow
s
suspendingly and getNumbersFlow
is a suspending function. If that wasn't the case and we wanted to
just transform a Flow<T>
into a Flow<Loadable<T>>
, it could be accomplished by one of the
following:
val coroutineScope = CoroutineScope(Dispatchers.IO)
val numbersFlow = flowOf(1, 2, 3, 4)
// Returns Flow<Loadable<Int>>.
numbersFlow.loadable()
// Returns StateFlow<Loadable<Int>>, started and with its value shared in the given coroutine scope.
numbersFlow.loadable(coroutineScope)