> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/ios/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/ios/appendix/notes/state-machine.md).

# State Machine

{% tabs %}
{% tab title="StateMachine" %}

```swift
/*
Simple State Machine in Swift
https://blog.lukaskukacka.com/swift/2018/10/08/simple-state-machine-in-swift.html
*/

public protocol StateMachine {

    /// represents a State of a state machine. 
    /// this will most likely be an enum. 
    /// it is `Hashable` so it can be stored in a `Set`
    /// of allowed state transitions.
    associatedtype State: Hashable

    /// current state of a state machine
    var state: State { get set }

    /// states transitions dictionary.
    /// contains set of allowed next states for each state.
    static var transitions: [State: Set<State>] { get }

    /// determines whether the transition to next state is allowed
    /// from current state.
    func canTransition(to nextState: State) -> Bool

    /// performs the transition of a state machine to next state.
    /// it is marked as mutating because it mutates the state.
    mutating func transition(to nextState: State)

}

// default implementation
extension StateMachine {

    // self.canTransition(to: nextState)
    func canTransition(to nextState: State) -> Bool {
        return Self.transitions[state]?.contains(nextState) ?? false
    }

    // self.transition(to: nextState)
    mutating func transition(to nextState: State) {

        guard canTransition(to: nextState) else {
            print("\n⛔ Invalid state transition: \n\t\(state) -> \(nextState)")
            return
        }
        
        print("\n\(type(of: self)) is about to change state:\n\t\(state) -> \(nextState)")
        self.state = nextState
    }

}
```

{% endtab %}

{% tab title="JiraTicket" %}

```swift
struct JiraTicket: StateMachine {

    let id: String

    init(id: String) {
        self.id = id
    }

    // `StateMachine` protocol requirements

    var state: State  = .open

    enum State: CaseIterable {
        case open
        case reopened
        case inProgress
        case resolved
        case closed
    }

    static let transitions: [State: Set<State>] = [
        .open      : [.inProgress, .resolved, .closed],
        .reopened  : [.inProgress, .resolved, .closed],
        .inProgress: [.open, .resolved],
        .resolved  : [.reopened, .closed],
        .closed    : [.reopened]
    ]

}
```

{% endtab %}

{% tab title="main" %}

```swift
var ticket = JiraTicket(id: "XX-123")

// open -> inProgress
ticket.transition(to: .inProgress)

// inProgress -> resolved
ticket.transition(to: .resolved)

// ⛔ Invalid state transition
ticket.transition(to: .inProgress)
```

{% endtab %}

{% tab title="參考" %}
{% embed url="<https://blog.lukaskukacka.com/swift/2018/10/08/simple-state-machine-in-swift.html>" %}

{% embed url="<https://github.com/ReactKit/SwiftState>" %}

{% embed url="<https://www.swiftbysundell.com/articles/modelling-state-in-swift/>" %}

{% embed url="<https://khawerkhaliq.com/blog/swift-design-patterns-state-pattern/>" %}
{% endtab %}
{% endtabs %}

{% embed url="<https://repl.it/@pegasusroe/State-Machine#main.swift>" %}
