Lilly
05/12/2021, 4:54 PMsolidogen
05/12/2021, 6:23 PMLilly
05/12/2021, 11:38 PMAbhishek Dewan
05/18/2021, 2:08 AMAbhishek Dewan
05/18/2021, 2:11 AMdewildte
05/19/2021, 1:36 PMLilly
06/02/2021, 12:53 AMGenerally speaking if you feel the need to add other presenters as dependencies then you probably should be extracting logic into repositoriesThis is a good point @Abhishek Dewan @dewildte I'm using jetpack compose but I will use the terms activity and fragment. I have an activity which acts like a container, holding multiple fragments. The activity shows the views that will persist in every fragment, e.g. bottom navigation bar and top app bar. For each fragment (every fragment has its own presenter): when entering the fragment I call a content-specific operation (fetch data from repo). The flow goes like: presenter->use case->repo->data source. But every operation has a precondition, e.g. "bluetooth device has to be connected". Problems begin if I handle the connection as separate use case. I have 3 options: 1. hide connection logic in shared presenter 2. hide connection logic in shared use case 3. Don't handle connection logic as use case. Handle it in data access class. This is what Abhishek is suggesting. My current favorite is 3. It took a long time to accept that connection logic don't have to be a separate use case. In the end it's a question how granular you wanna be with use cases. What do you think?
Abhishek Dewan
06/02/2021, 1:01 AMLilly
06/02/2021, 1:15 AM// use case
fun fetchFromRepo() {
if (repo.connect()) repo.fetchData()
}
// repo
fun connect(): Boolean {
dataSource.connect() // dataSource is responsible for connecting and also tracking the status. connect() returns always true. If device is not connected, it connects
}
Abhishek Dewan
06/02/2021, 1:21 AMclass ConnectionRepo {
val connectionState : Boolean //tracked and available to use
//internal tracking mechanism that does whatever you need it to
// in your case always make sure to be connected
}
class MyUseCase(connectionRepo: ConnectionRepo, otherRepo: Repo) {
fun fetchFromRepo() {
if (connectionRepo.connectionState) {
otherRepo.fetchData()
}
}
}
The benefits of doing this are:
1. ConnectionRepo is not available to use in other use cases.
2. You can unit test your code with ease 🙂
This is just a quick blueprint 🙂 that can be molded however you see fit for your codebase 🙂Abhishek Dewan
06/02/2021, 1:21 AMLilly
06/02/2021, 1:29 AMconnectionState
returns false. That's the reason why I wrote if (repo.connect()) repo.fetchData()
. There shouldn't be the possibility that the device is not connected. Btw this brings me to another better idea. When a fetch operation is performed, wouldn't it be better to not bother with the connection state in the use case and instead connect silently in a place where all bt operations come together, e.g. in write
:
// dataSource
fun write(bytes: ByteArray) {
if (!isConnected) connect()
socket.write(bytes)
}
But this has also drawbacks. It's not so easy.Abhishek Dewan
06/02/2021, 1:33 AMLilly
06/02/2021, 1:36 AMLilly
06/02/2021, 1:39 AMAbhishek Dewan
06/02/2021, 1:43 AMviewModelScope.launch{
withContext(IO) {
runBlockingCoroutine
}
anotherCoroutine.
}
Abhishek Dewan
06/02/2021, 1:43 AMLilly
06/02/2021, 1:49 AMAbhishek Dewan
06/02/2021, 1:50 AMLilly
06/02/2021, 1:53 AMAbhishek Dewan
06/02/2021, 1:55 AMLilly
06/02/2021, 1:56 AMAbhishek Dewan
06/02/2021, 1:57 AMLilly
06/02/2021, 2:00 AMCoroutineScope
? Currently with no di I can only pass the scope the way down or did I miss something?Abhishek Dewan
06/02/2021, 2:02 AMLilly
06/02/2021, 2:09 AMBasePresenter
class where I create the CoroutineScope
. I might be blind but do I not have to pass the presenter/viewmodel scope to the use case here or is there a shortcut?Lilly
06/02/2021, 2:12 AMpresenterScope.launch{
useCase.fetchFromRepo(presenterScope)
}
Lilly
06/02/2021, 2:15 AMAbhishek Dewan
06/02/2021, 2:17 AMval useCase = UseCase()
in your presenter then you will need to pass the scope in when doing that as a constructor param to the UseCase.
2. Secondly you don’t need to pass the presenterScope in there, you can always create a new scope like you did with the presenterscope and reserve that for the blocking task and pass that in 🙂. Since you have a reference to it in the presenter it should be easy to tie it to the lifecycle of the presenter also.Abhishek Dewan
06/02/2021, 2:20 AMval blockedScope = CoroutineContext(<http://Dispatchers.IO|Dispatchers.IO>)
presenterScope.launch{
val useCase = UseCase(blockedScope)
useCase.fetchFromRepo()
}
class UseCase(scope: CoroutineContext) {
suspend fun fetchFromRepo() {
withContext(scope) { blockingcall }
}
}
Abhishek Dewan
06/02/2021, 2:20 AMAbhishek Dewan
06/02/2021, 2:20 AMLilly
06/02/2021, 2:22 AMAbhishek Dewan
06/02/2021, 2:24 AMdewildte
06/02/2021, 3:23 AMclass MyUseCase( val isConnected: () -> Boolean, ...) { ... }
dewildte
06/02/2021, 3:31 AMdewildte
06/02/2021, 3:31 AM() -> Boolean
is good enough.dewildte
06/02/2021, 3:36 AMUseCase
object you do not need some giant piece of infrastructure to wire it up.Abhishek Dewan
06/02/2021, 5:07 AMdave08
06/04/2021, 5:59 AM