Skip to content

Commit 279fdd9

Browse files
authored
Merge pull request #10 from motorro/typed_multi_machine
Typed multi machine
2 parents 477d7fa + c5bb1ee commit 279fdd9

60 files changed

Lines changed: 1310 additions & 146 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.idea/copyright/Apache.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/copyright/profiles_settings.xml

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ val commonMain by getting {
133133
- [Welcome](examples/welcome/welcome) - multi-module example of user on-boarding flow
134134
- [Parallel](examples/multi/parallel) - two machines running in parallel in one proxy state
135135
- [Navbar](examples/multi/navbar) - several machines running in proxy state, one of them active at a time
136+
- [Mixed](examples/multi/mixed) - two machines of different gesture/UI system mixed in one state
136137
- [Lifecycle](examples/lifecycle) - track your Android app lifecycle to pause pending operations when the app is suspended
137138

138139
## The basic task - Load-Content-Error
@@ -1258,12 +1259,12 @@ private sealed class MultiGesture {
12581259
data class StringGesture(val data: String) : MultiGesture()
12591260
}
12601261

1261-
private open class TestState : MultiMachineState<MultiGesture, String>() {
1262+
private open class TestState : MultiMachineState<MultiGesture, String, Any, Any>() {
12621263

12631264
private data object IntKey : MachineKey<Int, Int>(null) // Int for gesture and state
12641265
private data object StringKey : MachineKey<String, String>(null) // String for gesture and state
12651266

1266-
override val container: ProxyMachineContainer = AllTogetherMachineContainer(
1267+
override val container: ProxyMachineContainer<Any, Any> = AllTogetherMachineContainer(
12671268
listOf(
12681269
object : MachineInit<Int, Int> {
12691270
override val key: MachineKey<Int, Int> = IntKey
@@ -1310,8 +1311,8 @@ private open class TestState : MultiMachineState<MultiGesture, String>() {
13101311
private data object StringKey : MachineKey<String, String>(null) // String for gesture and state
13111312

13121313
// ... machine init omitted
1313-
1314-
override fun mapUiState(provider: UiStateProvider, changedKey: MachineKey<*, *>?): String {
1314+
1315+
override fun mapUiState(provider: UiStateProvider<Any>, changedKey: MachineKey<*, out Any>?): String {
13151316
val i: Int = provider.getValue(IntKey) // Cast to Int
13161317
val s: String = provider.getValue(StringKey) // Cast to String
13171318
return "$i - $s" // Combined state of any kind you like
@@ -1347,7 +1348,7 @@ private open class TestState : MultiMachineState<MultiGesture, String>() {
13471348
// ... machine init omitted
13481349

13491350
// Our parent gesture is
1350-
override fun mapGesture(parent: MultiGesture, processor: GestureProcessor) = when(parent) {
1351+
override fun mapGesture(parent: MultiGesture, processor: GestureProcessor<Any, Any>) = when(parent) {
13511352
is MultiGesture.IntGesture -> {
13521353
processor.process(IntKey, parent.data) // Int expected
13531354
}

commonstatemachine/src/commonMain/kotlin/com/motorro/commonstatemachine/CommonStateMachine.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ package com.motorro.commonstatemachine
1717
* Common state machine input - from the outside world to the current state
1818
* @param G UI gesture
1919
*/
20-
interface MachineInput<G: Any> {
20+
interface MachineInput<in G: Any> {
2121
/**
2222
* Updates state with UI gesture
2323
* @param gesture UI gesture to proceed
@@ -51,7 +51,7 @@ interface MachineOutput<G: Any, U: Any> {
5151
/**
5252
* Current public machine status
5353
*/
54-
interface MachineStatus<U : Any> {
54+
interface MachineStatus<out U : Any> {
5555
/**
5656
* Checks if machine is started
5757
*/
@@ -71,6 +71,11 @@ interface MachineStatus<U : Any> {
7171
*/
7272
interface CommonStateMachine<G: Any, U: Any> : MachineInput<G>, MachineOutput<G, U>, MachineStatus<U> {
7373

74+
/**
75+
* Starts the machine
76+
*/
77+
fun start()
78+
7479
/**
7580
* Base state-machine implementation
7681
* @param G UI gesture
@@ -104,9 +109,9 @@ interface CommonStateMachine<G: Any, U: Any> : MachineInput<G>, MachineOutput<G,
104109
}
105110

106111
/**
107-
* Starts machine
112+
* Starts the machine
108113
*/
109-
fun start() {
114+
override fun start() {
110115
if (started.not()) {
111116
activeState = init()
112117
startMachineState()
@@ -136,5 +141,4 @@ interface CommonStateMachine<G: Any, U: Any> : MachineInput<G>, MachineOutput<G,
136141
activeState.start(this)
137142
}
138143
}
139-
}
140-
144+
}

commonstatemachine/src/commonMain/kotlin/com/motorro/commonstatemachine/multi/ActiveStateMachine.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import com.motorro.commonstatemachine.lifecycle.MachineLifecycle
2828
*/
2929
internal class ActiveStateMachine<G: Any, U: Any>(
3030
init: MachineInit<G, U>,
31-
onUiChanged: (MachineKey<*, *>, Any) -> Unit
31+
onUiChanged: (MachineKey<G, U>, U) -> Unit
3232
) : CommonStateMachine<G, U>, Activated {
3333
/**
3434
* Machine lifecycle
@@ -41,7 +41,10 @@ internal class ActiveStateMachine<G: Any, U: Any>(
4141
{ onUiChanged(init.key, it.child) }
4242
)
4343

44-
init {
44+
/**
45+
* Starts the machine
46+
*/
47+
override fun start() {
4548
machine.start()
4649
}
4750

commonstatemachine/src/commonMain/kotlin/com/motorro/commonstatemachine/multi/MachineAccess.kt

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,55 +13,71 @@
1313

1414
package com.motorro.commonstatemachine.multi
1515

16-
import com.motorro.commonstatemachine.CommonStateMachine
16+
/**
17+
* Provides access to the state-machine
18+
* @param CG Child gesture system
19+
* @param CU Child UI-state system
20+
*/
21+
interface MachineAccess<CG: Any, CU: Any> {
22+
/**
23+
* Keys collection
24+
*/
25+
val keys: Set<MachineKey<*, out CU>>
26+
27+
/**
28+
* Retrieves UI state.
29+
* [MachineInit] and [key] bind types securely.
30+
* @param U Concrete UI state bound with the [key], subtype of CU
31+
* @param key Machine key
32+
*/
33+
fun <U: CU> getState(key: MachineKey<*, U>): U?
34+
35+
/**
36+
* Processes machine gesture.
37+
* [MachineInit] and [key] bind types securely.
38+
* @param G Concrete gesture, subtype of th [CG]
39+
* @param key Machine key
40+
* @param gesture Gesture to process
41+
*/
42+
fun <G: CG> process(key: MachineKey<G, out CU>, gesture: G)
43+
}
1744

1845
/**
1946
* Retrieves a ui-state given the [MachineKey]
2047
*/
21-
interface UiStateProvider {
48+
interface UiStateProvider<CU: Any> {
2249

2350
/**
2451
* Retrieves all running machine keys
2552
*/
26-
fun getMachineKeys(): Set<MachineKey<*, *>>
53+
fun getMachineKeys(): Set<MachineKey<*, out CU>>
2754

2855
/**
2956
* Gets a concrete UI-state
3057
* @param key Machine key your state is bound to
3158
* @throws IllegalStateException if state is not found in common state
3259
*/
33-
fun <U: Any> getValue(key: MachineKey<*, U>): U = checkNotNull(get(key)) {
60+
fun <U: CU> getValue(key: MachineKey<*, out U>): U = checkNotNull(get(key)) {
3461
"Key $key not found in machine map"
3562
}
3663

3764
/**
3865
* Gets a concrete UI-state
3966
* @param key Machine key your state is bound to
4067
*/
41-
operator fun <U: Any> get(key: MachineKey<*, U>): U?
68+
operator fun <U: CU> get(key: MachineKey<*, U>): U?
4269
}
4370

4471
/**
4572
* Redirects your gesture to be processed with a child machine
4673
* identified by [MachineKey]
4774
*/
48-
interface GestureProcessor {
75+
interface GestureProcessor<CG: Any, CU: Any> {
4976
/**
5077
* Redirects your gesture to be processed by child machine
5178
* if machine identified by [key] is found
5279
* @param key Machine key
5380
* @param gesture Gesture to process
5481
*/
55-
fun <G: Any> process(key: MachineKey<G, *>, gesture: G)
56-
}
57-
58-
/**
59-
* Runs [block] with machine stored in [machineMap] under this key
60-
*/
61-
@Suppress("UNCHECKED_CAST")
62-
internal inline fun <G: Any, U: Any, R> withMachine(
63-
key: MachineKey<G, U>,
64-
machineMap: MachineMap,
65-
block: CommonStateMachine<G, U>.() -> R
66-
): R? = (machineMap[key] as? CommonStateMachine<G, U>)?.block()
67-
82+
fun <G: CG> process(key: MachineKey<G, out CU>, gesture: G)
83+
}

commonstatemachine/src/commonMain/kotlin/com/motorro/commonstatemachine/multi/MultiMachineState.kt

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ import com.motorro.commonstatemachine.CommonMachineState
2121
* - Override [mapUiState] to build a combined UI state af all the machines
2222
* - Override [container] with a [ProxyMachineContainer] of your choice
2323
*/
24-
abstract class MultiMachineState<PG: Any, PU: Any> : CommonMachineState<PG, PU>() {
24+
abstract class MultiMachineState<PG: Any, PU: Any, CG: Any, CU: Any> : CommonMachineState<PG, PU>() {
2525
/**
2626
* Proxy machines container
2727
*/
28-
protected abstract val container: ProxyMachineContainer
28+
protected abstract val container: ProxyMachineContainer<CG, CU>
2929

3030
/**
3131
* A part of [start] template to initialize state
@@ -45,20 +45,18 @@ abstract class MultiMachineState<PG: Any, PU: Any> : CommonMachineState<PG, PU>(
4545
* Updates machine view-state
4646
*/
4747
@Suppress("UNUSED_PARAMETER")
48-
private fun onUiStateChange(key: MachineKey<*, *>, uiState: Any) {
48+
private fun onUiStateChange(key: MachineKey<*, out CU>, uiState: CU) {
4949
setUiState(buildUiState(key))
5050
}
5151

5252
/**
5353
* Builds common UI state
5454
*/
55-
private fun buildUiState(changedKey: MachineKey<*, *>?): PU {
56-
val machineMap = container.getMachines()
57-
val uiStateProvider = object : UiStateProvider {
58-
override fun getMachineKeys(): Set<MachineKey<*, *>> = machineMap.keys
59-
override fun <U : Any> get(key: MachineKey<*, U>): U? {
60-
return withMachine(key, machineMap) { getUiState() }
61-
}
55+
private fun buildUiState(changedKey: MachineKey<*, out CU>?): PU {
56+
val access = container.machineAccess
57+
val uiStateProvider = object : UiStateProvider<CU> {
58+
override fun getMachineKeys(): Set<MachineKey<*, out CU>> = access.keys
59+
override fun <U : CU> get(key: MachineKey<*, U>): U? = access.getState(key)
6260
}
6361
return mapUiState(uiStateProvider, changedKey)
6462
}
@@ -74,10 +72,10 @@ abstract class MultiMachineState<PG: Any, PU: Any> : CommonMachineState<PG, PU>(
7472
* A part of [process] template to process UI gesture
7573
*/
7674
override fun doProcess(gesture: PG) {
77-
val machineMap = container.getMachines()
78-
val processor = object : GestureProcessor {
79-
override fun <G : Any> process(key: MachineKey<G, *>, gesture: G) {
80-
withMachine(key, machineMap) { process(gesture) }
75+
val access = container.machineAccess
76+
val processor = object : GestureProcessor<CG, CU> {
77+
override fun <G : CG> process(key: MachineKey<G, out CU>, gesture: G) {
78+
access.process(key, gesture)
8179
}
8280
}
8381
mapGesture(gesture, processor)
@@ -88,13 +86,13 @@ abstract class MultiMachineState<PG: Any, PU: Any> : CommonMachineState<PG, PU>(
8886
* @param parent Parent gesture
8987
* @param processor Use it to send child gesture to the relevant child machine
9088
*/
91-
protected abstract fun mapGesture(parent: PG, processor: GestureProcessor)
89+
protected abstract fun mapGesture(parent: PG, processor: GestureProcessor<CG, CU>)
9290

9391
/**
9492
* Maps combined child UI state to parent
9593
* @param provider Provides child UI states
9694
* @param changedKey Key of machine that changed the UI state. Null if called explicitly via [updateUi]
9795
* @see updateUi
9896
*/
99-
protected abstract fun mapUiState(provider: UiStateProvider, changedKey: MachineKey<*, *>?): PU
97+
protected abstract fun mapUiState(provider: UiStateProvider<CU>, changedKey: MachineKey<*, out CU>?): PU
10098
}

0 commit comments

Comments
 (0)