Which one is better? 1. Call retrofit from viewMod...
# coroutines
j
Which one is better? 1. Call retrofit from viewModel than map data using ViewModelScope, knowing that retrofit create a background task 2. Uses withContext inside ViewModelScope to do all on Dispatchers.IO?
p
I normally encapsulate that operation in a UseCase
Copy code
Class UseCase<CustomResult<T>> {
  suspend execute():CustomResult<T>
}
Then consume the use case from the VM like in your point 2
1
a
I preferer to have Repository or UseCase level to do context switch to IO and mapping to domain data model. Then ViewModel doesn't know about data source and works on UI thread. ViewModel could do additional data transformation to UI specific data classes but they are usually lightweight enough to do on UI thread relying on some implicit assumptions that some code will run on non-UI thread is not cool, I think.
2
j
I sow one day a video on Google q/a that they're talking about not changing context in Network/database calls. They tell is lighter do on ui thread with coroutines than changing context than back to ui.
a
it's easier for me to wrap Repo suspend methods into withContext(IO) and do all data transforms there and forget about it.
p
It is lighter for sure, no context switching but as Andrii mentioned you would have to trust the library doing the right job.
j
I see. In case of map data to model could require CPU work, it's better to change context on data layer
p
That also counts
a
I do agree with @andriyo, switching coroutines context on data-layer it’s also helpful when it comes to tests.
j
Retrofit will use IO (or Default) anyway independently you switch the context or not.
a
but if tomorrow someone replaces Retrofit with Ktor in your service talking code, that won't longer be the case. it's better to have explicit boundaries just be safe.
j
Ktor uses IO or Default too. It should be weird a network library doesn’t use a dispatcher under the hood
a
why not to allow the caller to choose in what context to run network library code? it gives flexibility to run it on whatever
j
The convention from coroutines is that the library author knows which is the correct implementation detail.
2
a
I understand where you're coming from and it's true that Retrofit changes execution context for its APIs ( and other data access libraries like Room for example do that too), but Ktor Client doesn't. And I don't think coroutines as such dictate in any way whether libraries should give control over execution context to clients or not. It's on library authors to design however they please.
j
Well, it is explained in the coroutines docs. Of course authors can do whatever they want later
About ktor, are you sure about that? I would bet it provides a dispatcher.
a quick look at ktor show they are providing a dispatcher in its Darwin client implementation
Looks like it is using
IO
as default and it is overridden in some platforms like Darwin. https://github.com/ktorio/ktor/blob/main/ktor-client/ktor-client-core/common/src/io/ktor/client/engine/HttpClientEngineBase.kt
a
I'm pretty sure I needed to wrap it in withContext (Io)for Android, I'll check
a
I use OkHttp for my android client and it ANRs if I don't wrap Ktor client code in withContext(IO) on my side
j
OkHttp client is using a different implementation
I would report it tbh
a
it's cool since it give client freedom to provide whatever dispatcher they want, at least for OkHttp engine ) but my point stands - importance of defensive use of withContext() since it's not consistent even within a single framework whether it's going to be run on UI thread or not
1
m
retrofit just uses its own dispatcher. I just call the interface on the main thread dispatcher, and map it also in the main (if the map is fast). Otherwise i just do the mapping on IO. I never use IO to call retrofit stuff. I don’t see any point since it will use its own dispatcher ?
a
no need to retrofit especially if it's explicitly stated in their API documentation that they switch context for you