{"id":15040938,"url":"https://github.com/ensody/reactivestate-kotlin","last_synced_at":"2025-04-09T23:15:01.838Z","repository":{"id":45187159,"uuid":"246103397","full_name":"ensody/ReactiveState-Kotlin","owner":"ensody","description":"Easy reactive state management and ViewModels for Kotlin Multiplatform. No boilerplate. Compatible with Android.","archived":false,"fork":false,"pushed_at":"2025-03-31T08:47:46.000Z","size":1780,"stargazers_count":41,"open_issues_count":2,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-09T23:14:55.699Z","etag":null,"topics":["android","android-library","coroutines","demand-driven","demand-driven-programming","jetpack-compose","kotlin","kotlin-android","kotlin-multiplatform","kotlin-multiplatform-library","kotlin-mvvm","lifecycle","lifecycle-handling","mobile-development","mobx","reactive","reactive-programming","state-management","stateflow","viewmodel"],"latest_commit_sha":null,"homepage":"https://ensody.github.io/ReactiveState-Kotlin/","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ensody.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-09T17:46:35.000Z","updated_at":"2025-03-31T08:47:49.000Z","dependencies_parsed_at":"2023-02-12T23:31:11.171Z","dependency_job_id":"6e5b9510-5e5e-4641-ba8a-110619079e29","html_url":"https://github.com/ensody/ReactiveState-Kotlin","commit_stats":{"total_commits":277,"total_committers":5,"mean_commits":55.4,"dds":"0.46570397111913353","last_synced_commit":"9b86ddfbdaa9b69110d23110679e3082bc2bfa99"},"previous_names":[],"tags_count":84,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ensody%2FReactiveState-Kotlin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ensody%2FReactiveState-Kotlin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ensody%2FReactiveState-Kotlin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ensody%2FReactiveState-Kotlin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ensody","download_url":"https://codeload.github.com/ensody/ReactiveState-Kotlin/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248125591,"owners_count":21051770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","android-library","coroutines","demand-driven","demand-driven-programming","jetpack-compose","kotlin","kotlin-android","kotlin-multiplatform","kotlin-multiplatform-library","kotlin-mvvm","lifecycle","lifecycle-handling","mobile-development","mobx","reactive","reactive-programming","state-management","stateflow","viewmodel"],"created_at":"2024-09-24T20:45:18.461Z","updated_at":"2025-04-09T23:15:01.810Z","avatar_url":"https://github.com/ensody.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ReactiveState for Kotlin Multiplatform and Android\n\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.ensody.reactivestate/reactivestate/badge.svg?gav=true)](https://maven-badges.herokuapp.com/maven-central/com.ensody.reactivestate/reactivestate?gav=true)\n\nEasy reactive state management and ViewModels for Kotlin Multiplatform. No boilerplate. Compatible with Android.\n\nReactiveState-Kotlin provides these foundations for building multiplatform ViewModels and lower-level logic:\n\n* [reactive programming](https://ensody.github.io/ReactiveState-Kotlin/reactive-programming/): everything is recomputed/updated automatically based on straightforward code\n* [demand-driven programming](https://ensody.github.io/ReactiveState-Kotlin/demand-driven-programming/): resource-consuming computations and values are allocated on-demand and disposed when not needed\n* [multiplatform](https://ensody.github.io/ReactiveState-Kotlin/multiplatform-viewmodels/): share your ViewModels and reactive state handling logic between all platforms\n* [event handling](https://ensody.github.io/ReactiveState-Kotlin/event-handling/): simple events based on interfaces (more composable and less boilerplate than sealed classes)\n* [automatic error catching](https://ensody.github.io/ReactiveState-Kotlin/error-handling/): no more forgotten try-catch or copy-pasted error handling logic all over the place\n* [coroutine-based unit tests](https://ensody.github.io/ReactiveState-Kotlin/unit-testing-coroutines/): worry no more about passing around `CoroutineDispatcher`s everywhere\n* [lifecycle handling](https://ensody.github.io/ReactiveState-Kotlin/lifecycle-handling/)\n* [state restoration](https://ensody.github.io/ReactiveState-Kotlin/state-restoration/)\n\nSee the [ReactiveState documentation](https://ensody.github.io/ReactiveState-Kotlin/) for more details.\n\n## Supported platforms\n\nandroid, jvm, ios, tvos, watchos, macosArm64, macosX64, mingwX64, linuxX64\n\n## Installation\n\nAdd the package to your `build.gradle`'s `dependencies {}`:\n\n```groovy\ndependencies {\n    // Add the BOM using the desired ReactiveState version\n    api platform(\"com.ensody.reactivestate:reactivestate-bom:VERSION\")\n\n    // Leave out the version number from now on:\n    implementation \"com.ensody.reactivestate:reactivestate\"\n\n    // Utils for unit tests that want to use coroutines\n    implementation \"com.ensody.reactivestate:reactivestate-test\"\n    // Note: kotlin-coroutines-test only supports the \"jvm\" target,\n    // so reactivestate-test has the same limitation\n}\n```\n\nAlso, make sure you've integrated the Maven Central repo, e.g. in your root `build.gradle`:\n\n```groovy\nsubprojects {\n    repositories {\n        // ...\n        mavenCentral()\n        // ...\n    }\n}\n```\n\n## Quick intro\n\nThe following two principles are here to give you a quick idea of the reactive programming aspect only.\nThe \"Guide\" section in the [documentation](https://ensody.github.io/ReactiveState-Kotlin/) describes how to work with the more advanced aspects like multiplatform ViewModels, lifecycle handling, etc.\n\nNote: While the discussion is about `StateFlow`, you can also use `LiveData` or even implement extensions for other observable values.\n\n### Observing StateFlow\n\nImagine you have an input form with first and last name and want to observe two `StateFlow` values at the same time:\n\n* `isFirstNameValid: StateFlow\u003cBoolean\u003e`\n* `isLastNameValid: StateFlow\u003cBoolean\u003e`\n\nThis is how you'd do it by using the `autoRun` function:\n\n```kotlin\nautoRun {\n    submitButton.isEnabled = get(isFirstNameValid) \u0026\u0026 get(isLastNameValid)\n}\n```\n\nWith `get(isFirstNameValid)` you retrieve `isFirstNameValid.value` and at the same time tell `autoRun` to re-execute the block whenever the value is changed.\nThat code is similar to writing this:\n\n```kotlin\nlifecycleScope.launchWhenStarted {\n    isFirstNameValid\n        .combine(isLastNameValid) { firstNameValid, lastNameValid -\u003e\n            firstNameValid to lastNameValid\n        }\n        .conflate()\n        .collect { (firstNameValid, lastNameValid) -\u003e\n            try {\n                submitButton.isEnabled = firstNameValid \u0026\u0026 lastNameValid\n            } catch (e: CancellationException) {\n                throw e\n            } catch (e: Throwable) {\n                onError(e)\n            }\n        }\n}\n```\n\n### Reactive StateFlow / reactive data\n\nThe same principle can be used to create a `derived`, reactive `StateFlow`:\n\n```kotlin\nval isFormValid: StateFlow\u003cBoolean\u003e = derived {\n    get(isFirstNameValid) \u0026\u0026 get(isLastNameValid)\n}\n```\n\nNow you can use `autoRun { submitButton.isEnabled = get(isFormValid) }` in the rest of your code.\n\nGoing even further, `isFirstNameValid` itself would usually also be the result of a `derived` computation.\nSo, you can have multiple layers of reactive `derived` `StateFlow`s.\n\n## Relation to Jetpack Compose / Flutter / React\n\nReactive UI frameworks like Jetpack Compose automatically rebuild the UI whenever e.g. a `StateFlow` changes.\nSo, in the UI layer `autoRun` can usually be replaced with a `Composable`.\n\nHowever, below the UI your data still needs to be reactive, too.\nHere ReactiveState provides `derived` to automatically recompute a `StateFlow` based on other `StateFlow`s.\nThis pattern is very useful in practice and provides the perfect foundation for frameworks like Jetpack Compose which primarily focus on the UI aspect.\nReactiveState's `derived` and `autoRun` provide the same reactivity for your data and business logic.\n\nIn Jetpack Compose you even have `derivedStateOf` which is very similar to `derived`.\nSo, you can choose whether you want to build your business logic based on the official coroutines library (`StateFlow`/`derived`) or Jetpack Compose (`State`/`derivedStateOf`). However, the coroutines library has the advantage that it's available for more platforms and it's fully independent of any UI frameworks. Finally, most open-source non-UI libraries will probably be based on coroutines, so `StateFlow` based code might also be better for compatibility/interoperability.\n\nIn other words, the combination of both solutions used together results in a fully reactive, multiplatform codebase - which improves code simplicity and avoids many bugs.\n\nMoreover, Jetpack Compose currently doesn't provide any multiplatform ViewModel support or any large-scale architecture.\nSo, this library solves that by providing `BaseReactiveState` for ViewModels.\nIt also comes with a lifecycle-aware event system (`eventNotifier`) and loading state handling (so you can track one or multiple different loading indicators based on coroutines that you launch).\n\n## See also\n\nThis library is based on [reactive_state](https://github.com/ensody/reactive_state) for Flutter and adapted to Kotlin Multiplatform and Android patterns.\n\n## License\n\n```\nCopyright 2024 Ensody GmbH, Waldemar Kornewald\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fensody%2Freactivestate-kotlin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fensody%2Freactivestate-kotlin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fensody%2Freactivestate-kotlin/lists"}