Skip to content

Commit

Permalink
Remove feature output type. (#368)
Browse files Browse the repository at this point in the history
* Remove feature output type.

* Fix documentation.
  • Loading branch information
Laimiux authored Apr 29, 2024
1 parent 96df3e0 commit 0aa73d7
Show file tree
Hide file tree
Showing 16 changed files with 39 additions and 40 deletions.
6 changes: 3 additions & 3 deletions docs/Formula-Android.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ example, we define a `CounterFeatureFactory` which will handle `CounterKey` frag

```kotlin
class CounterFeatureFactory : FeatureFactory<Any, CounterKey> {
override fun initialize(dependencies: Any, key: CounterKey): Feature<*> {
override fun initialize(dependencies: Any, key: CounterKey): Feature {
val counterFormula = CounterFormula()
return Feature(
state = counterFormula.toObservable(),
Expand Down Expand Up @@ -125,7 +125,7 @@ data class CounterKey(
You can access the `CounterKey` within `CounterFeatureFactory`
```kotlin
class CounterFeatureFactory : FeatureFactory<Any, CounterKey> {
override fun initialize(dependencies: Any, key: CounterKey): Feature<*> {
override fun initialize(dependencies: Any, key: CounterKey): Feature {
val initialCount = key.initialCount
val counterFormula = CounterFormula(initialCount)
...
Expand Down Expand Up @@ -159,7 +159,7 @@ class CounterFeatureFactory : FeatureFactory<Dependencies, CounterKey> {
fun counterEventRouter(): CounterEventRouter
}

override fun initialize(dependencies: Dependencies, key: CounterKey): Feature<*> {
override fun initialize(dependencies: Dependencies, key: CounterKey): Feature {
val counterEventRouter = dependencies.counterEventRouter()
// We can pass the event router to the counter formula.
val counterFormula = CounterFormula(counterEventRouter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class FragmentLifecycleTest {
},
contracts = {
val featureFactory = object : FeatureFactory<Unit, TestLifecycleKey> {
override fun initialize(dependencies: Unit, key: TestLifecycleKey): Feature<*> {
override fun initialize(dependencies: Unit, key: TestLifecycleKey): Feature {
return Feature(
state = Observable.empty(),
viewFactory = ViewFactory.fromLayout(R.layout.test_empty_layout) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import io.reactivex.rxjava3.core.Observable
class TestFeatureFactory<Key : FragmentKey>(
private val state: (Key) -> Observable<Any>
) : FeatureFactory<Unit, Key> {
override fun initialize(dependencies: Unit, key: Key): Feature<*> {
override fun initialize(dependencies: Unit, key: Key): Feature {
return Feature(
state = state(key),
viewFactory = ViewFactory.fromLayout(R.layout.test_empty_layout) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,27 @@ import io.reactivex.rxjava3.core.Observable
/**
* Feature is based on uni-directional state management where a single state model drives
* the view rendering logic. It is used to support [FormulaFragment] by providing [ViewFactory]
* and the [state] observable.
* and the [stateObservable] observable.
*
* To define a feature, we need to create a [FeatureFactory] for a specific [FragmentKey] type
* and [bind][FragmentStoreBuilder.bind] it to the [FragmentFlowStore].
*
* Take a look at [FeatureFactory] for more information.
*/
class Feature<RenderModel : Any>(
val state: Observable<RenderModel>,
val viewFactory: ViewFactory<RenderModel>
)
class Feature private constructor(
val stateObservable: Observable<Any>,
val viewFactory: ViewFactory<Any>
) {
companion object {
@Suppress("UNCHECKED_CAST")
operator fun <RenderModel : Any> invoke(
state: Observable<RenderModel>,
viewFactory: ViewFactory<RenderModel>
): Feature {
return Feature(
stateObservable = state as Observable<Any>,
viewFactory = viewFactory as ViewFactory<Any>,
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.instacart.formula.android

sealed class FeatureEvent {
data class Init(override val id: FragmentId, val feature: Feature<*>): FeatureEvent()
data class Init(override val id: FragmentId, val feature: Feature): FeatureEvent()
data class Failure(override val id: FragmentId, val error: Throwable): FeatureEvent()
data class MissingBinding(override val id: FragmentId): FeatureEvent()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ package com.instacart.formula.android
* fun taskListInput(): TaskListFormula.Input
* }
*
* override fun initialize(dependencies: Dependencies, key: TaskListKey): Feature<*> {
* override fun initialize(dependencies: Dependencies, key: TaskListKey): Feature {
* // Note: we could create our own internal dagger component here using the dependencies.
* val formula = TaskListFormula(dependencies.taskRepo())
* return Feature(
Expand Down Expand Up @@ -40,5 +40,5 @@ interface FeatureFactory<in Dependencies, in Key : FragmentKey> {
/**
* Initializes state observable and a view factory for a specific [key].
*/
fun initialize(dependencies: Dependencies, key: Key): Feature<*>
fun initialize(dependencies: Dependencies, key: Key): Feature
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ data class FragmentEnvironment(
factory: FeatureFactory<DependenciesT, KeyT>,
dependencies: DependenciesT,
key: KeyT,
): Feature<*> {
): Feature {
return factory.initialize(dependencies, key)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ class FragmentStoreBuilder<Component> {
* @param featureFactory Feature factory that provides state observable and view rendering logic.
*/
inline fun <reified Key: FragmentKey> bind(
crossinline initFeature: (Component, Key) -> Feature<*>,
crossinline initFeature: (Component, Key) -> Feature,
) = apply {
val factory = object : FeatureFactory<Component, Key> {
override fun initialize(dependencies: Component, key: Key): Feature<*> {
override fun initialize(dependencies: Component, key: Key): Feature {
return initFeature(dependencies, key)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import io.reactivex.rxjava3.core.Observable
class FeatureObservableAction(
private val fragmentEnvironment: FragmentEnvironment,
private val fragmentId: FragmentId,
private val feature: Feature<*>,
private val feature: Feature,
) : Action<Any> {

override fun key(): Any = fragmentId

override fun start(send: (Any) -> Unit): Cancelable {
val observable = feature.state.onErrorResumeNext {
val observable = feature.stateObservable.onErrorResumeNext {
fragmentEnvironment.onScreenError(fragmentId.key, it)
Observable.empty()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ internal class FormulaFragmentViewFactory(

private var factory: ViewFactory<Any>? = null

@Suppress("UNCHECKED_CAST")
override fun create(inflater: LayoutInflater, container: ViewGroup?): FeatureView<Any> {
val key = fragmentId.key
val featureEvent = featureProvider.getFeature(fragmentId) ?: throw IllegalStateException("Could not find feature for $key.")
Expand All @@ -29,7 +28,7 @@ internal class FormulaFragmentViewFactory(
throw IllegalStateException("Feature failed to initialize: $key", featureEvent.error)
}
is FeatureEvent.Init -> {
featureEvent.feature.viewFactory as ViewFactory<Any>
featureEvent.feature.viewFactory
}
}
this.factory = viewFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class FragmentFlowStoreTest {

@Test fun `bind feature factory with to dependencies defined`() {
val myFeatureFactory = object : FeatureFactory<String, MainKey> {
override fun initialize(dependencies: String, key: MainKey): Feature<*> {
override fun initialize(dependencies: String, key: MainKey): Feature {
return TestUtils.feature(
stateValue = dependencies
)
Expand Down Expand Up @@ -268,7 +268,7 @@ class FragmentFlowStoreTest {
private fun FragmentKey.asRemovedEvent() = FragmentLifecycleEvent.Removed(FragmentId("", this))

class TestFeatureFactory<FragmentKeyT : FragmentKey>: FeatureFactory<FakeComponent, FragmentKeyT> {
override fun initialize(dependencies: FakeComponent, key: FragmentKeyT): Feature<*> {
override fun initialize(dependencies: FakeComponent, key: FragmentKeyT): Feature {
return Feature(
state = dependencies.state(key),
viewFactory = NoOpViewFactory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,10 @@ import com.instacart.formula.android.fakes.NoOpViewFactory
import io.reactivex.rxjava3.core.Observable

object TestUtils {
fun <Value : Any> feature(stateValue: Value): Feature<Value> {
fun <Value : Any> feature(stateValue: Value): Feature {
return Feature(
state = Observable.just(stateValue),
viewFactory = NoOpViewFactory()
)
}

fun <Dependencies, Key : FragmentKey> featureFactory(
init: (Dependencies, Key) -> Observable<Any>
): FeatureFactory<Dependencies, Key> {
return object : FeatureFactory<Dependencies, Key> {
override fun initialize(dependencies: Dependencies, key: Key): Feature<*> {
return Feature(
state = init(dependencies, key),
viewFactory = NoOpViewFactory()
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class FakeAuthFlowFactory : FlowFactory<FakeComponent, FakeAuthFlowFactory.Compo
}

class TestFeatureFactory<FragmentKeyT : FragmentKey> : FeatureFactory<Component, FragmentKeyT> {
override fun initialize(dependencies: Component, key: FragmentKeyT): Feature<Any> {
override fun initialize(dependencies: Component, key: FragmentKeyT): Feature {
dependencies.onInitialized(dependencies, key)
return Feature(
state = Observable.empty(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import io.reactivex.rxjava3.core.Observable

class NoOpFeatureFactory<FragmentKeyT : FragmentKey> : FeatureFactory<Unit, FragmentKeyT> {

override fun initialize(dependencies: Unit, key: FragmentKeyT): Feature<Any> {
override fun initialize(dependencies: Unit, key: FragmentKeyT): Feature {
return Feature(
state = Observable.empty(),
viewFactory = NoOpViewFactory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import com.instacart.formula.invoke
import com.instacart.formula.rxjava3.toObservable

class StopwatchFeatureFactory : FeatureFactory<Any, StopwatchKey> {
override fun initialize(dependencies: Any, key: StopwatchKey): Feature<*> {
override fun initialize(dependencies: Any, key: StopwatchKey): Feature {
return Feature(
state = StopwatchFormula().toObservable(),
viewFactory = StopwatchViewFactory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class TaskListFeatureFactory : FeatureFactory<TaskListFeatureFactory.Dependencie
fun taskListInput(): TaskListFormula.Input
}

override fun initialize(dependencies: Dependencies, key: TaskListKey): Feature<*> {
override fun initialize(dependencies: Dependencies, key: TaskListKey): Feature {
// Note: we could create our own internal dagger component here using the dependencies.
val formula = TaskListFormula(dependencies.taskRepo())
return Feature(
Expand Down

0 comments on commit 0aa73d7

Please sign in to comment.