Last active
January 19, 2019 06:58
-
-
Save Pooh3Mobi/d19dd1b2aefc258a94016c538b625951 to your computer and use it in GitHub Desktop.
Learning FRP with Android + Kotlin + ViewModel + DataBing inspired The Elm Architecture
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import androidx.lifecycle.LiveData | |
import androidx.lifecycle.ViewModel | |
import io.reactivex.Flowable | |
import io.reactivex.processors.BehaviorProcessor | |
import io.reactivex.schedulers.Schedulers | |
// | |
// In/Out/State | |
// | |
sealed class Msg { | |
object Submit : Msg() | |
data class GotResult(val result: Result) : Msg() | |
} | |
enum class Cmd { | |
FETCH_DATA, NONE | |
} | |
sealed class Model { | |
object Nothing : Model() | |
object Loading : Model() | |
} | |
sealed class Result : Model() { | |
object Success : Result() | |
object Failure : Result() | |
} | |
fun Flowable<Pair<Model, Cmd>>.setCommand(pair: Pair<Cmd, () -> Msg>, event: BehaviorProcessor<Msg>) { | |
this.filter { it.second == pair.first }.map { pair.second() }.subscribe(event) | |
} | |
// | |
// Boundary Ports | |
// | |
data class LongInputs( | |
val event: BehaviorProcessor<Msg> | |
) : Inputs | |
data class LongOutputs( | |
val submitEnable: Flowable<Boolean>, | |
val isLoading: Flowable<Boolean>, | |
val submitResult: Flowable<Result> | |
) : Outputs | |
interface LongProcessor : Processor<LongInputs, LongOutputs> | |
// | |
// Controller | |
// | |
@Suppress("MoveLambdaOutsideParentheses") | |
class LongProcessViewModel : ViewModel(), LongProcessor { | |
val api: () -> Result by lazy { { Result.Success.also { Thread.sleep(3000) } } } | |
// input event | |
val event = behaviorProcessor<Msg>() | |
// observe state | |
val submitEnable: LiveData<Boolean> | |
val submitResult: LiveData<Result> | |
val isLoading: LiveData<Boolean> | |
init { | |
// input port to output port | |
val outputs = create(LongInputs(event = event)) | |
submitEnable = outputs.submitEnable.toLiveData() | |
submitResult = outputs.submitResult.toLiveData() | |
isLoading = outputs.isLoading.toLiveData() | |
} | |
override fun create(inputs: LongInputs): LongOutputs { | |
val event = inputs.event | |
// observeOn for concuurent | |
val state = event.observeOn(Schedulers.io()) | |
.update<Pair<Model, Cmd>, Msg>( | |
initialValue = Model.Nothing to Cmd.NONE, | |
accumulator = { _, msg -> | |
when (msg) { | |
// Msg -> (Model, Cmd) | |
is Msg.Submit -> Model.Loading to Cmd.FETCH_DATA | |
is Msg.GotResult -> msg.result to Cmd.NONE | |
} | |
} | |
) | |
val fetchData: Pair<Cmd, () -> Msg> = Cmd.FETCH_DATA to { Msg.GotResult(api()) } // concurrency process | |
state.setCommand(fetchData, event) | |
return LongOutputs( | |
submitEnable = state.map { it.first != Model.Loading }, | |
submitResult = state.filter { it.first is Result }.map { it.first as Result }, | |
isLoading = state.map { it.first == Model.Loading } | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment