Katana 介绍

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

}

Side Effects


使用纯函数更新应用程序的状态很好,而且有很多好处。如果应用程序必须处理外部世界(例如,API调用,磁盘文件管理)。对于所有这类操作,Katana提供了Side Effects 的概念。可以使用Side Effects 与应用程序的其他部分交互,然后分发新的状态更新器来更新状态。对于更复杂的情况,您还可以分派其他Side Effects。

struct GenerateRandomNumberFromBackend: SideEffect {

  func sideEffect(_ context: SideEffectContext) throws {

    // 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) throws {

    // 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)))

  }

}


Dependencies(依赖关系)

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

}

}


Interceptors


当定义一个存储时,你可以提供一个拦截器列表,当一个项目被分派时,这些拦截器会按照给定的顺序被触发。拦截器就像一个包罗万象的系统,可以用来实现日志记录或动态更改存储的行为等功能。每次要处理可分派项时都会调用拦截器。

Katana自带一个内置的dispatchabllogger拦截器,它记录所有的dispatchables,除了黑名单参数中列出的那些。

let dispatchableLogger = DispatchableLogger.interceptor(blackList: [NotToLog.self])

let store = Store(interceptor: [dispatchableLogger])

有时,监听系统中发生的事件并对它们作出反应是很有用的。Katana提供了可以用来实现这个结果的Observer Interceptor。

特别是,您会在以下情况下指示拦截器调度项目:

Store已初始化

状态以特定方式变化

特定的可调度物料由Store管理

特定的通知将发送到默认的NotificationCenter

let observerInterceptor = ObserverInterceptor.observe([

  .onStart([

    // list of dispatchable items dispatched when the store is initialized

  ])

])

let store = Store(interceptor: [observerInterceptor])

注意,当使用ObserverInterceptor拦截Side Effects时,dispatchable的返回值对拦截器本身是不可用的。


Requirements

iOS 9.0+ / macOS 10.10+

Xcode 8.0+

Swift 4.0+

CocoaPods

use_frameworks!

source 'https://github.com/CocoaPods/Specs.git'

platform :ios, '9.0'

target 'MyApp' do

  pod 'Katana'

end

你可能感兴趣的:(Katana 介绍)