diff --git a/CHANGELOG.md b/CHANGELOG.md index e5f34040492..8c30ac202c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 0.20.18 + +* `Coroutines`: + * `SpecialMutableStateFlow` now extends `MutableStateFlow` + * `Compose`: + * Deprecate `FlowState` due to its complexity in fixes + ## 0.20.17 * `Versions`: diff --git a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowState.kt b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowState.kt index 487f1065564..c3729ac32cf 100644 --- a/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowState.kt +++ b/coroutines/compose/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/compose/FlowState.kt @@ -1,10 +1,7 @@ package dev.inmo.micro_utils.coroutines.compose import androidx.compose.runtime.MutableState -import androidx.compose.runtime.State -import androidx.compose.runtime.derivedStateOf import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow -import dev.inmo.micro_utils.coroutines.doInUI import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -12,6 +9,7 @@ import kotlinx.coroutines.Dispatchers * This type works like [MutableState], [kotlinx.coroutines.flow.StateFlow] and [kotlinx.coroutines.flow.MutableSharedFlow]. * Based on [SpecialMutableStateFlow] */ +@Deprecated("Will be removed soon") class FlowState( initial: T, internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default) @@ -25,9 +23,9 @@ class FlowState( tryEmit(value) } - override suspend fun onChange(value: T) { + override fun onChangeWithoutSync(value: T) { internalValue = value - super.onChange(value) + super.onChangeWithoutSync(value) } override fun component1(): T = value diff --git a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SpecialMutableStateFlow.kt b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SpecialMutableStateFlow.kt index e39d5cdaa48..ecc421aa5cb 100644 --- a/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SpecialMutableStateFlow.kt +++ b/coroutines/src/commonMain/kotlin/dev/inmo/micro_utils/coroutines/SpecialMutableStateFlow.kt @@ -3,10 +3,14 @@ package dev.inmo.micro_utils.coroutines import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.internal.SynchronizedObject +import kotlinx.coroutines.internal.synchronized /** * Works like [StateFlow], but guarantee that latest value update will always be delivered to @@ -15,7 +19,9 @@ import kotlinx.coroutines.flow.StateFlow open class SpecialMutableStateFlow( initialValue: T, internalScope: CoroutineScope = CoroutineScope(Dispatchers.Default) -) : StateFlow, FlowCollector, MutableSharedFlow { +) : MutableStateFlow, FlowCollector, MutableSharedFlow { + @OptIn(InternalCoroutinesApi::class) + private val syncObject = SynchronizedObject() protected val internalSharedFlow: MutableSharedFlow = MutableSharedFlow( replay = 0, extraBufferCapacity = 2, @@ -28,16 +34,13 @@ open class SpecialMutableStateFlow( ) protected var _value: T = initialValue - override val value: T + override var value: T get() = _value - protected open suspend fun onChange(value: T) { - _value = value - publicSharedFlow.emit(value) - } - protected val job = internalSharedFlow.subscribe(internalScope) { - if (_value != it) { - onChange(it) + set(value) { + doOnChangeAction(value) } + protected val job = internalSharedFlow.subscribe(internalScope) { + doOnChangeAction(it) } override val replayCache: List @@ -45,6 +48,29 @@ open class SpecialMutableStateFlow( override val subscriptionCount: StateFlow get() = publicSharedFlow.subscriptionCount + @OptIn(InternalCoroutinesApi::class) + override fun compareAndSet(expect: T, update: T): Boolean { + return synchronized(syncObject) { + if (expect == _value && update != _value) { + doOnChangeAction(update) + } + expect == _value + } + } + + protected open fun onChangeWithoutSync(value: T) { + _value = value + publicSharedFlow.tryEmit(value) + } + @OptIn(InternalCoroutinesApi::class) + protected open fun doOnChangeAction(value: T) { + synchronized(syncObject) { + if (_value != value) { + onChangeWithoutSync(value) + } + } + } + @ExperimentalCoroutinesApi override fun resetReplayCache() = publicSharedFlow.resetReplayCache() diff --git a/gradle.properties b/gradle.properties index 68aa34ac761..28a0c89171f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,5 +15,5 @@ crypto_js_version=4.1.1 # Project data group=dev.inmo -version=0.20.17 -android_code_version=223 +version=0.20.18 +android_code_version=224