{"id":13428324,"url":"https://github.com/Tinder/StateMachine","last_synced_at":"2025-03-16T01:32:19.590Z","repository":{"id":37663125,"uuid":"135768962","full_name":"Tinder/StateMachine","owner":"Tinder","description":"A Kotlin and Swift DSL for finite state machine","archived":false,"fork":false,"pushed_at":"2024-07-22T04:05:41.000Z","size":238,"stargazers_count":2008,"open_issues_count":30,"forks_count":130,"subscribers_count":34,"default_branch":"main","last_synced_at":"2025-03-13T14:07:08.311Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Tinder.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-06-01T22:33:51.000Z","updated_at":"2025-03-08T15:09:20.000Z","dependencies_parsed_at":"2022-07-09T10:16:21.902Z","dependency_job_id":"9dcb7bc1-4ce0-4eed-874a-6a451d25c69a","html_url":"https://github.com/Tinder/StateMachine","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tinder%2FStateMachine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tinder%2FStateMachine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tinder%2FStateMachine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Tinder%2FStateMachine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Tinder","download_url":"https://codeload.github.com/Tinder/StateMachine/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243814900,"owners_count":20352037,"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":[],"created_at":"2024-07-31T01:00:52.571Z","updated_at":"2025-03-16T01:32:19.096Z","avatar_url":"https://github.com/Tinder.png","language":"Swift","readme":"# StateMachine\n\n[![Swift](https://github.com/Tinder/StateMachine/actions/workflows/swift.yml/badge.svg)](https://github.com/Tinder/StateMachine/actions/workflows/swift.yml)\n\nA state machine library in Kotlin and Swift.\n\n`StateMachine` is used in [Scarlet](https://github.com/Tinder/Scarlet)\n\n## Example State Diagram\n\nThe examples below create a `StateMachine` from the following state diagram for matter:\n\n\u003cimg src=\"./example/activity-diagram.png\" width=\"378\" /\u003e\n\n## Kotlin Usage\n\nDefine states, events and side effects:\n\n```kotlin\nsealed class State {\n    object Solid : State()\n    object Liquid : State()\n    object Gas : State()\n}\n\nsealed class Event {\n    object OnMelted : Event()\n    object OnFroze : Event()\n    object OnVaporized : Event()\n    object OnCondensed : Event()\n}\n\nsealed class SideEffect {\n    object LogMelted : SideEffect()\n    object LogFrozen : SideEffect()\n    object LogVaporized : SideEffect()\n    object LogCondensed : SideEffect()\n}\n```\n\nInitialize state machine and declare state transitions:\n\n```kotlin\nval stateMachine = StateMachine.create\u003cState, Event, SideEffect\u003e {\n    initialState(State.Solid)\n    state\u003cState.Solid\u003e {\n        on\u003cEvent.OnMelted\u003e {\n            transitionTo(State.Liquid, SideEffect.LogMelted)\n        }\n    }\n    state\u003cState.Liquid\u003e {\n        on\u003cEvent.OnFroze\u003e {\n            transitionTo(State.Solid, SideEffect.LogFrozen)\n        }\n        on\u003cEvent.OnVaporized\u003e {\n            transitionTo(State.Gas, SideEffect.LogVaporized)\n        }\n    }\n    state\u003cState.Gas\u003e {\n        on\u003cEvent.OnCondensed\u003e {\n            transitionTo(State.Liquid, SideEffect.LogCondensed)\n        }\n    }\n    onTransition {\n        val validTransition = it as? StateMachine.Transition.Valid ?: return@onTransition\n        when (validTransition.sideEffect) {\n            SideEffect.LogMelted -\u003e logger.log(ON_MELTED_MESSAGE)\n            SideEffect.LogFrozen -\u003e logger.log(ON_FROZEN_MESSAGE)\n            SideEffect.LogVaporized -\u003e logger.log(ON_VAPORIZED_MESSAGE)\n            SideEffect.LogCondensed -\u003e logger.log(ON_CONDENSED_MESSAGE)\n        }\n    }\n}\n```\n\nPerform state transitions:\n\n```kotlin\nassertThat(stateMachine.state).isEqualTo(Solid)\n\n// When\nval transition = stateMachine.transition(OnMelted)\n\n// Then\nassertThat(stateMachine.state).isEqualTo(Liquid)\nassertThat(transition).isEqualTo(\n    StateMachine.Transition.Valid(Solid, OnMelted, Liquid, LogMelted)\n)\nthen(logger).should().log(ON_MELTED_MESSAGE)\n```\n\n## Swift Usage\n\nAdopt `StateMachineBuilder` to gain access to the DSL builder methods:\n\n```swift\nclass MyExample: StateMachineBuilder {\n\n    ... state machine implementation ...\n}\n```\n\nDefine states, events and side effects:\n\n```swift\n@StateMachineHashable\nenum State {\n    case solid, liquid, gas\n}\n\n@StateMachineHashable\nenum Event {\n    case melt, freeze, vaporize, condense\n}\n\nenum SideEffect {\n    case logMelted, logFrozen, logVaporized, logCondensed\n}\n```\n\nInitialize state machine and declare state transitions:\n\n```swift\nlet stateMachine = StateMachine\u003cState, Event, SideEffect\u003e {\n    initialState(.solid)\n    state(.solid) {\n        on(.melt) {\n            transition(to: .liquid, emit: .logMelted)\n        }\n    }\n    state(.liquid) {\n        on(.freeze) {\n            transition(to: .solid, emit: .logFrozen)\n        }\n        on(.vaporize) {\n            transition(to: .gas, emit: .logVaporized)\n        }\n    }\n    state(.gas) {\n        on(.condense) {\n            transition(to: .liquid, emit: .logCondensed)\n        }\n    }\n    onTransition {\n        guard case let .success(transition) = $0, let sideEffect = transition.sideEffect else { return }\n        switch sideEffect {\n        case .logMelted: logger.log(Message.melted)\n        case .logFrozen: logger.log(Message.frozen)\n        case .logVaporized: logger.log(Message.vaporized)\n        case .logCondensed: logger.log(Message.condensed)\n        }\n    }\n}\n```\n\nPerform state transitions:\n\n```swift\nexpect(stateMachine.state).to(equal(.solid))\n\n// When\nlet transition = try stateMachine.transition(.melt)\n\n// Then\nexpect(stateMachine.state).to(equal(.liquid))\nexpect(transition).to(equal(\n    StateMachine.Transition.Valid(fromState: .solid, event: .melt, toState: .liquid, sideEffect: .logMelted))\n)\nexpect(logger).to(log(Message.melted))\n```\n\n#### Pre-Swift 5.9 Compatibility\n\n\u003cdetails\u003e\n\n\u003csummary\u003eExpand\u003c/summary\u003e\n\n\u003cbr\u003e\n\nThis information is only applicable to Swift versions older than `5.9`:\n\n\u003e ### Swift Enumerations with Associated Values\n\u003e\n\u003e Due to Swift enumerations (as opposed to sealed classes in Kotlin), any `State` or `Event` enumeration defined with associated values will require [boilerplate implementation](https://github.com/Tinder/StateMachine/blob/c5c8155d55db5799190d9a06fbc31263c76c80b6/Swift/Tests/StateMachineTests/StateMachine_Turnstile_Tests.swift#L198-L260) for `StateMachineHashable` conformance.\n\u003e\n\u003e The easiest way to create this boilerplate is by using the [Sourcery](https://github.com/krzysztofzablocki/Sourcery) Swift code generator along with the [AutoStateMachineHashable stencil template](https://github.com/Tinder/StateMachine/blob/main/Swift/Resources/AutoStateMachineHashable.stencil) provided in this repository. Once the codegen is setup and configured, adopt `AutoStateMachineHashable` instead of `StateMachineHashable` for the `State` and/or `Event` enumerations.\n\n\u003c/details\u003e\n\n## Examples\n\n#### Matter State Machine\n\n- [Kotlin](https://github.com/Tinder/StateMachine/blob/c5c8155d55db5799190d9a06fbc31263c76c80b6/src/test/kotlin/com/tinder/StateMachineTest.kt#L18-L47)\n- [Swift](https://github.com/Tinder/StateMachine/blob/c5c8155d55db5799190d9a06fbc31263c76c80b6/Swift/Tests/StateMachineTests/StateMachine_Matter_Tests.swift#L40-L69)\n\n#### Turnstile State Machine\n\n- [Kotlin](https://github.com/Tinder/StateMachine/blob/c5c8155d55db5799190d9a06fbc31263c76c80b6/src/test/kotlin/com/tinder/StateMachineTest.kt#L157-L185)\n- [Swift](https://github.com/Tinder/StateMachine/blob/c5c8155d55db5799190d9a06fbc31263c76c80b6/Swift/Tests/StateMachineTests/StateMachine_Turnstile_Tests.swift#L36-L64)\n\n## Kotlin Download\n\n`StateMachine` is available in Maven Central.\n\nSnapshots of the development version are available in [Sonatype's `snapshots` repository](https://oss.sonatype.org/content/repositories/snapshots/).\n\n### Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.tinder.statemachine\u003c/groupId\u003e\n    \u003cartifactId\u003estatemachine\u003c/artifactId\u003e\n    \u003cversion\u003e0.3.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\n\n```groovy\nimplementation 'com.tinder.statemachine:statemachine:0.3.0'\n```\n\n## Swift Installation\n\n### Swift Package Manager\n\n```\n.package(url: \"https://github.com/Tinder/StateMachine.git\", from: \"0.3.0\")\n```\n\n### Cocoapods\n\n```\npod 'StateMachine', :git =\u003e 'https://github.com/Tinder/StateMachine.git'\n```\n\n## References\n\n[A composable pattern for pure state machines with effects](https://gist.github.com/andymatuschak/d5f0a8730ad601bcccae97e8398e25b2) - [Andy Matuschak](https://gist.github.com/andymatuschak)\n\n## Visualization\n\nThanks to [@nvinayshetty](https://github.com/nvinayshetty), you can visualize your state machines right in the IDE using the [State Arts](https://github.com/nvinayshetty/StateArts) Intellij [plugin](https://plugins.jetbrains.com/plugin/12193-state-art).\n\n## License\n```\nCopyright (c) 2018, Match Group, LLC\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n    * Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n    * Neither the name of Match Group, LLC nor the names of its contributors\n      may be used to endorse or promote products derived from this software\n      without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL MATCH GROUP, LLC BE LIABLE FOR ANY\nDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n```\n","funding_links":[],"categories":["Libraries","Kotlin","\u003ca name=\"Swift\"\u003e\u003c/a\u003eSwift"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTinder%2FStateMachine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTinder%2FStateMachine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTinder%2FStateMachine/lists"}