https://kotlinlang.org logo
#coroutines
Title
# coroutines
c

Colton Idle

02/23/2023, 6:36 PM
I'm building an android app, and I'm following the "repository" pattern. Any suggestions on whether or not my functions should be suspend functions?
Copy code
class NetworkBackedBookRepository(
    private val api: ApiService,
) : BookRepository {
    override suspend fun getBook(id: String): Book? {
        val result = api.getBookById(id).execute()
        return result.body()?.firstOrNull()
    }
}
y

yschimke

02/23/2023, 6:55 PM
I'd always have suspend or returning flows (not suspending). What other options are you thinking of?
c

Casey Brooks

02/23/2023, 7:04 PM
In general, ViewModels are expected to expose non-suspending methods and launch coroutines internally, so they can be easily used from UI callbacks. In contrast, Repositories are typically used as a dependency of a ViewModel, and thus can be expected to be called from the suspending context of a ViewModel. My general pattern is to expose
Flow
for any data that needs to be read/queried, and to expose
suspend
methods for any changes that need to be made. Even for “one-shot” queries like an API call, I wrap the value in a loading-type monad (something like this) and have the query emit multiple states as it’s being loaded, to help structure the UI as fully reactive and drive things like progress indicators from the Repository
c

Colton Idle

02/23/2023, 7:06 PM
I guess I was thinking of just having
override fun getBook(id): Book?
but yeah what both Yuri and Casey says make sense. It just felt "wrong" but I think it only felt wrong because I know Android ViewModels are typically frowned upon to have suspending functions exposed.
thanks for the sanity check though. although ive been using coroutines/suspend/flows for a bit I still dont feel completely comforatble with them.
f

Francesc

02/23/2023, 11:40 PM
Android ViewModels are typically frowned upon to have suspending functions exposed.
that's because the idea is that the UI makes a call to the viewmodel which immediately puts a message into a queue to be processed asynchronously, the UI call is supposed to return immediately
Even for “one-shot” queries like an API call, I wrap the value in a loading-type monad (something like this) and have the query emit multiple states as it’s being loaded
I've seen this used, but I'm not one to use this pattern. I prefer my repositories to just return the data or an error, the viewmodel informs the UI when it's loading, rather than the repository telling the caller what steps it's going through, I find that this sort of leaks implementation details and, if you have a domain layer where you map repository models to domain models, you have quite a bit of boilerplate to handle the different possible states
z

zt

02/24/2023, 5:28 AM
In my service and repository I use suspend functions. Only difference is in my service I also do
withContext(<http://Dispatchers.IO|Dispatchers.IO>)
to keep it off the main thread
c

Colton Idle

02/24/2023, 3:26 PM
Yeah. that was kinda my next question to see if people inject dispatchers or what into their repository
y

yschimke

02/24/2023, 3:37 PM
If your repository is using some modern APIs like room or retrofit with suspending functions you won't need that.
Suspending functions should be safe to call from main thread
6 Views