Noé Casas
03/18/2021, 10:37 AMResult
affecting 1.4.31, which is needed by compose beta:
• https://youtrack.jetbrains.com/issue/KT-44867
• https://youtrack.jetbrains.com/issue/KT-45259
These problems result in a ClassCastException
and the subsequent app crash.
Is there any workaround? How are people using compose dealing with this?Timo Drick
03/18/2021, 12:13 PMNoé Casas
03/18/2021, 12:21 PMclass GreetingService {
fun getGreeting(callback: (Result<String>) -> Unit) {
callback(Result.success("Hi there"))
}
}
@Composable
fun Greeting(greetingService: GreetingService) {
var greeting: String by remember { mutableStateOf("") }
DisposableEffect(Unit) {
greetingService.getGreeting { result ->
result.fold(
onSuccess = { greeting = it },
onFailure = { greeting = "NOOO" }
)
}
onDispose { }
}
Text(text = greeting)
}
This results in a java.lang.ClassCastException: java.lang.String cannot be cast to kotlin.Result
and the subsequent app crash.Timo Drick
03/18/2021, 12:27 PMTimo Drick
03/18/2021, 12:31 PMsealed class MyResult<out T> {
data class Success<T>(val data: T): MyResult<T>()
class Error(val error: Throwable): MyResult<Nothing>()
}
class GreetingService {
fun getGreeting(callback: (MyResult<String>) -> Unit) {
callback(MyResult.Success("Hi there"))
}
}
@Composable
fun Greeting(greetingService: GreetingService) {
var greeting: String by remember { mutableStateOf("") }
DisposableEffect(Unit) {
greetingService.getGreeting { result ->
when (result) {
is MyResult.Success -> { greeting = result.data }
is MyResult.Error -> { greeting = "NOOO" }
}
}
onDispose { }
}
Text(text = greeting)
}
Noé Casas
03/18/2021, 12:33 PMResult
, one option is to implement their own Result
to avoid the bug. However, given that Result
is a standard Kotlin thing, I was wondering if no one else had bumped into this bug and if there wasn’t a “known fix”, given than the version of the Kotlin gradle plugin required by compose breaks a standard Kotlin mechanism.Timo Drick
03/18/2021, 12:34 PMNoé Casas
03/18/2021, 12:37 PMResult
is from the Kotlin standard library: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/Timo Drick
03/18/2021, 12:39 PMNoé Casas
03/18/2021, 12:40 PMResult
until the bugs are fixed.Timo Drick
03/18/2021, 12:41 PMTimo Drick
03/18/2021, 12:41 PMsealed class LoadingState<out T: Any> {
object Loading : LoadingState<Nothing>()
class Error(val error: Throwable): LoadingState<Nothing>()
class Success<T: Any>(val data: T): LoadingState<T>()
}
Timo Drick
03/18/2021, 12:48 PMsealed class LoadingState<out T: Any> {
object Loading: LoadingState<Nothing>()
class Error(val error: Throwable): LoadingState<Nothing>()
class Success<T: Any>(val data: T): LoadingState<T>()
}
@Composable
fun <T: Any> loadingStateFor(vararg inputs: Any?,
loadingBlock: suspend CoroutineScope.() -> T): LoadingState<T> {
var state by remember(*inputs) { mutableStateOf<LoadingState<T>>(LoadingState.Loading) }
LaunchedEffect(*inputs) {
state = try {
LoadingState.Success(loadingBlock())
} catch (err: Exception) {
LoadingState.Error(err)
}
}
return state
}
data class CustomData(val test: String)
suspend fun dummyLoader() = withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
delay(1000)
CustomData("Loaded data")
}
@Composable
fun exampleUsage() {
var retryCounter by remember { mutableStateOf(0) }
val imageState = loadingStateFor(retryCounter) {
dummyLoader()
}
when (val state = imageState) {
is LoadingState.Loading -> LoadingBox()
is LoadingState.Success -> YourDataScreen(state.data)
is LoadingState.Error -> ErrorBox(message = state.error.message, onRetry = { retryCounter++ })
}
}
@Composable
fun YourDataScreen(data: CustomData) {
//....
}
Noé Casas
03/18/2021, 12:54 PMTimo Drick
03/18/2021, 12:57 PMtylerwilson
03/18/2021, 1:22 PM