Skip to content

ReactKit/SwiftState

Repository files navigation

SwiftState Circle CI

Elegant state machine for Swift.

SwiftState

Example

enum MyState: StateType {
    case State0, State1, State2
}
// setup state machine
let machine = Machine<MyState, NoEvent>(state: .State0) { machine in

    machine.addRoute(.State0 => .State1)
    machine.addRoute(.Any => .State2) { context in print("Any => 2, msg=\(context.userInfo)") }
    machine.addRoute(.State2 => .Any) { context in print("2 => Any, msg=\(context.userInfo)") }

    // add handler (`context = (event, fromState, toState, userInfo)`)
    machine.addHandler(.State0 => .State1) { context in
        print("0 => 1")
    }

    // add errorHandler
    machine.addErrorHandler { event, fromState, toState, userInfo in
        print("[ERROR] \(transition.fromState) => \(transition.toState)")
    }
}

// initial
XCTAssertTrue(machine.state == .State0)
        
// tryState 0 => 1 => 2 => 1 => 0

machine <- .State1
XCTAssertTrue(machine.state == .State1)
        
machine <- (.State2, "Hello")
XCTAssertTrue(machine.state == .State2)
        
machine <- (.State1, "Bye")
XCTAssertTrue(machine.state == .State1)
        
machine <- .State0  // fail: no 1 => 0
XCTAssertTrue(machine.state == .State1)

This will print:

0 => 1
Any => 2, msg=Optional("Hello")
2 => Any, msg=Optional("Bye")
[ERROR] State1 => State0

Transition by Event

Use <-! operator to try transition by Event rather than specifying target State (Test Case).

enum MyEvent: EventType {
    case Event0, Event1
}
let machine = StateMachine<MyState, MyEvent>(state: .State0) { machine in
        
    // add 0 => 1 => 2
    machine.addRoute(event: .Event0, transitions: [
        .State0 => .State1,
        .State1 => .State2,
    ])
}
   
// tryEvent
machine <-! .Event0
XCTAssertEqual(machine.state, MyState.State1)

// tryEvent
machine <-! .Event0
XCTAssertEqual(machine.state, MyState.State2)

// tryEvent
machine <-! .Event0
XCTAssertEqual(machine.state, MyState.State2, "Event0 doesn't have 2 => Any")

If there is no Event-based transition, use built-in NoEvent instead.

For more examples, please see XCTest cases.

Features

  • Easy Swift syntax
    • Transition: .State0 => .State1, [.State0, .State1] => .State2
    • Try transition: machine <- .State1
    • Try transition + messaging: machine <- (.State1, "GoGoGo")
    • Try event: machine <-! .Event1
  • Highly flexible transition routing
    • Using Condition
    • Using .Any state/event
    • Blacklisting: .Any => .Any + Condition
    • Route Mapping (closure-based routing): #36
  • Success/Error/Entry/Exit handlers with order: UInt8 (more flexible than before/after handlers)
  • Removable routes and handlers
  • Chaining: .State0 => .State1 => .State2
  • Hierarchical State Machine: #10

Terms

Term Type Description
State StateType (protocol) Mostly enum, describing each state e.g. .State0.
Event EventType (protocol) Name for route-group. Transition can be fired via Event instead of explicitly targeting next State.
State Machine Machine State transition manager which can register Route/RouteMapping and Handler separately for variety of transitions.
Transition Transition From- and to- states represented as .State1 => .State2. Also, .Any can be used to represent any state.
Route Route Transition + Condition.
Condition Context -> Bool Closure for validating transition. If condition returns false, transition will fail and associated handlers will not be invoked.
Event Route Mapping (event: E?, fromState: S, userInfo: Any?) -> S? Another way of defining routes using closure instead of transition arrows (=>). This is useful when state & event are enum with associated values. Return value (S?) means preferred-toState, where passing nil means no routes available. See #36 for more info.
State Route Mapping (fromState: S, userInfo: Any?) -> [S]? Another way of defining routes using closure instead of transition arrows (=>). This is useful when state is enum with associated values. Return value ([S]?) means multiple toStates from single fromState. See #36 for more info.
Handler Context -> Void Transition callback invoked when state has been changed successfully.
Context (event: E?, fromState: S, toState: S, userInfo: Any?) Closure argument for Condition & Handler.
Chain TransitionChain / RouteChain Group of continuous routes represented as .State1 => .State2 => .State3

Related Articles

  1. Swiftで有限オートマトン(ステートマシン)を作る - Qiita (Japanese)
  2. Swift+有限オートマトンでPromiseを拡張する - Qiita (Japanese)

Licence

MIT

About

Elegant state machine for Swift.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 15