Hello everyone. How are you? I have two components...
# mvikotlin
f
Hello everyone. How are you? I have two components (A and B) each one with its store. In Store A, I have a stateFlow collect which is launched in onAction. So I navigate to component B via push, in this way store A is not disposed (even component A calls onStop). Once store A is not disposed, the stateFlow continues collecting. What is the better approach to cancel the collect and resume it when the store A come back visible again??
a
Maybe pass the Lifecycle to the Executor and use Flow.flowWithLifecycle?
This won't work if the Store is retained, though.
Another option is to send Intent.Start and Intent.Stop explicitly.
You can create LifecycleRegistry inside the Executor and drive it via intents. And still use flowWithLifecycle.
f
You can create LifecycleRegistry inside the Executor and drive it via intents. And still use flowWithLifecycle.
I will try. I was creating a child scope from store and start/cancel it via intents
👍 1
First I need to update from 3.2.1 to 4.0.0
a
Indeed!
f
@Arkadii Ivanov I am updating the Decompose as well.
Copy code
Execution failed for task ':shared:transformIosMainCInteropDependenciesMetadataForIde'.
> Could not resolve all files for configuration ':shared:iosMainResolvableDependenciesMetadata'.
   > Could not find com.arkivanov.decompose:extensions-compose-jetbrains:3.0.0.
     Required by:
         project :shared
The gradle sync is failing. Do you know the reason??
a
Yes, for Decompose you will need to follow the migration guides. https://github.com/arkivanov/Decompose/releases/tag/3.0.0 Though, this shouldn't be really necessary for your case. You could just copy-paste this: https://github.com/arkivanov/Essenty/blob/master/lifecycle-coroutines/src/commonMain/kotlin/com/arkivanov/essenty/lifecycle/coroutines/FlowWithLifecycle.kt
f
I made in this way
Copy code
onIntent<HomeStore.Intent.Start> {
    logger.d { "START" }
    scopeObservations = CoroutineScope(Job() + Dispatchers.Main)
    observations()
}
Copy code
onIntent<HomeStore.Intent.Stop> {
    logger.d { "STOP" }
    scopeObservations.cancel()
}
And in the component I call the intent in the lifecycle correspondent.
One thing I did not like is:
Copy code
private fun CoroutineExecutorScope<HomeStore.State, Msg, Unit, HomeStore.Label>.observations() {
    peripheralRepository.power.onEach {
        dispatch(Msg.UpdatePower(it))
    }.launchIn(scopeObservation)
}
The observation function has CoroutineExecutorScope as its scope because I need of Msg and State, but the onEach is launched in another scope.
a
You mentioned earlier:
I have a stateFlow collect which is launched in onAction
So I assumed that you need to collect the flow in an action handler, and also have the flow lifecycle-aware. With your current approach in the snippet you are just starting the flow on Start and cancel it on Stop. Also I still find the approach with
LifecycleRegistry
cleaner. I.e. you would need to only
collect
once (e.g. in
onAction
) and then just
start/stop
the lifecycle via intents.
f
ok...I will try, but I need your help....
Copy code
val lifecycleRegistry = LifecycleRegistry()

onAction<Unit> {
    peripheralRepository.power.onEach {
        dispatch(Msg.UpdatePower(it))
    }.launchIn(this).   // scope of store executor
}

onIntent<HomeStore.Intent.Start> {
    lifecycleRegistry.onStart()
}

onIntent<HomeStore.Intent.Stop> {
    lifecycleRegistry.onStart()
}
I do not know how to bind the collect with lifecycleRegistry :(
a
First of all, it's better to use the
resume
and
stop
extension functions. And lastly, you can use
flowWithLifecycle
operator from Essenty, and then
collect
. https://github.com/arkivanov/Essenty?tab=readme-ov-file#coroutines-extensions
Let me know if there are any issues.
f
Copy code
private val lifecycleRegistry = LifecycleRegistry()

onAction<Unit> {
    logger.d { "ACTION" }
    peripheralRepository.power.withLifecycle(lifecycleRegistry).onEach {
        logger.d { "power" }        
    }.launchIn(scope = this)
}

onIntent<HomeStore.Intent.Start> {
    logger.d { "START" }
    lifecycleRegistry.resume()
}

onIntent<HomeStore.Intent.Stop> {
    logger.d { "STOP" }
    lifecycleRegistry.pause()
}
power is logged even after lifecycleRegistry.pause() is called 😞
a
Yes, because by default withLifecycle is bound to STARTED state. Could you please try either using start/stop on lifecycle, or specify RESUMED state for withLifecycle?
f
works with
Copy code
onIntent<HomeStore.Intent.Start> {
    logger.d { "START" }
    lifecycleRegistry.start()
}

onIntent<HomeStore.Intent.Stop> {
    logger.d { "STOP" }
    lifecycleRegistry.stop()
}
a
Yep, looks good! Or resume/stop would also work. But not resume/pause.
f
thank you so much
👍 1