Android-CleanArchitecture-Kotlin icon indicating copy to clipboard operation
Android-CleanArchitecture-Kotlin copied to clipboard

How should we pass new CoroutineContext to UseCase

Open miroslavign opened this issue 5 years ago • 3 comments

Or in general, what modifications should be implemented to support new CoroutineContext in UseCase and from calling side ?

miroslavign avatar Nov 25 '18 22:11 miroslavign

I'd assume all you need to do is make sure the Usecase function is suspend so that you can call it from any coroutine. Considering that ViewModel is typically the one who owns said CoroutineScope.

Zhuinden avatar Nov 26 '18 08:11 Zhuinden

Let me try it like this,

I'm ok with ViewMmodel being the owner, the question is should it be like:

    abstract class UseCase<out Type, in Params> where Type : Any {

    abstract suspend fun run(params: Params): Either<Failure, Type>

    operator fun invoke(params: Params, coroutineDispatcher: CoroutineDispatcher, job: Job,
                        onResult: (Either<Failure, Type>) -> Unit = {}) {
        val scope = CoroutineScope(coroutineDispatcher + job)
        scope.launch {
            onResult(run(params))
        }
    }

    class None
    }

and from viewModel send/provide::

    private val job = Job()
    override val coroutineContext: CoroutineContext
        get() = job + Dispatchers.Main

    getBooks.invoke(GetBooks.Params("e-book-fiction"), Dispatchers.IO, job) { it.either(::handleError, ::handleBooks) }

miroslavign avatar Nov 26 '18 11:11 miroslavign

@miroslavign I refactored the UseCase class like this:

abstract class UseCase<out Type, in Params> where Type : Any {

    abstract suspend fun run(params: Params): Result<Type>

    operator fun invoke(params: Params, job: Job, onResult: (Result<Type>) -> Unit = {}) {
        val backgroundJob = CoroutineScope(job + Dispatchers.IO).async { run(params) }
        CoroutineScope(job + Dispatchers.Main).launch { onResult(backgroundJob.await()) }
    }

    class None
}

I decided not to pass a CoroutineDispatcher as a method parameter. You can easily set a CoroutineScope based on a Job object, for example job + Dispatchers.Main. I'm not sure (I've not tested it yet) if this implementation would cancel the background task based on coroutine scope, but i cancel a job manually anyway inside OnCleared method of my base ViewModel in this way:

override fun onCleared() { job.cancel() super.onCleared() }

I also replaced Either with Result class (simpler implementation), but this solution should works for both cases.

patrykserek avatar May 11 '19 20:05 patrykserek