Skip to content

Instantly share code, notes, and snippets.

@Pooh3Mobi
Last active January 19, 2019 06:58
Show Gist options
  • Save Pooh3Mobi/d19dd1b2aefc258a94016c538b625951 to your computer and use it in GitHub Desktop.
Save Pooh3Mobi/d19dd1b2aefc258a94016c538b625951 to your computer and use it in GitHub Desktop.
Learning FRP with Android + Kotlin + ViewModel + DataBing inspired The Elm Architecture
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