Skip to content

Yet another UDF loop implementation for KMP

License

Notifications You must be signed in to change notification settings

atomgomba/hurok

Repository files navigation

hurok

This is a framework library for developing applications on the JVM based on the unidirectional dataflow model.

flowchart LR
    A[First State] -->|Input| B{Action} -->|Mutate| C(Model) --> D[Renderer] -->|Derive| E[Next State] -.->|Input| B
    B -->|Trigger| F([Effect]) --> B
Loading

Please click here for generated documentation.

How it looks like

This brief example is missing some glue code to provide a quick feel of the library mechanics.

data class ScoreScreenModel(
    val user: User? = null,
    val isLoading: Boolean = false,
    val throwable: Throwable? = null,
)

data class ScoreScreenState(
    val playerName: String,
    val score: String,
    val isLoading: Boolean,
    val errorMessage: String?,
) : ViewState<ScoreScreenModel, ScoreScreenDependency>

@Composable
fun ScoreScreenView(args: ScoreScreenArgs?) {
    LoopView(
        builder = ScoreScreenLoop,
        args = args,
        firstAction = OnLoopStart,
    ) {
        Column {
            if (isLoading) {
                Text("Loading...")
            } else if (errorMessage != null) {
                Text(errorMessage)

                Button(onClick = { emit(OnUpdateScoreClick) }) {
                    Text("Retry")
                }
            } else {
                Text(playerName)
                Text(score)

                Button(onClick = { emit(OnUpdateScoreClick) }) {
                    Text("Update score")
                }
            }
        }
    }
}

class ScoreScreenRenderer : Renderer<ScoreScreenModel, ScoreScreenState> {
    fun renderState(model: ScoreScreenModel) = with(model) {
        ScoreScreenState(
            playerName = if (user == null) "N/A" else user.nickname,
            score = if (user == null) "N/A" else user.score.roundToInt().toString(),
            errorMessage = throwable?.run { message ?: "Unknown error" },
            isLoading = isLoading,
        )
    }
}

data object OnUpdateScoreClick : ScoreScreenAction {
    override fun ScoreScreenModel.proceed() =
        outcome(copy(isLoading = true, throwable = null), UpdateScore(user.id))
}

data class UpdateScoreSuccess(val user: User) : ScoreScreenAction {
    override fun ScoreScreenModel.proceed() =
        mutate(copy(user = user, isLoading = false))
}

data class UpdateScoreError(val throwable: Throwable) : ScoreScreenAction {
    override fun ScoreScreenModel.proceed() =
        mutate(copy(throwable = throwable, isLoading = false))
}

data class UpdateScore(val userId: String) : ScoreScreenEffect {
    override suspend fun ScoreScreenEmitter.trigger(dependency: ScoreScreenDependencv) {
        try {
            val user = scoreService.getUserScore(userId)
            emit(UpdateScoreSuccess(user))
        } catch (throwable: Throwable) {
            emit(UpdateScoreError(throwable))
        }
    }
}

Parts

Name Description
Model Holds data for business logic
ViewState UI state derived from the Model
Renderer Uses the Model to create new State for the UI
Action Mutates the Model and can trigger (any) Effect
Effect Does background work and triggers (any) Action
Loop Handles Action and Effect

Using hurok with Gradle

Packages are published to Maven Central, so make sure to add it to your list of repositories.

repositories {
    mavenCentral()
}

Kotlin DSL

dependencies {
    // core multiplatform package
    implementation("com.ekezet.hurok:base:2.0.0")
    // library for using hurok with Compose Multiplatform
    implementation("com.ekezet.hurok:compose:2.0.0")
    // library for testing hurok-based applications
    implementation("com.ekezet.hurok:test:2.0.0")
}

Version catalog

[versions]
hurok = "2.0.0

[libraries]
hurok-base = { group = "com.ekezet.hurok", name = "base", version.ref = "hurok" }
hurok-compose = { group = "com.ekezet.hurok", name = "compsoe", version.ref = "hurok" }
hurok-test = { group = "com.ekezet.hurok", name = "test", version.ref = "hurok" }

Technologies

  • Kotlin Multiplatform
  • Kotlin Coroutines
  • Compose Multiplatform
  • Android SDK

Example code

For code samples please see Othello for Android.

About

Yet another UDF loop implementation for KMP

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Languages