说明 1.2.0

说明 1.2.0

测试已测试
语言语言 SwiftSwift
许可 MIT
发布上次发布2017年10月
SwiftSwift 版本4.0
SPM支持 SPM

Jordan Hamill 维护。



说明 1.2.0

  • Jordan Hamill

一个简单的状态机实现,具有美观的 DSL。

状态转换会触发效果,可以将新的输入发送到状态机,错误可以表示为新状态和输入。
每个状态都符合 StateSimpleStateStateTakingInputStateUsingMappedState 之一。状态对象可以从定义的输入中接收参数。它还可以从上一个状态接收任何内容

该 DSL 启发于 RxAutomaton




示例状态机

根据可能出现的不同初始屏幕,应用代理可以迅速变得非常复杂。
以下是一个应用,它有一个

  • 数据库迁移阶段,用于更新本地数据。
  • 数据库索引阶段,显示加载动画。如果您的应用程序存储了大量的本地数据,则非常有用。
  • 登录状态,显示主 UIViewController
  • 注销状态,显示登录/注册 UIViewController
import Stated

class AppLauncher {

    // MARK: Create some simple states that hold no data.

    struct UninitializedState: SimpleState { }
    struct InitializedState: SimpleState { }
    struct UpgradingState: SimpleState { }
    struct IndexingState: SimpleState { }
    struct LoggedInState: SimpleState { }
    struct LoggedOutState: SimpleState { }

    // MARK: Define the states we're going to use by creating "slots" in which the system can place a given instance of one of our states

    struct States {
        static let uninitialized = UninitializedState.slot
        static let initialized = InitializedState.slot
        static let upgrading = UpgradingState.slot
        static let indexing = IndexingState.slot
        static let loggedIn = LoggedInState.slot
        static let loggedOut = LoggedOutState.slot
    }

    // MARK: Define inputs that will be used to trigger transitions between the above states

    struct Inputs {
        static let initialize = input()
        static let upgrade = input()
        static let indexDatabase = input()
        static let logIn = input()
        static let logOut = input()
    }

    // MARK: Private propteries

    private var machine: StateMachine!

    // MARK: Lifecycle

    init(upgradeService: Upgrade, apiService: APIService, db: PersistenceService, rootViewController: RootViewController) {

        // MARK: Side Effects

        func initialize(stateMachine: StateMachine) {
            if upgradeService.isUpgradePending {
                stateMachine.send(Inputs.upgrade)
            } else {
                stateMachine.send(Inputs.indexDatabase)
            }
        }

        func upgrade(stateMachine: StateMachine) {
            rootViewController.showUpgradeProgressController(onCompletion: {
                stateMachine.send(Inputs.indexDatabase)
            })
        }

        func indexDatabase(stateMachine: StateMachine) {
            db.createSecondaryIndices(onCompletion: {
                if apiService.canLogIn {
                    stateMachine.send(Inputs.logIn)
                } else {
                    stateMachine.send(Inputs.logOut)
                }
            })
        }

        func logIn(stateMachine: StateMachine) {
            rootViewController.showLoggedInExperience(apiService: apiService, db: db, onLogOut: {
                stateMachine.send(Inputs.logOut)
            })
        }

        func logOut(stateMachine: StateMachine) {
            rootViewController.showLogInViewController(onLoggedIn: {
                stateMachine.send(Inputs.logIn)
            })
        }

        // MARK: Define state machine using the inputs, slots and side effects from above

        // This is the long-form syntax and is exactly equivalent to the operator syntax below
        let mappings: [AnyStateTransitionTrigger] = [
            Inputs.initialize
                .given(States.uninitialized)
                .transition(to: States.initialized)
                .performingSideEffect(initialize),

            Inputs.upgrade
                .given(States.initialized)
                .transition(to: States.upgrading)
                .performingSideEffect(upgrade),

            Inputs.indexDatabase
                .given(States.upgrading)
                .transition(to: States.indexing)
                .performingSideEffect(indexDatabase),
            Inputs.indexDatabase
                .given(States.initialized)
                .transition(to: States.indexing)
                .performingSideEffect(indexDatabase),

            Inputs.logIn
                .given(States.indexing)
                .transition(to: States.loggedIn)
                .performingSideEffect(logIn),
            Inputs.logIn
                .given(States.loggedOut)
                .transition(to: States.loggedIn)
                .performingSideEffect(logIn),

            Inputs.logOut
                .given(States.indexing)
                .transition(to: States.loggedOut),
                .performingSideEffect(logOut)
            Inputs.logOut
                .given(States.loggedIn)
                .transition(to: States.loggedOut)
                .performingSideEffect(logOut)
        ]

        // This is the shorter operator syntax and is exactly equivalent to the syntax above.
        // It is very easy to visualize how the system should behave in this case
        let mappings: [AnyStateTransitionTrigger] = [
            /* Input             |        from          =>        to          | side effect */
            Inputs.initialize    | States.uninitialized => States.initialized | initialize,

            Inputs.upgrade       | States.initialized   => States.upgrading   | upgrade,

            Inputs.indexDatabase | States.upgrading     => States.indexing    | indexDatabase,
            Inputs.indexDatabase | States.initialized   => States.indexing    | indexDatabase,

            Inputs.logIn         | States.indexing      => States.loggedIn    | logIn,

            Inputs.logOut        | States.indexing      => States.loggedOut   | logOut,
            Inputs.logOut        | States.loggedIn      => States.loggedOut   | logOut,
        ]
        machine = StateMachine(initialState: UninitializedState(), mappings: mappings)
    }

    // MARK: Internal methods

    func initialize() {
        machine.send(Inputs.initialize)
    }
}

安装

Swift 包管理器

在您的 package.swift 中添加 Stated 依赖项

dependencies: [
    .Package(url: "https://github.com/jordanhamill/Stated.git", majorVersion: 1)
]