{"id":16464972,"url":"https://github.com/sellmair/quantum","last_synced_at":"2025-03-21T06:31:58.549Z","repository":{"id":135056224,"uuid":"147240457","full_name":"sellmair/quantum","owner":"sellmair","description":"State management library for Android","archived":false,"fork":false,"pushed_at":"2019-02-03T14:02:58.000Z","size":1178,"stargazers_count":74,"open_issues_count":1,"forks_count":4,"subscribers_count":6,"default_branch":"develop","last_synced_at":"2025-03-01T03:42:10.480Z","etag":null,"topics":["android","android-library","kotlin","kotlin-android","library","quantum","state","state-management"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sellmair.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2018-09-03T18:32:04.000Z","updated_at":"2024-10-01T17:49:25.000Z","dependencies_parsed_at":"2023-03-13T11:05:27.009Z","dependency_job_id":null,"html_url":"https://github.com/sellmair/quantum","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sellmair%2Fquantum","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sellmair%2Fquantum/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sellmair%2Fquantum/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sellmair%2Fquantum/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sellmair","download_url":"https://codeload.github.com/sellmair/quantum/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244117640,"owners_count":20400743,"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","kotlin","kotlin-android","library","quantum","state","state-management"],"created_at":"2024-10-11T11:31:03.899Z","updated_at":"2025-03-21T06:31:58.191Z","avatar_url":"https://github.com/sellmair.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"left\"\u003e\n  \u003cimg width=\"512\" src=\"https://github.com/sellmair/quantum/blob/develop/etc/logo/big.png?raw=true\"\u003e\u003cbr\u003e\n\u003c/p\u003e\n\n## State management library for Android\n![GitHub top language](https://img.shields.io/github/languages/top/sellmair/quantum.svg)\n[![Build Status](https://travis-ci.org/sellmair/quantum.svg?branch=develop)](https://travis-ci.org/sellmair/quantum)\n![Bintray](https://img.shields.io/bintray/v/sellmair/sellmair/quantum.svg)\n\n\n ## What is it\n\n Quantum is a general purpose state management library designed for building easy, stable and thread safe\n Android applications. It was inspired by [AirBnb's MvRx](https://github.com/airbnb/MvRx) and tailored\n for building reliable ViewModels.\n \n \u003cp align=\"center\"\u003e\n   \u003cimg src=\"https://github.com/sellmair/quantum/blob/develop/etc/illustration.jpeg?raw=true\"\u003e\u003cbr\u003e\n \u003c/p\u003e\n\n\n## Usage\n\n##### gradle\n```groovy\n\ndependencies { \n    implementation \"io.sellmair:quantum:1.0.0\"\n    \n    // optional rx extensions\n    implementation \"io.sellmair:quantum-rx:1.0.0\"\n    \n    // optional LiveData extensions\n    implementation \"io.sellmair:quantum-livedata:1.0.0\"\n}\n```\n\n\n##### Define a State\nStates should always be immutable. I highly recommend using \nkotlin data classes to make immutability easy 👍 \n\n\nExample:\n\n```kotlin\ndata class MyState(\n    val isLoading: Boolean = false, \n    val error: Error? = null,\n    val content: Content? = null, \n    val userLocation: Location? = null)\n```\n\n##### Create a Quantum\nA Quantum is the owner of your state. It applies all reducers, \ninvokes actions and publishes new states.\n\nExample:\n\n\n```kotlin\n// Create a new Quantum with initial state. \nval quantum = Quantum.create(MyState())\n```\n\n\n##### Enqueue a Reducer\nReducers are functions that take the current state and create a new state. \nReducers will always be called by a internal thread of the `Quantum`. \nOnly one reducer will run at a time!\nReducers are allowed to return the same (untouched) instance to signal a no-operation.\n\n###### Example (simple reducer): \nA simple reducer that that says hello to a certain user. \n\n```kotlin\ndata class SimpleState(val name: String, val message: String = \"\" )\n\nval quantum = Quantum.create(SimpleState(\"Julian\"))\n\nfun sayHello() = quantum.setState {\n    copy(message = \"Hello $name\")\n}\n\n```\n\nUnlike other \"State Owner\" concepts, Quantum allows reducers to dispatch async operations.\nThis decision was made to give developers the option to handle side-effects \ninside a safer environment. \n\n\n###### Example (load content): \nMuch more complicated reducer problem:  \u003cbr\u003e\nWe want to \n- Load content from repository asyncronously\n- Ensure that only one loading operation is running at a time\n- Publish the content when fetched successfully\n- Publish the error when an error occurred \n\n```kotlin\n// Reducer that fetches the content (if not currently loading)\nfun loadContent() = quantum.setState {\n    // Do not try to load the content while currently loading\n    // Returning this (the current / input state) signals the quantum that \n    // this reducer was a NOOP\n    if(isLoading) return@setState this\n    \n    // Dispatch a async loading operation with myRepository (exemplary)\n    myRepository.loadContent\n        .onSuccess(::onContentLoaded)\n        .onError(::onError)\n        .execute()\n        \n    // Copy the current state but set loading flag  \n    copy(isLoading = true)\n    \n}\n\nfun onContentLoaded(content: Content) = setState {\n    // Content loaded: \n    // Copy current state and clear any error\n    copy(content = content, error = null, isLoading = false)\n}\n\nfun onError(error: Error) = setState {\n    // Copy current state but publish the error\n    copy(error = error, isLoading = false)\n}\n```\n\n\n##### Enqueue an Action\nActions are parts of your code that require the most recent state, but do not intend to change it. \nActions will always be called by a internal thread of the `Quantum` and run after\nall reducers are applied.\n\n\n```kotlin\nval quantum = Quantum.create(SimpleState(name = \"Balazs\"))\n\nquantum.setState {\n    copy(name = \"Paul\")\n}\n\nquantum.withState {\n    // will print 'Hello Paul'\n    Log.i(\"Readme\", \"Hello $name\")\n}\n```\n\n\n##### Listen for changes\nListeners are invoked by Android's main thread by default. \nIt is possible to configure the thread which invokes listeners by specifying an Executor.\n\n###### Example: Without Extensions, Rare\n\n```kotlin\nquantum.addStateListener { state -\u003e print(state.message) }\n```\n\n###### Example: Without Extensions, Function\n\n```kotlin\nfun onState(state: SimpleState){\n  // be awesome\n}\n\nfun onStart() {\n    quantum.addListener(::onState)\n}\n\nfun onStop() {\n    quantum.removeListener(::onState)\n}\n```\n\n\n###### Example: Rx (recommended)\n\n```kotlin\nfun onStart() {\n    quantum.rx.subscribe { state -\u003e /* be awesome */ }\n}\n```\n\n\n##### Nested Quantum / Map\n\nIt is possible to map a Quantum to create a 'Child-Quantum' which can enqueue reducers and actions\nas usual. The state of this child will be in sync with the parent Quantum.\n\n###### Example: Child\n\n```kotlin\ndata class ChildState(val name: String, val age: Int)\n\ndata class ParentState(val name: String, val age: Int, val children: List\u003cChildState\u003e)\n\n// Get the quantum instance of the parent state\nval parentQuantum: Quantum\u003cParentState\u003e =  /* ... */\n\n// Create the child state\nval childQuantum = parentQuantum\n    .map { parentState -\u003e  parentState.children }\n    .connect { parentState, children -\u003e parentState.copy(children = children) }\n\n// Increase the age of all children\nchildQuantum.setState { children -\u003e\n     children.map { child -\u003e child.copy(age=child.age++) }\n}\n```\n\n##### Debugging\n\n###### History\nIt is possible to record all states created in a `Quantum`. \n\n```kotlin\nval quantum = Quantum.create(MyState()).apply { \n    history.enabled = true\n}\n\nfun debug(){\n   for(state in quantum.history){\n       print(state)\n   }\n}\n```\n\n\n##### Quitting\nA `Quantum` has to be stopped if it's no longer needed, in order to stop the internal background \nthread and release all resources.\n\n```kotlin\nquantum.quit() // will quit as fast as possible\nquantum.quitSafely() // will quit after all currently enqueued reducers / actions\n```\n\n\n##### ViewModel (Suggestion)\nI suggest having one 'ViewState' for each ViewModel. The ViewModel itself\nmight want to implement `Quantum` itself. \n\n###### Example:\n\n```kotlin\ndata class LoginState(\n    val email: String = \"\",\n    val password: String = \"\", \n    val user: User? = null)\n\nclass LoginViewModel(private val loginService: LoginService): \n    ViewModel(), \n    Quantum\u003cLoginState\u003e by Quantum.create(LoginState()) {\n   \n   fun setEmail(email: String) = setState {\n        copy(email = email)\n   }                   \n   \n   fun setPassword(password: String) = setState {\n        copy(password = password)\n   }\n   \n   fun login() = setState {\n       val user = loginService.login(email, password)\n       copy(user = user)\n   }\n   \n   override fun onCleared() {\n        // Quit the quantum\n        quit()\n   }\n}\n\n```\n\n\n#### Configuration\nIt is possible to configure the defaults of Quantum for your whole application. \nFor example: It is possible to specify the default threading mode, history settings, \nor even the thread pool that is shared for multiple Quantum instances.\n\n##### Global configuration\n```kotlin\n// configure defaults\nQuantum.configure {\n    // Quantum instances will use the given thread pool by default\n    this.threading.default.mode = Threading.Pool\n            \n    // Listeners are now invoked by a new background thread\n    this.threading.default.callbackExecutor = Executors.newSingleThreadExecutor()\n            \n    // Override the default shared thread pool\n    this.threading.pool = Executors.newCachedThreadPool()\n            \n    // Set history default to enabled with limit of 100 states\n    this.history.default.enabled = true\n    this.history.default.limit = 100\n            \n    // Get info's from quantum\n    this.logging.level = LogLevel.INFO\n}\n```\n\n##### Instance configuration\n\n```kotlin\n Quantum.create(\n        // initial state\n        initial = LoginState(), \n        \n        // invoke listeners by background thread\n        callbackExecutor = Executors.newSingleThreadExecutor(),\n        \n        // use thread pool \n        threading = Threading.Pool)\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsellmair%2Fquantum","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsellmair%2Fquantum","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsellmair%2Fquantum/lists"}