I am implementing the coroutine for first time. I am following MVP pattern for a simple login app. Here is my code flow –
The login button clicked will follow this direction –
LoginFragment -> LoginPresenter -> Repository -> APIRepository -> RetrofitInterface
The login response will follow this direction –
RetrofitInterface -> APIRepository -> Repository -> LoginPresenter -> LoginFragment
Here is the code –
RetrofitInterface.kt
@POST("login") fun loginAPI(@Body loginRequest: LoginRequest): Deferred<LoginResponse>?
Here is my Result.kt
sealed class Result<out T : Any> { class Success<out T : Any>(val data: T) : Result<T>() class Error(val exception: Throwable, val message: String = exception.localizedMessage) : Result<Nothing>() }
APIRepository.kt
override suspend fun loginAPICall(loginRequest: LoginRequest) : Result<LoginResponse>? { try { val loginResponse = apiInterface?.loginAPI(loginRequest)?.await() return Result.Success<LoginResponse>(loginResponse!!) } catch (e : HttpException) { return Result.Error(e) } catch (e : Throwable) { return Result.Error(e) } }
Repository.kt
override suspend fun loginUser(loginRequest: LoginRequest): Result<LoginResponse> { if (isInternetPresent(context)) { val result = apiRepositoryInterface?.loginAPICall(loginRequest) if (result is Result.Success<LoginResponse>) { val loginData = result.data cache?.storeData(loginData) } return result!! } else { return Result.Error(Exception()) } }
How do I launch a coroutine now in my presenter? I need to execute this API call on a background thread and publish the results on UI thread?
Advertisement
Answer
You need to launch a coroutine in a Presenter using local scope and injected CoroutineContext
to be able to change it, for example in Unit Tests:
class Presenter( private val repo: Repository, private val uiContext: CoroutineContext = Dispatchers.Main ) : CoroutineScope { // creating local scope private var job: Job = Job() // or SupervisorJob() - children of a supervisor job can fail independently of each other // To use Dispatchers.Main (CoroutineDispatcher - runs and schedules coroutines) // in Android add dependency: implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1' override val coroutineContext: CoroutineContext get() = uiContext + job fun detachView() { // cancel the job when view is detached job.cancel() } fun login(request: LoginRequest) = launch { // launching a coroutine val result = repo.loginUser(request) // calling 'loginUser' function will not block the Main Thread, it suspends the coroutine //use result, update UI when (result) { is Success<LoginResponse> -> { /* update UI when login success */ } is Error -> { /* update UI when login error */ } } } }