ReSwiftConsumer
现在是整理中。(现在开发中)
通过简单的示例项目(Example 项目)可以快速了解其工作原理。正在收集中 ... 您可以通过 Example 项目了解 ReSwift 状态如何通过该库来工作。
使用 ReSwiftConsumer,您只能消费感兴趣的 State 中属性的变化。并且您可以有独立的存储与全局状态分开。
ReSwiftConsumer 通过连接属性选择函数(Property selector)和更改状态的消费函数(Changed property consumer, Observer)与 ReSwift,帮助开发人员对 State 的特定属性进行响应。通过使用 ReSwiftConsumer,您可以仅从 ReSwift 的 State 中检测到所需的属性更改。
使用属性选择函数实现多观察者,有一个用
extension
实现的例子,链接如下。 ReSwift+select.swift 我用Promise
形式简洁地实现了我一直在思考的问题。Wow!!
StateConsumer
虽然 ReSwift 可以通过 newState 函数知道 State 发生了变化,但无法知道状态中哪些属性值发生了变化。如果 UI 组件与大量的 State 连接,则需要针对频繁的 State 更新进行复杂和细致的处理。正为此创建了 StateConsumer。
StateConsumer 实现了以下 Consumer 协议,当状态变化时,会调用 consume 函数。
public protocol Consumer {
associatedtype State
func consume(old: State?, new: State?)
}
使用方法很简单。通过 add
函数,将关心的属性和对应消费函数连接起来即可。也可以观察到子 State 中的特定值。
struct AppState: StateType {
var counter: Int = 0
var name: String? = nil
var works: [String] = []
}
let appStore = Store<AppState>(reducer: reducer, state: nil)
class CounterViewController: UIViewController, StoreSubscriber {
typealias StoreSubscriberStateType = AppState
@IBOutlet var counterLabel: UILabel!
@IBOutlet var nameLabel: UILabel!
var works: [String] = []
let consumer = StateConsumer<AppState>()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
appStore.subscribe(self)
// add consumer of property selectively
consumer.add({state in state.counter}, onCounterChanged)
consumer.add({state in state.name}, onNameChanged)
consumer.add({state in state.works}, onWorksChanged)
}
override func viewWillDisappear(_ animated: Bool) {
appStore.unsubscribe(self)
// remove all consumers
consumer.removeAll()
super.viewWillDisappear(animated)
}
func newState(state: AppState) {
// pass new state into consumer
consumer.consume(newState: state)
}
// MARK: Consumers
private func onCounterChanged(old: Int, new: Int) {
counterLabel.text = "\(new)"
}
private func onNameChanged(old: String?, new: String?) {
nameLabel.text = new
}
private func onWorksChanged(old: [String]?, new: [String]) {
self.works = new
}
}
尚未对 StateConsumer
进行性能测试。不过,由于 StateConsumer
内部包含状态实例,因此在处理大型 State 时,可能会出现内存或速度问题。因此,应该精心设计状态结构,并分片使用。为了解决此类问题,已创建了独立的存储。以下可查看详情。
分离存储(additional)
PageStoreSubscriber
已创建一个新的独立存储订阅者。
在 ReSwift 中,建议创建一个单一的存储进行使用。这一内容在 Redux 的指南中有很好的说明。但就应用开发而言,我需要一个面向屏幕的存储或非全局存储。当我们考虑通常的开发单位时,它是屏幕级的,并且我们设计了屏幕级的子状态结构。随着应用程序变得更加复杂,屏幕数量也会不断增加,一个状态结构体将变得更加复杂。而且执行中的屏幕控制器(ViewController)会被创建和销毁数十次,因此有很多临时数据。向全局状态添加属性值也有很多微不足道的属性。我对在全局状态中放入此类临时数据有些不自在。
如果全局状态与屏幕的生命周期相同,并且有相同的状态,那么我认为这将更容易进行开发。在创建屏幕控制器时,所需的初始状态值可以通过引用全局状态来创建,而当需要修改全局状态时,可以向全局存储发送动作。全局状态只需要包含真正的全局元素。
在 ReSwift 中,也提供了只选择性地订阅子状态的功能,并可以使用这种方式根据需要分离状态。但遗憾的是,在分离的子状态存储订阅者中,不能以订阅的形式访问全局状态的值。
例如,从选择性地订阅了 AppState 的 Sub State 实现 MainState 的 (UIViewController 或 UIView 等)上下文中,无法检测到 `authState` 或 `foregorund` 值的变化。而且,我想知道全局状态的变化。
struct AppState: StateType {
var authState = AuthState()
var foreground = false
var mainState = MainState()
}
PageStoreSubscriber 通过使用分离的存储可以如下订阅 MainState
和 AppState
。
编写中...
首先,分离 AppState 和 MainState。
struct AppState: StateType {
var authState = AuthState()
var foreground = false
}
struct MainState: StateType {
var count: Int = 10
}
MainVc
通过创建同时订阅 MainState
和 MainVc
作为对象而存在,所以当 ViewController 被销毁时,同时销毁,但全局创建的 appStore
则在应用程序存在的情况下持续保持其状态。这样一来,我们可以像一个应用程序的范围一样使用状态,但实际上可以在每个 ViewController 中分开管理状态。
class MainVc: UIViewController, PageStoreSubscriber, StoreSubscriber {
// for AppState
typealias StoreSubscriberStateType = AppState
let appConsumer = StateConsumer<AppState>()
// for MainState
typealias PageStoreSubscriberStateType = MainState
typealias PageStoreInteractStateType = MainState
private var pageStore: Store<MainState>?
private let pageConsumer = StateConsumer<MainState>()
private lazy var pageStoreSubscriber = RePageStoreSubscriber(subscriber: self)
override func viewDidLoad() {
super.viewDidLoad()
pageStore = Store<MainState>(reducer: mainReducer,
state: nil,
middleware: [])
// bind AppState
appStore.subscribe(self)
appConsumer.add({state in state?.foreground}, onForegroundChanged)
// bind MainState
pageStore?.subscribe(pageStoreSubscriber)
pageConsumer?.add({state in state?.count}, onCountChanged)
}
deinit {
// unbinding appStore, its consumer
appConsumer.removeAll()
appStore.unsubscribe(self)
// unbinding pageStore, its consumer
pageConsumer.removeAll()
if pageStoreSubscriber != nil {
pageStore?.unsubscribe(pageStoreSubscriber!)
}
}
func newState(state: AppState) {
appConsumer.consume(newState: state)
}
func newPageState(state: MainState) {
pageConsumer.consume(newState: state)
}
// MARK: Consumers
private func onForegroundChanged(curr: Bool) {
print("AppState- foreground \(curr)")
}
private func onCountChanged(prev: Int?, curr: Int) {
print("MainState- count: \(curr)"
}
}
组件(additional)
该库包含了一些在 ViewController 单元中可以直接使用的组件。这些组件是以便于使用重复性操作的形式创建的。
-
RePageController : 已实现
PageStoreSubscriber
以直接使用,具有ReSwift
提供的StoreSubscriber
以及不同的 Store 和 Middleware。 -
StateViewController, StateNavigationController : 可以与
RePageController
连接的基本视图控制器。它们可以订阅或解除订阅与独立存储和 RePageController 采取的状态。 -
ConsumberBag: 用于选择性地收集 Consumer 并一次性删除时使用。
示例
要运行示例项目,请克隆仓库,然后从Example目录运行pod install
命令。
查看示例应用程序的代码。
安装
CocoaPods
ReSwiftConsumer可通过CocoaPods获取。要安装,请将以下行添加到Podfile文件中。
pod 'ReSwift'
pod 'ReSwiftConsumer'
作者
brownsoo,@hansoo.labs,hansoo.labs
许可证
ReSwiftConsumer采用MIT许可证。更多信息请查看LICENSE文件。