Pragnesh Meniya
08/21/2023, 5:02 AMSeth Madison
09/06/2023, 5:36 AMbinding
has started collecting. I have code along the lines of:
binder = bind {
renderer.events.map { it.toIntent() } bindTo store
}.apply { start() }
But I have no way to know when it is safe to call renderer.dispatch
because the call to start()
is asynchronous, and non-blocking.
It would be nice to provide an API similar to stateIn
, which offers a default implementation that launches a job to start sharing, and a secondary suspending stateIn()
function that blocks until the sharing has begun.
In MVIKotlin we could offer suspending start()
that would block until the flows are all actively collecting.
Thoughts? Am I missing something?Shabinder Singh
09/08/2023, 8:59 PMCoroutineExecutor
and StoreFactories in upper levels extend this BaseExecutor
My use case is, I want to do some logic process whenever a BaseMsg
is dispatched,
My first thought was to override the dispatch method
in BaseExecutor
and do processing by having a check in there.
but the dispatch of CoroutineExecutor is not marked as Open.
Is there a better way designed to support this use case or should I open a ticket suggesting marking dispatch as open ?Nacho Ruiz Martin
09/18/2023, 6:16 AMCoroutineScope
of the executor inside coroutineExecutorFactory
or onIntent
?
I’d like to use the same mechanism as with the previous API for my flows to be automatically cancelled when the scope is disposed.Tung97 Hl
10/13/2023, 4:17 AMArkadii Ivanov
11/04/2023, 9:05 PMArkadii Ivanov
11/04/2023, 11:14 PM3.3.0
is released!
👉 Updated Kotlin to 1.9.20
and other dependencies
👉 Removed the support of the legacy memory model
Release notes: https://github.com/arkivanov/MVIKotlin/releases/tag/3.3.0Nacho Ruiz Martin
11/18/2023, 7:15 AMstateFlow
extension?
I’ve found that if you map it with:
store.stateFlow.map(::mapper).stateIn(scope, SharingStarted.Eagerly, store.state.mapper())
you fall into the everlasting problem of textfield states.
I’ve been thinking of adding an optional mapping function inside stateFlow
. This would help users of MviKotlin
+ Decompose
that want to use Coroutines
all the way down.Arkadii Ivanov
11/19/2023, 10:03 PM1.3.0-beta01
.
- Added Lifecycle extensions for Coroutines
- Added Lifecycle extensions for Reaktive
- Added convenience extensions for LifecycleOwner
- Call doOnDestroy callback if Lifecycle is already destroyed
Release notes: https://github.com/arkivanov/Essenty/releases/tag/1.3.0-beta01
Slack ConversationArkadii Ivanov
12/02/2023, 4:02 PMmvikotlin-extensions-reaktive
module to version 2.0.0
- Updated Essenty to 1.3.0
- Deleted all deprecated code
- Merged rx
, rx-internal
and utils-internal
modules into mvikotlin
module
- Updated Kotlin to 1.9.21
- Disabled debug artifact publications for Android
- Support sending Actions from Executors
- Replaced getState
argument function in Executors and state
property in DSL with state()
function
Release notes: https://github.com/arkivanov/MVIKotlin/releases/tag/4.0.0-alpha01electrolobzik
12/31/2023, 2:09 PMwhen
block of the regular Executor for Intents?electrolobzik
01/04/2024, 2:41 PMArkadii Ivanov
01/16/2024, 9:10 AM4.0.0-alpha02
is released!
👉 Support wasmJs
target
Release notes: https://github.com/arkivanov/MVIKotlin/releases/tag/4.0.0-alpha02Zsolt.bertalan
02/29/2024, 2:03 PMRobert Munro
03/29/2024, 2:13 PMUiComponent
interface with create()
and destory()
methods. So now i want to create an essenty lifecycle object that i can pass to my Controller and i can manually call state changes from the create and destroy methods. Is there some prebuilt object in essenty that I can use for this or how would i implement one?Arkadii Ivanov
04/05/2024, 8:51 PM4.0.0-beta01
is released!
👉 Updated Kotlin to 1.9.23
, Essenty to 2.0.0-beta01
, coroutines to 1.8.0
, Reaktive to 2.1.0
Release notes: https://github.com/arkivanov/MVIKotlin/releases/tag/4.0.0-beta01Arkadii Ivanov
04/27/2024, 9:33 PM4.0.0
is released!
Changes since version 4.0.0-beta01
👉 Updated Essenty to 2.0.0
👉 Promoted Executor onAction
and forward
methods to stable
👉 Promoted DSL APIs to stable
Full release notes and migration guides: https://github.com/arkivanov/MVIKotlin/releases/tag/4.0.0Tung97 Hl
05/02/2024, 11:16 AMFrancis Mariano
05/17/2024, 5:21 PMPavel S
05/22/2024, 7:24 AMstore
by calling storeFactory.create
and instead is supposed to create some factory class with a function returning object : Store<…> by storeFactory.create(…)
?Pavel S
05/31/2024, 8:01 AMPavel S
06/02/2024, 4:16 PMlabels
when the label is published immediately on store startup. I’ve modified my previous sample to illustrate the issue. I expect to see “a” in the first text and “bb” in the second text. AStore
dispatches an Action.PublishLabel
in the bootstrapper, and the action handler publishes Label.LabelPublished
. I subscribe to labels
in the init block of DefaultWelcomeComponent
but the label event is never received. But if you add a delay(<long>)
call before the publish
call, the label event is received. I initially thought this had something to do with the order of initialization of all the objects, but it seems that wasn’t the case, since if you use labels(observer: Observer<Label>): Disposable
the label event is still received. How would you suggest to deal with this?Arkadii Ivanov
06/07/2024, 1:05 PM4.1.0
is released!
👉 Use typeOf
as key instead of KClass
in InstanceKeeper#getStore
👉 Updated Kotlin to 2.0.0
, Essenty to 2.1.0
and other dependencies
Full release notes: https://github.com/arkivanov/MVIKotlin/releases/tag/4.1.0CXwudi
06/25/2024, 8:47 PMScreenAComponent
that receive a callback to navigate to screen B like the following:
interface ScreenAComponent {
val state: Value<ScreenAState>
fun sendIntent(intent: ScreenAIntent)
}
class DefaultScreenAComponent(
private val componentContext: ComponentContext,
private val storeFactory: StoreFactory,
val onScreenBNavigate: (String) -> Unit
) : ScreenAComponent, ComponentContext by componentContext
The DefaultScreenAComponent
is created by the root component which basically just does the stack navigation, and the onScreenBNavigate
callback passed into ScreenAComponent
is basically just stackNavigation.push()
function call.
Now, I don't know which is the right place to call onScreenBNavigate
, should I do it by subscribing the labels? Or should I call it in Reducer? Or in Executor? Or create a dedicated state for representing that the callback should be called?CXwudi
07/25/2024, 10:07 PMonIntent<LandingPageIntent.ToNextPage> {
launch {
... some other suspend fun call
publish(LandingPageToNextPageLabel)
log.d { "Label sent" }
}
Now I have two test cases in kotlintest. One has deadlock, another one doesn't. Here is the one with the deadlock:
@Test
fun normalFlowStucked() = runTest {
// the labels Flow way will be stuck if running in single-threaded env like JS or single-threaded Dispatcher
// However, from debugging, the label is actually dispatched, but for some reason, we can't receive it
val job = launch {
landingPageStore.labels.stateIn(CoroutineScope(coroutineContext)).collect {
log.d { "Received $it" }
assertEquals(LandingPageToNextPageLabel, it)
}
}
landingPageStore.accept(LandingPageIntent.TextChanged("a change"))
assertEquals("a change", landingPageStore.state.url)
landingPageStore.accept(LandingPageIntent.ToNextPage)
job.join()
}
The test case can still print "Label sent"
, but not "Received LandingPageToNextPageLabel"
. This only happens if running in jsTest
or in Android platform without specifying the Dispatchers.Default
. Now here is another test case that uses Channel
instead of `Flow`:
@Test
fun testNormalFlow1() = runTest {
// Can't make Store.labels Flow way working in single-threaded env like JS or single-threaded Dispatcher
val channel = Channel<LandingPageToNextPageLabel>()
val scope = CoroutineScope(coroutineContext)
landingPageStore.labels(observer {
scope.launch {
channel.send(it)
log.d { "Received $it" }
}
})
landingPageStore.accept(LandingPageIntent.TextChanged("a change"))
assertEquals("a change", landingPageStore.state.url)
landingPageStore.accept(LandingPageIntent.ToNextPage)
val label = channel.receive()
assertEquals(label, LandingPageToNextPageLabel)
channel.close()
}
This one runs successfully, no deadlock.
The actual codes are open sourced at [here](https://github.com/CXwudi/realworld-compose-http4k-example-app/blob/master/conduit[…]onduit/frontend/logic/component/landing/LandingPageStoreTest.kt) and [here](https://github.com/CXwudi/realworld-compose-http4k-example-app/blob/master/conduit[…]uit/frontend/logic/component/landing/LandingPageStoreFactory.kt) for references
So here are my questions:
1. Is the deadlock expected for the first test case?
2. Is the second test case a good way to test labels dispatching? If not, then what are some good way to test it?Arkadii Ivanov
08/08/2024, 11:06 PM4.2.0
is released!
👉 Added Store.labelsChannel(...): ReceiveChannel<Label>
extension function
👉 Updated Kotlin to 2.0.10
Release notes: https://github.com/arkivanov/MVIKotlin/releases/tag/4.2.0CXwudi
10/04/2024, 6:26 PMlabelsChannel()
, I have some improvement idea in the comment sectionSeth Madison
10/29/2024, 5:49 PMMark Rebhan
12/06/2024, 3:24 PMLoggingStoreFactory
, the Android app eventually crashes due to a JNI global reference table overflow. This crash goes away if we don't use the logging store 🤔. I'm trying to figure out if it's an issue with the library or system print.Arkadii Ivanov
01/16/2025, 4:32 PM4.3.0
is released!
👉 Updated Kotlin to 2.1.0
and other deps
👉 Added Store#stateFlow(Lifecycle)
and Store#labelsChannel(Lifecycle)
API, promoted to stable
Release notes: https://github.com/arkivanov/MVIKotlin/releases/tag/4.3.0