Data type library that describes loading, content and error states. To better understand the concept, read the following blog post.
Type | Loading | Content | Error |
---|---|---|---|
LCE<L, C, E> |
Loading | Content | Error |
UCE<C, E> |
Unit | Content | Error |
UCT<C> |
Unit | Content | Throwable |
CE<C, E> |
- | Content | Error |
CT<C> |
- | Content | Throwable |
LC<L, C> |
Loading | Content | - |
UC<C> |
Unit | Content | - |
LCE<L, C, E>
is the most flexible type which stands for Loading / Content / Error. It allows you to specify the generic parameter for each state.
val loading = LCE.loading("We are loading")
val content = LCE.content("Hello world")
val error = LCE.error(RuntimeException("boo"))
When we don't need a value for loading state, we can use UCE<C, E>
type which stands for Unit / Content / Error.
val loading = UCE.loading()
val content = UCE.content("Hello world")
val error = UCE.error("Something went wrong.")
When we use Throwable
for errors, we can use UCT<C>
which stands for Unit / Content / Throwable.
val loading = UCT.loading()
val content = UCT.content("Hello world")
val error = UCT.error(RuntimeException("boo"))
When we only care about content and error states, we can use CE<C, E>
type which stands for Content / Error.
val content = CE.content("Hello world")
val error = CE.error("My custom error message")
When we use Throwable
for errors, we can use CT<C>
type which stands for Content / Throwable.
val content = CT.content("Hello world")
val error = CT.error(RuntimeException("boo"))
When we only care about loading and content states, we can use UC<C>
which stands for Unit / Content.
val loading = UC.loading()
val content = UC.content("Hello world")
Each type has a fold extension method which allows us to unwrap the data type and handle all of the cases.
For example, this is useful when we want to update the UI based on the current state.
fun render(event: UCT<MyData>) {
event.fold(
onLoading = { view.showLoading() },
onError = { exception -> view.showError(exception) },
onContent = { data -> view.showData(data) }
)
}
This is also useful when we want to map to another type.
fun toMessage(event: CT<String>): String {
return event.fold(
onContent = { message -> message },
onError = { error -> "Something went wrong" }
)
}
You can map from one content type to another. You can also use this to transform the content
LCE.content("Hello").mapContent {
"$it World!"
}
LCE.content(0).mapContent { it + 1 }
val lce = LCE.error("failure").mapError { message -> RuntimeException(message) }
You can easily convert from one data type to another without incurring any memory allocation.
val uct = LCE.content("Hello World").asUCT()
val lce = uct.asLCE()
// When we go from LCE to CT/CE type, we treat loading state as null
val ct: CT<String>? = lce.asCT()
Converting a Single<T>
operation into Observable<UCT<T>>
fun fetchData(): Observable<UCT<MyData>> {
return Single.fromCallable { MyData() }.toUCT()
}
You'll need to add the lce-rxjava3
dependency to access these features.
Converting a Flow<T>
operation into Flow<UCT<T>>
fun fetchData(): Flow<UCT<MyData>> = flow {
emit(MyData())
}.toUCT()
You'll need to add the lce-coroutines
dependency to access these features.
Use lce-test
artifact in your tests to simplify making assertions on the LCE
types.
@Test fun `my test`() {
val event: LCE<String, String, Throwable> = ...
// Loading assertions
event.assertLoading()
event.assertLoading("loading value")
// Content assertions
event.assertContent()
event.assertContent("content value")
// Error assertions
event.assertError()
event.assertError(throwable)
}
Add the library to your list of dependencies:
dependencies {
implementation("com.laimiux.lce:lce:0.4.0")
// For RxJava 3 support
implementation("com.laimiux.lce:lce-rxjava3:0.4.0")
// For Coroutine/Flow support
implementation("com.laimiux.lce:lce-coroutines:0.4.0")
// Helper functions for assertions in tests
implementation("com.laimiux.lce:lce-test:0.4.0")
}
To run tests
./gradlew :lce:test
./gradlew :lce-rxjava3:test
./gradlew :lce-coroutines:test
Copyright 2021 Laimonas Turauskas
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.