Unable to create converter for class com.slack.eithernet.ApiResult when using Nothing with Moshi
egek92 opened this issue · 10 comments
@WorkerThread
suspend fun fetchPokemonList(
page: Int,
onSuccess: () -> Unit,
onError: (String) -> Unit
) = flow {
var pokemons = pokemonDao.getPokemonList(page)
if (pokemons.isEmpty()) {
when(val result = apiClient.fetchPokemonList(page = page)) {
is ApiResult.Success -> {
pokemons = result.response.results
pokemons.forEach { pokemon -> pokemon.page = page }
pokemonDao.insertPokemonList(pokemons)
emit(pokemons)
onSuccess()
}
is ApiResult.Failure -> onError("Error!")
}
} else {
emit(pokemons)
onSuccess()
}
}.flowOn(Dispatchers.IO)`
ApiClient.kt
suspend fun fetchPokemonList(
page: Int
) = apiService.fetchPokemonList(
limit = PAGING_SIZE,
offset = page * PAGING_SIZE
)
ApiService.kt
@GET("pokemon")
suspend fun fetchPokemonList(
@Query("limit") limit: Int = 20,
@Query("offset") offset: Int = 0
): ApiResult<PokemonResponse, Nothing>
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.client(okHttpClient)
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(MoshiConverterFactory.create())
.addConverterFactory(ApiResultConverterFactory)
.addCallAdapterFactory(ApiResultCallAdapterFactory)
.build()
}
@Provides
@Singleton
fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
@Provides
@Singleton
fun provideApiClient(apiService: ApiService): ApiClient {
return ApiClient(apiService)
}`
Am I missing something?
What's the stacktrace?
2020-11-19 19:41:33.965 12628-12628/com.ege.test E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.ege.test, PID: 12628
java.lang.IllegalArgumentException: Unable to create converter for class com.slack.eithernet.ApiResult
for method ApiService.fetchPokemonList
at retrofit2.Utils.methodError(Utils.java:54)
at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:126)
at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:85)
at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:39)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:202)
at retrofit2.Retrofit$1.invoke(Retrofit.java:160)
at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
at $Proxy3.fetchPokemonList(Unknown Source)
at com.ege.exercise.network.ApiClient.fetchPokemonList(ApiClient.kt:15)
at com.ege.exercise.repository.MainRepository$fetchPokemonList$2.invokeSuspend(MainRepository.kt:29)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Caused by: java.lang.IllegalArgumentException: Cannot serialize abstract class com.slack.eithernet.ApiResult
at com.squareup.moshi.ClassJsonAdapter$1.create(ClassJsonAdapter.java:80)
at com.squareup.moshi.Moshi.adapter(Moshi.java:138)
at com.squareup.moshi.Moshi.adapter(Moshi.java:98)
at retrofit2.converter.moshi.MoshiConverterFactory.responseBodyConverter(MoshiConverterFactory.java:89)
at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:362)
at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:345)
at retrofit2.HttpServiceMethod.createResponseConverter(HttpServiceMethod.java:124)
at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:85)
at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:39)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:202)
at retrofit2.Retrofit$1.invoke(Retrofit.java:160)
at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
at $Proxy3.fetchPokemonList(Unknown Source)
at com.ege.exercise.network.ApiClient.fetchPokemonList(ApiClient.kt:15)
at com.ege.exercise.repository.MainRepository$fetchPokemonList$2.invokeSuspend(MainRepository.kt:29)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
@ZacSweers Moshi is trying to convert Nothing
in which case we need to use either Unit or an Error response like:
sealed class ErrorResponse : Parcelable {
@Parcelize
object Unknown : ErrorResponse()
@Parcelize
object FetchPokemonsFailed : ErrorResponse()
@Parcelize
object FetchPokemonInfoFailed : ErrorResponse()
}
but idea behind this library is reducing boilerplate code so maybe we can do an improvement?
What happens if you move ApiResultConverterFactory
above the moshi converter?
same issue
I can easily bypass this by using an Empty sealed class like sealed class ErrorResponse
instead of Nothing
, not sure if there will be any consequences tho.
I need to look more. EitherNet should be using Nothing as a "ignore me nothing to see here" type, similar to how Void would be used in java
Apologies for the radio silence. I'm going to try to address this before 1.0
The right solution in this case is to use Unit
Can EitherNet throw a nicer error suggesting developers to use Unit
instead of Nothing
?
Sure! Wanna send a PR?