SwiftyState 1.1.1

SwiftyState 1.1.1

Mertol Kasanan维护。



  • 出自
  • mrtksn

SwiftyState

v1.1.1

SwiftyState是一个带有强大**设备端调试器,具有图形用户界面**的功能性状态引擎。您还可以在应用程序中进行时间旅行。它受到了Redux JS库的启发,但并没有那么多。

以下是演示应用程序的视频预览。UIKit元素会随状态变化自动更新。底部界面是设备端调试器,其中开发者可以查看和撤销状态更改。

它是用来做什么的?

在SwiftState中您创建一个单一数据源 - 一个状态,这是一种时尚的方式来表示一个结构体,它保存您的应用程序数据。您只能通过调用操作来修改数据,并通过订阅来获取通知,告诉您何时发生了变化。

这使得您能够创建独立的组件,它们只需观察状态并相应地行动,而无需担心应用程序的其他部分。将所有数据集中在一个地方简化了其有效性的验证和管理。

例如,您如何在不同会话之间持久化应用程序状态?使用SwiftyState,您几乎不需要做任何事情,您只需调用save()或load()来从磁盘保存和加载状态即可完成。不想将数据保存在磁盘上,而是想要在线上保存?很简单,SwiftState支持导出和导入JSON,这样您就可以在任何地方拥有您的应用状态。

拥有在iOS上运行的图形调试器也将帮助您查找错误,理解逻辑或欣赏您作品的内部结构,即使您没有XCode也是如此。只需摇晃您的设备,调试器就会弹出(如果您选择的话)。

设备状态调试器

SwiftyState 内置一个运行在 iOS 上的调试器,可以通过摇晃设备启动(这可以更改)。这是一个独立的 UI,您可以在其中查看当前状态、动作,并且可以回滚到先前的状态——时间旅行。

示例

首先,您需要导入 SwiftyState

import SwiftyState

创建您的状态对象

在 SwiftyState 中,您需要定义您的状态结构。您只有一个这样的。

/// This is your store. It is a struct that conforms to the SwiftyStateStore
struct MyStore : SwiftyStateStoreEquatable {
    var Jenny : Int = 100
    var Donald : Int = 20
}
/// Add this to make SwiftState return your store when asked
extension SwiftyState {
    func getState()->MyStore{
        return self.getRawState() as! MyStore
    }
}

因此,默认情况下,珍妮有 100 美元,唐纳德只有 20 美元。这些值在任何地方都可以访问。您可以通过获取您状态的最新副本来访问它们

/// Get a copy of your state
let state = SwiftyState().getState()

print("Jenny owns \(state.Jenny) and Donald owns \(state.Donald)")
/// the output is "Jenny owns 100 and Donald owns 20"

创建动作

您如何更改状态?您创建动作并调用这些动作来正确修改数据。

以下是一个让珍妮给唐纳德 10 美元的动作。您只需遵循 SwiftyAction 协议,使用枚举添加动作,并在 reducer 函数中编写动作的逻辑即可。

别担心,它具有自动完成功能。Xcode(或您喜欢的 IDE)将帮助您,您无需编写太多代码。

enum PayAction : SwiftyAction {
    case give10BuckToDonald

    func reducer(state: SwiftyStateStore) -> SwiftyStateStore {
        var newState = state as! MyStore
        switch self {
        case .give10BuckToDonald:
            newState.Donald += 10
            newState.Jenny -= 10
        }
        // When you are done changing, always return the new state
        return newState
    }
}

您可以随时调用此动作,珍妮将给唐纳德 10 美元。您按如下方式调用动作

SwiftyState().action(a: PayAction.give10BuckToDonald)

好的,如何定义一个自定义金额?让我们添加另一个动作

enum PayAction : SwiftyAction {
    case give10BuckToDonald
    case giveToDonald(amount : Int)

    func reducer(state: SwiftyStateStore) -> SwiftyStateStore {
        var newState = state as! MyStore
        switch self {
        case .give10BuckToDonald:
            newState.Donald += 10
            newState.Jenny -= 10
        case .giveToDonald(let amount):
            newState.Donald += amount
            newState.Jenny -= amount
        }
        // When you are done changing, always return the new state
        return newState
    }
}

然后从任何地方调用该动作

SwiftyState().action(a: PayAction.giveToDonald(amount: 5))

您注意到这里的模式吗?您可以随时添加动作,并且不必在一个地方。例如,让我们创建一个 StealAction,可以用它从 Donald 和 Jenny 那里偷东西。

enum StealAction : SwiftyAction{
    case stealAll

    func reducer(state: SwiftyStateStore) -> SwiftyStateStore {
        var newState = state as! MyStore
        newState.Donald = 0
        newState.Jenny = 0
        return newState
    }
}

从任何地方调用

SwiftyState().action(a: StealAction.stealAll)

监听变化

使独立组件能够感知你的数据并根据数据变化进行适应是一种很好的做法。你可以通过订阅状态变化来实现,如下所示

let subscription = SwiftyState().subscribe { [weak self] in
    let state = $0 as! MyStore
    let oldState = $1 as? MyStore

    /// Your code goes here
}

SwiftyState().subscribe()方法返回一个包含ID和取消订阅方法的SwiftySubscription对象。如果你的对象不再需要,请务必取消订阅,以防止内存泄漏。

例如,当你使用SwiftState和UIKit时,最佳订阅变化的时机是在ViewController的viewDidLoad()中,并在deinit()中取消订阅。让我们来看看

class MoneyStatus : UIViewController {
    var subscription : SwiftySubscription?

    override func viewDidLoad() {
        self.subscription = SwiftyState().subscribe { [weak self] in
            let state = $0 as! MyStore // the new state
            let oldState = $1 as? MyStore // the old state
            // if the state changed, apply the changes
            if state.Donald != oldState?.Donald{
                print("Donald now owns \(state.Donald)$")
            }
        }
        // execute the closure at the start
        self.subscription?.hotStart()
    }

    // free up resources when you no longer need it
    deinit {
        self.subscription?.unsubscribe()
    }
}

验证变化

使用SwiftyState,你可以选择拒绝不理想的动作结果。例如,确保Jenny和Donald不会债务累累。

struct MyValidator : SwiftyStateValidiator{
    func validiator(_ state: SwiftyStateStore) -> Bool {
        // the new state is available here, before making it available everywhere:
        let newState = state as! MyStore

        return (newState.Donald >= 0) && (newState.Jenny >= 0)
    }
}

实际上很简单。你创建一个遵循SwiftyStateValidiator的struct,这意味着你将有一个验证器函数,每次动作运行时都会接收到新状态。你评估新状态,如果它是正常的,则返回true,如果不是,则返回false。如果你返回false,则新状态将被丢弃,并传递旧状态。

注意事项

SwiftyState在主线程上同步运行。这意味着如果在动作或订阅中进行大量计算,则应用程序可能会冻结,直到你的代码执行完毕。保持动作的纯净(即只使用reducer提供的数据修改状态,不在动作中包含异步操作),并且如果你有大量计算,请使用SwiftState的订阅来异步或单独在线程中启动它们。

附带的示例应用程序,请查看。

要运行示例项目,首先从Example目录克隆仓库,然后运行pod install

安装

获取 SwiftState

SwiftyState 可以通过 CocoaPods 获得。要安装它,只需将以下行添加到您的 Podfile 中

pod 'SwiftyState'

集成到您的项目中

与任何框架一样,在您使用 SwiftyState 的任何地方导入 SwiftState

import SwiftyState

按照描述创建您的 State 和 Validiator 对象。通常将它们保存在单独的 Swift 文件中是一个好主意。

在您的 AppDelegate.swiftdidFinishLaunchingWithOptions 方法中启动 SwiftyState。建议将状态保存到磁盘并从磁盘加载,以在会话之间保持数据。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    //Configure SwiftyState to use your State object
    SwiftyState().setStore(MyStore())
    // Configure SwiftState to use your validator
    SwiftyState().setStateValidiator(MyValidator())
    // Load the state from the disk
    SwiftyState().load()
    // Start collecting state history for debugging. Remove this one before shipping
    SwiftyState().startDebug()
    return true
}

/// Save the state before app terminates. You can save the state whenever you like.
func applicationWillTerminate(_ application: UIApplication) {
    SwiftyState().save()
}

这足够使用 SwiftyState,但如果您想使用调试器,您需要找到一种启动它的方法。推荐的方式是监听设备晃动。为此,请将以下行添加到您的根 ViewController 中。

override func viewDidLoad() {
    super.viewDidLoad()
    // Enable SwiftyState Debuggur UI to be displayed when you shake your device.
    // Remove this one before shipping unless you want to expose the debugger to your users
    SwiftyState().debugUIManager().showOnShake(self)

    /// --------- the rest of your code ---- ///
}

如果您不想使用设备晃动,可以通过调用 SwiftyState().debugUIManager().showDebugger(self) 来启动调试器。

作者

Mertol Kasanan,mrtksn at gmail

许可证

SwiftyState 在 MIT 许可证下提供。有关更多信息,请参阅 LICENSE 文件。