Skip to content

Commit 599b04d

Browse files
committed
Rename MutableStateFlowImpl to ObservableMutableStateFlow
1 parent 0b074fd commit 599b04d

File tree

3 files changed

+107
-88
lines changed

3 files changed

+107
-88
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.rickclephas.kmp.observableviewmodel
2+
3+
import kotlinx.coroutines.ExperimentalForInheritanceCoroutinesApi
4+
import kotlinx.coroutines.flow.FlowCollector
5+
import kotlinx.coroutines.flow.MutableStateFlow
6+
import kotlinx.coroutines.flow.StateFlow
7+
import kotlinx.coroutines.flow.combine
8+
9+
/**
10+
* A [StateFlow] that combines the subscription counts of a [NativeViewModelScope] and a [StateFlow].
11+
*/
12+
@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
13+
internal class CombinedSubscriptionCount private constructor(
14+
private val viewModelScopeCount: StateFlow<Int>,
15+
private val stateFlowCount: StateFlow<Int>
16+
): StateFlow<Int> {
17+
18+
constructor(viewModelScope: NativeViewModelScope, stateFlow: MutableStateFlow<*>) : this(
19+
viewModelScopeCount = viewModelScope.subscriptionCount,
20+
stateFlowCount = stateFlow.subscriptionCount
21+
)
22+
23+
override val value: Int
24+
get() = viewModelScopeCount.value + stateFlowCount.value
25+
26+
override val replayCache: List<Int>
27+
get() = listOf(value)
28+
29+
override suspend fun collect(collector: FlowCollector<Int>): Nothing {
30+
viewModelScopeCount.combine(stateFlowCount) { viewModelScopeCount, stateFlowCount ->
31+
viewModelScopeCount + stateFlowCount
32+
}.collect(collector)
33+
throw IllegalStateException("CombinedSubscriptionCount.collect should never complete")
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.rickclephas.kmp.observableviewmodel
2+
3+
import kotlinx.coroutines.ExperimentalCoroutinesApi
4+
import kotlinx.coroutines.ExperimentalForInheritanceCoroutinesApi
5+
import kotlinx.coroutines.Job
6+
import kotlinx.coroutines.flow.FlowCollector
7+
import kotlinx.coroutines.flow.MutableStateFlow
8+
import kotlinx.coroutines.flow.StateFlow
9+
10+
/**
11+
* A [MutableStateFlow] that triggers [NativeViewModelScope.sendObjectWillChange]
12+
* and accounts for the [NativeViewModelScope.subscriptionCount].
13+
*/
14+
@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
15+
internal class ObservableMutableStateFlow<T>(
16+
private val viewModelScope: NativeViewModelScope,
17+
private val stateFlow: MutableStateFlow<T>
18+
): MutableStateFlow<T> {
19+
20+
override var value: T
21+
get() = stateFlow.value
22+
set(value) {
23+
if (stateFlow.value != value) {
24+
viewModelScope.sendObjectWillChange()
25+
}
26+
stateFlow.value = value
27+
}
28+
29+
override val replayCache: List<T>
30+
get() = stateFlow.replayCache
31+
32+
override val subscriptionCount: StateFlow<Int> = CombinedSubscriptionCount(viewModelScope, stateFlow)
33+
34+
override suspend fun collect(collector: FlowCollector<T>): Nothing =
35+
stateFlow.collect(collector)
36+
37+
override fun compareAndSet(expect: T, update: T): Boolean {
38+
if (stateFlow.value == expect && expect != update) {
39+
viewModelScope.sendObjectWillChange()
40+
}
41+
return stateFlow.compareAndSet(expect, update)
42+
}
43+
44+
@ExperimentalCoroutinesApi
45+
override fun resetReplayCache() = stateFlow.resetReplayCache()
46+
47+
// Same implementation as in StateFlowImpl, but we need to go through our own value property.
48+
// https://github.com/Kotlin/kotlinx.coroutines/blob/6dfabf763fe9fc91fbb73eb0f2d5b488f53043f1/kotlinx-coroutines-core/common/src/flow/StateFlow.kt#L369
49+
override fun tryEmit(value: T): Boolean {
50+
this.value = value
51+
return true
52+
}
53+
54+
// Same implementation as in StateFlowImpl, but we need to go through our own value property.
55+
// https://github.com/Kotlin/kotlinx.coroutines/blob/6dfabf763fe9fc91fbb73eb0f2d5b488f53043f1/kotlinx-coroutines-core/common/src/flow/StateFlow.kt#L374
56+
override suspend fun emit(value: T) {
57+
this.value = value
58+
}
59+
}
60+
61+
/**
62+
* A [StateFlow] backed by an [ObservableMutableStateFlow] optionally holding a reference to a [Job].
63+
*/
64+
@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
65+
internal class ObservableStateFlow<T>(
66+
internal val flow: ObservableMutableStateFlow<T>,
67+
@Suppress("unused")
68+
private val job: Job? = null
69+
): StateFlow<T> by flow

kmp-observableviewmodel-core/src/appleMain/kotlin/com/rickclephas/kmp/observableviewmodel/StateFlow.kt

Lines changed: 3 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -11,81 +11,7 @@ import kotlin.coroutines.EmptyCoroutineContext
1111
public actual fun <T> MutableStateFlow(
1212
viewModelScope: ViewModelScope,
1313
value: T
14-
): MutableStateFlow<T> = MutableStateFlowImpl(viewModelScope.asNative(), MutableStateFlow(value))
15-
16-
/**
17-
* A [MutableStateFlow] that triggers [ViewModelScopeImpl.sendObjectWillChange]
18-
* and accounts for the [ViewModelScopeImpl.subscriptionCount].
19-
*/
20-
@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
21-
private class MutableStateFlowImpl<T>(
22-
private val viewModelScope: NativeViewModelScope,
23-
private val stateFlow: MutableStateFlow<T>
24-
): MutableStateFlow<T> {
25-
26-
override var value: T
27-
get() = stateFlow.value
28-
set(value) {
29-
if (stateFlow.value != value) {
30-
viewModelScope.sendObjectWillChange()
31-
}
32-
stateFlow.value = value
33-
}
34-
35-
override val replayCache: List<T>
36-
get() = stateFlow.replayCache
37-
38-
override val subscriptionCount: StateFlow<Int> =
39-
SubscriptionCountFlow(viewModelScope.subscriptionCount, stateFlow.subscriptionCount)
40-
41-
override suspend fun collect(collector: FlowCollector<T>): Nothing =
42-
stateFlow.collect(collector)
43-
44-
override fun compareAndSet(expect: T, update: T): Boolean {
45-
if (stateFlow.value == expect && expect != update) {
46-
viewModelScope.sendObjectWillChange()
47-
}
48-
return stateFlow.compareAndSet(expect, update)
49-
}
50-
51-
@ExperimentalCoroutinesApi
52-
override fun resetReplayCache() = stateFlow.resetReplayCache()
53-
54-
// Same implementation as in StateFlowImpl, but we need to go through our own value property.
55-
// https://github.com/Kotlin/kotlinx.coroutines/blob/6dfabf763fe9fc91fbb73eb0f2d5b488f53043f1/kotlinx-coroutines-core/common/src/flow/StateFlow.kt#L369
56-
override fun tryEmit(value: T): Boolean {
57-
this.value = value
58-
return true
59-
}
60-
61-
// Same implementation as in StateFlowImpl, but we need to go through our own value property.
62-
// https://github.com/Kotlin/kotlinx.coroutines/blob/6dfabf763fe9fc91fbb73eb0f2d5b488f53043f1/kotlinx-coroutines-core/common/src/flow/StateFlow.kt#L374
63-
override suspend fun emit(value: T) {
64-
this.value = value
65-
}
66-
}
67-
68-
/**
69-
* A [StateFlow] that combines the subscription counts of a [ViewModelScopeImpl] and [StateFlow].
70-
*/
71-
@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
72-
private class SubscriptionCountFlow(
73-
private val viewModelScopeSubscriptionCount: StateFlow<Int>,
74-
private val stateFlowSubscriptionCount: StateFlow<Int>
75-
): StateFlow<Int> {
76-
override val value: Int
77-
get() = viewModelScopeSubscriptionCount.value + stateFlowSubscriptionCount.value
78-
79-
override val replayCache: List<Int>
80-
get() = listOf(value)
81-
82-
override suspend fun collect(collector: FlowCollector<Int>): Nothing {
83-
viewModelScopeSubscriptionCount.combine(stateFlowSubscriptionCount) { count1, count2 ->
84-
count1 + count2
85-
}.collect(collector)
86-
throw IllegalStateException("SubscriptionCountFlow collect completed")
87-
}
88-
}
14+
): MutableStateFlow<T> = ObservableMutableStateFlow(viewModelScope.asNative(), MutableStateFlow(value))
8915

9016
/**
9117
* @see kotlinx.coroutines.flow.stateIn
@@ -98,9 +24,9 @@ public actual fun <T> Flow<T>.stateIn(
9824
// Similar to kotlinx.coroutines, but using our custom MutableStateFlowImpl and CoroutineContext logic.
9925
// https://github.com/Kotlin/kotlinx.coroutines/blob/6dfabf763fe9fc91fbb73eb0f2d5b488f53043f1/kotlinx-coroutines-core/common/src/flow/operators/Share.kt#L135
10026
val scope = viewModelScope.asNative()
101-
val state = MutableStateFlowImpl(scope, MutableStateFlow(initialValue))
27+
val state = ObservableMutableStateFlow(scope, MutableStateFlow(initialValue))
10228
val job = scope.coroutineScope.launchSharing(EmptyCoroutineContext, this, state, started, initialValue)
103-
return ReadonlyStateFlow(state, job)
29+
return ObservableStateFlow(state, job)
10430
}
10531

10632
/**
@@ -136,14 +62,3 @@ private fun <T> CoroutineScope.launchSharing(
13662
}
13763
}
13864
}
139-
140-
/**
141-
* Similar to the kotlinx.coroutines implementation, used to return a read-only StateFlow with an optional Job.
142-
* https://github.com/Kotlin/kotlinx.coroutines/blob/6dfabf763fe9fc91fbb73eb0f2d5b488f53043f1/kotlinx-coroutines-core/common/src/flow/operators/Share.kt#L379
143-
*/
144-
@OptIn(ExperimentalForInheritanceCoroutinesApi::class)
145-
private class ReadonlyStateFlow<T>(
146-
flow: StateFlow<T>,
147-
@Suppress("unused")
148-
private val job: Job?
149-
): StateFlow<T> by flow

0 commit comments

Comments
 (0)