# Simple State Machine and Transition Table

The goal is to have well defined state transitions, and the ability to provide the next event to execute.

I'd like to know if this is a proper implementation of State Machine, considering how states and transitionTable are defined, and how I handle event as input and output via nextEvent.

In many examples I cannot clearly define verbiage for state, so I defined them as verbs (notice ing suffix), as if it represents the ongoing progress of workflow. This may be wrong..

I also defined multiple events that could occur for a single state. For example, GetDeviceStatus and GetIntegrityStatus events both occur in RetrievingStatus state. This is also the case for Downloading state. You can see in the cases, when determining the next event, I need to check what the previous state was first.

If flawed, what are the pitfalls to my design, and how could it be improved? thanks.

enum ExampleState {
case Initiate
case Authorizing
case RetrievingStatus
case Confirming
case End
}

enum ExampleEvent {
case InitiateSequence
case GetAuthToken
case GetDeviceStatus
case GetIntegrityStatus
case Confirm
}

class StateMachine {

var oldState: ExampleState!
var currentState: ExampleState!
var currentEvent: ExampleEvent!

var table: [ExampleState: [ExampleEvent: ExampleState]] = [.Initiate:         [.InitiateSequence:   .Authorizing],
.Authorizing:      [.GetAuthToken:       .RetrievingStatus],
.Confirming:       [.Confirm:            .End]]

func nextEvent(event: ExampleEvent) -> EventExecutor? {

let transitionState = table[currentState]![event]!
let oldState = currentState

switch (transitionState) {

case .Initiate:
currentState = .Initiate
return InitiateSequence()

case .Authorizing:
currentState = .Authorizing
return GetAuthToken()

case .RetrievingStatus:
currentState = .RetrievingStatus
switch (oldState) {
case .Authorizing: return GetDeviceStatus()
default: return nil
}

switch (oldState) {
default: return nil
}

case .Confirming:
currentState = .Confirming
return Confirm()

case .End:
currentState = .End
return nil
}

}
}


Use case

@objc func rxExecute() {
publisher
.map { self.stateMachine.nextEvent(event: $0) } //$0 == event that just completed
.flatMap { \$0!.rxExecute() } // execute next event from state machine output
.subscribe(subscriber) // handles errors/success from completed event
}


First there are a few things about design

Does you machine has something like 'error' that is not handled (e.g. by throwing an exception) or you have only 'outcomes' that handled in the same matter.

Do you expect branches in the FSM? Is it possible for an fsm method to fail? If possible how it is handled? E.g. ignored (say we rely on caller to retry on timer), raise failure event and queue it for execution, use return value as a guard to branch in state machine method.

Do you expect your code to maintained over 20+ years period?

After many attempts to make better existing FSMs, making my own, trying different frameworks and making my own frameworks too, I came to a trivially simple conclusion (aka 'dumb' FSM pattern): each event should be represented by a function and each function should contain a switch by state; or every state should be represented by a function and every function should contain a switch by event.

In the long run this is the cheapest solution. E.g. if you do not have branches/failures one can write a simple table driven fsm, however, once we will get the first branch/failure to handle we will need to either make it way more complicated or throw it away and rewrite using the 'dumb' approach

• Ok thanks. I think I understand, but it would help if you could share an examples of this. In my case, the state machine wont be responsible for handling any errors except invalid state transitions. The ResponseSubscriber actually handles the errors from the actual event task, and if there are no errors I save and emit completed event to publisher. I'll add code, and would like your opinion of this approach. May 19, 2020 at 19:28
• Your simple fsm, your code will work as is and it is very close to the 'dumb' one I described. The only difference is that you have one switch statement to handle all combinations of state/and events and a table to figure out next state. And the real 'dumb' one will have one switch per state that will perform actions and figure out next state. May 19, 2020 at 20:04