Katana是一个现代的Swift框架,用于编写可测试且易于推理的iOS应用程序的业务逻辑。Katana受到Redux的强烈启发。
Overview
应用程序状态完全由一个可序列化的数据结构描述,更改状态的唯一方法是调度StateUpdater。AStateUpdater旨在改变状态,并包含所有要做的信息。因为所有更改都是集中的,并且是按严格顺序进行的,所以没有任何需要注意的微妙的竞争条件。
struct CounterState:State {
var counter:Int=0
}
应用程序的状态只能通过状态更新器修改。StateUpdater表示一个导致应用程序状态改变的事件。你通过实现updateState(:)方法来定义状态更新器的行为,该方法基于当前应用程序状态和StateUpdater本身来改变状态。updateState应该是一个纯函数,这意味着它只依赖于输入(即状态和状态更新器本身),而且它没有副作用,比如网络交互。
struct IncrementCounter: StateUpdater {
func updateState(_ state: inout CounterState) {
state.counter += 1
}
}
Store 包含并管理你的整个应用状态。它负责管理被分派的项(例如,刚才提到的状态更新器)。
let store=Store store.dispatch(IncrementCounter()) 你可以要求store在应用状态的每一个变化时都得到通知。 store.addListener() { //the app state has changed } 使用纯函数更新应用程序的状态很好,而且有很多好处。如果应用程序必须处理外部世界(例如,API调用,磁盘文件管理)。对于所有这类操作,Katana提供了Side Effects 的概念。可以使用Side Effects 与应用程序的其他部分交互,然后分发新的状态更新器来更新状态。对于更复杂的情况,您还可以分派其他Side Effects。 struct GenerateRandomNumberFromBackend: SideEffect { func sideEffect(_ context: SideEffectContext // invokes the `getRandomNumber` method that returns a promise that is fullfilled // when the number is received. At that point we dispatch a State Updater // that updates the state context.dependencies.APIManager .getRandomNumber() .then({ randomNumber in context.dispatch(SetCounter(newValue: randomNumber)) }) } } struct SetCounter: StateUpdater { let newValue: Int func updateState(_ state: inout CounterState) { state.counter = self.newValue } } 此外,您可以利用await操作符来编写模仿Async / Await模式的逻辑,该逻辑允许您以同步方式编写异步代码。 struct GenerateRandomNumberFromBackend: SideEffect { func sideEffect(_ context: SideEffectContext // invokes the `getRandomNumber` method that returns a promise that is fullfilled // when the number is received. let promise = context.dependencies.APIManager.getRandomNumber() // we use await to wait for the promise to be fullfilled let randomNumber = try await(promise) // then the state is updated using the proper state updater try await(context.dispatch(SetCounter(newValue: randomNumber))) } } Side Effects示例利用了一个APIManager方法。Side Effects是可以通过使用上下文的dependencies参数来获取APIManager(context.dependencies.APIManager)。依赖容器是执行依赖注入的Katana。我们测试我们的副作用,因此我们需要摆脱单例或其他阻止我们编写测试的坏习惯。创建依赖项容器非常简单:只需创建一个符合SideEffectDependencyContainer协议的类,使存储对它具有通用性,并在Side Effects中使用它。 final class AppDependencies: SideEffectDependencyContainer { required init(dispatch: @escaping PromisableStoreDispatch, getState: @escaping GetState) { // initialize your dependencies here } } 当定义一个存储时,你可以提供一个拦截器列表,当一个项目被分派时,这些拦截器会按照给定的顺序被触发。拦截器就像一个包罗万象的系统,可以用来实现日志记录或动态更改存储的行为等功能。每次要处理可分派项时都会调用拦截器。 Katana自带一个内置的dispatchabllogger拦截器,它记录所有的dispatchables,除了黑名单参数中列出的那些。 let dispatchableLogger = DispatchableLogger.interceptor(blackList: [NotToLog.self]) let store = Store 有时,监听系统中发生的事件并对它们作出反应是很有用的。Katana提供了可以用来实现这个结果的Observer Interceptor。 特别是,您会在以下情况下指示拦截器调度项目: Store已初始化 状态以特定方式变化 特定的可调度物料由Store管理 特定的通知将发送到默认的NotificationCenter let observerInterceptor = ObserverInterceptor.observe([ .onStart([ // list of dispatchable items dispatched when the store is initialized ]) ]) let store = Store 注意,当使用ObserverInterceptor拦截Side Effects时,dispatchable的返回值对拦截器本身是不可用的。 iOS 9.0+ / macOS 10.10+ Xcode 8.0+ Swift 4.0+ use_frameworks! source 'https://github.com/CocoaPods/Specs.git' platform :ios, '9.0' target 'MyApp' do pod 'Katana' endSide Effects
Dependencies(依赖关系)
Interceptors
Requirements
CocoaPods