-
Notifications
You must be signed in to change notification settings - Fork 6
/
SwiftFSM.swift
114 lines (96 loc) · 3.23 KB
/
SwiftFSM.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//
// SwiftFSM.swift
// OneUCore
//
// Created by Ciprian Caba on 03/07/15.
// Copyright (c) 2015 Cipri. All rights reserved.
//
import Foundation
import UIKit
/// A simple, enum based FSM implementation
open class SwiftFSM<State: Hashable, Transition: Hashable> {
fileprivate let _id: String
fileprivate let _willLog: Bool
fileprivate var _states = [State: SwiftFSMState<State, Transition>]()
fileprivate var _currentState: SwiftFSMState<State, Transition>?
/// Returns the current state of the fsm
open var currentState: State {
get {
return _currentState!.state
}
}
/**
Initializes a new fsm with the provided id and logging specifications
Also defines the State and Transition generics
:param: id The id of the fsm.. Might come in handy if you use multiple fsm instances
:param: willLog Parameter that will enable/disable logging of this fsm instance
:returns: A new SwiftFSM instance
*/
public init (id: String = "SwiftFSM", willLog: Bool = true) {
_id = id
_willLog = willLog
}
/**
Adds and returns a new or an already existing fsm state
If the state was defined before, that SwiftFSMState instance will be returned
:param: newState The enum of the new fsm state
:returns: A SwiftFSMState instance for the newState
*/
open func addState(_ newState: State) -> SwiftFSMState<State, Transition> {
if let existingState = _states[newState] {
log("State \(newState) already added")
return existingState
} else {
let newFsmState = SwiftFSMState<State, Transition>(state: newState)
_states[newState] = newFsmState
return newFsmState
}
}
/**
Starts the fsm in the specified state. The state must be defined with the addState method
This method will not trigger any onEnter or onExit methods
:param: state The initial state of the fsm
*/
open func startFrom(_ state: State) {
if _currentState == nil {
if let fsmState = _states[state] {
_currentState = fsmState
} else {
log("Cannot find the \(state) start state")
}
} else {
log("Fsm already started. Cannot startFrom again")
}
}
/**
Try transitioning the fsm with the specified transition
:param: transition The transition to be used
:returns: This method will return the new state if the state transition was successful or nil otherwise
*/
@discardableResult
open func transitionWith(_ transition: Transition) -> State? {
if let oldState = _currentState {
if let newState = oldState.transitionWith(transition) {
if let newFsmState = _states[newState] {
log("\(oldState.state) -> \(transition) -> \(newState)")
_currentState = newFsmState
oldState.exit(transition)
newFsmState.enter(transition)
return newState
} else {
log("The \(transition) transitions to an unregistered \(newState) state")
}
} else {
log("There is no transition defined from the [\(oldState.state)] state with the [\(transition)] transition")
}
} else {
log("Please start the fsm first")
}
return nil
}
fileprivate func log(_ message: String) {
if _willLog {
print("\(_id) \(message)")
}
}
}