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<CounterState,AppDependencies()

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<CounterState, AppDependencies>) 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<CounterState, AppDependencies>) 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<CounterState>(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<CounterState>(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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353