如果您还不了解它们,这两个都是很棒的框架
使用这两个框架,您可以构建非常优秀的应用程序架构。 RxSwift 允许您将应用程序状态绑定到您的 UI,而 ReSwift 响应 actions 发出状态更新。 我们在此基础上更进一步。
类似于 react-redux,ReRxSwift 允许您创建具有 props
和 actions
的视图控制器。 视图控制器从其 props
读取所需的所有数据(而不是直接从状态读取),并且它们通过调用由 actions
定义的回调(而不是直接分发 ReSwift actions)来更改数据。 这有一些很好的优势
props
以及从 ReSwift actions 到 actions
的不同映射一样简单。(另请参阅下面的“未解决的问题”部分。)props
和 actions
,以便获得可用的 UI 层原型。 无需编写任何应用程序的业务逻辑,您就可以以一种非常简单的方式实现您的表示层,以便用真实的状态和 actions 替换虚拟数据。使用此库最简单的方法是通过 Cocoapods 或 Carthage。 对于 CocoaPods,请将以下内容添加到您的 Podfile
pod 'ReRxSwift', '~> 2.0'
对于 Carthage,请将以下内容添加到您的 Cartfile
github "svdo/ReRxSwift" ~> 2.0
本节假设存在一个全局变量 store
,其中包含您的应用程序的 store,并且其类型为 Store<AppState>
。 您有一个管理文本字段的视图控制器; 该文本字段显示来自您状态的值,并且在 editingDidEnd
时,您触发一个 action 以将文本字段的内容存储回您的状态。 要为您的视图控制器 MyViewController
使用 ReRxSwift,请使用以下步骤。
创建视图控制器的扩展以使其成为 Connectable
,定义视图控制器需要的 Props
和 Actions
extension MyViewController: Connectable {
struct Props {
let text: String
}
struct Actions {
let updatedText: (String) -> Void
}
}
定义您的状态如何映射到上面的 Props
类型
private let mapStateToProps = { (appState: AppState) in
return MyViewController.Props(
text: appState.content
)
}
定义要分发的 actions
private let mapDispatchToActions = { (dispatch: @escaping DispatchFunction) in
return MyViewController.Actions(
updatedText: { newText in dispatch(SetContent(newContent: newText)) }
)
}
定义连接并将其连接起来
class MyViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
let connection = Connection(
store: store,
mapStateToProps: mapStateToProps,
mapDispatchToActions: mapDispatchToActions
)
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
connection.connect()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
connection.disconnect()
}
}
绑定文本字段的文本,使用 Swift 4 键路径来引用 Props
的 text
属性
override func viewDidLoad() {
super.viewDidLoad()
connection.bind(\Props.text, to: textField.rx.text)
}
调用 action
@IBAction func editingChanged(_ sender: UITextField) {
actions.updatedText(sender.text ?? "")
}
这与示例应用程序中的 SimpleTextFieldViewController
非常相似。 该视图控制器附带完整的单元测试:SimpleTextFieldViewControllerSpec
。
以下是 API 中最重要组件的高级描述。 还有 ReRxSwift 的完整 API 文档 可用。
这是您的视图控制器必须遵循的协议。 它要求您添加 connection
属性。 它提供了您可以在视图控制器中使用的 props
和 actions
。 通常,您按如下方式声明 connection
class MyViewController: Connectable {
let connection = Connection(
store: store,
mapStateToProps: mapStateToProps,
mapDispatchToActions: mapDispatchToActions
)
}
有关更多信息,请参阅 Connection
构造函数文档。
这包含您使用 mapStateToProps
创建的 Props
对象。 换句话说:它包含您的视图控制器使用的所有数据,自动从您的应用程序状态中提取。 当在 Connection
中使用 bind
方法时,您可能不需要直接使用此 props
属性。
这包含您使用 mapDispatchToActions
创建的 Actions
对象。 换句话说:它指定在调用由您的 actions
定义的回调时,必须分发哪个 ReSwift action。
Connection
负责从您的应用程序状态到您的视图控制器 props
的映射,以及在调用视图控制器 actions
中的函数时分发映射的 action。
要创建您的 Connection
实例,您需要使用三个参数构造它
props
对象中。 这将您的应用程序状态与视图控制器数据解耦。调用此方法会导致连接订阅 ReSwift store 并接收应用程序状态更新。 从视图控制器的 viewWillAppear
或 viewDidAppear
方法中调用此方法。
调用此方法会导致连接取消订阅 ReSwift store。 从视图控制器的 viewWillDisappear
或 viewDidDisappear
中调用此方法。
订阅视图控制器的 props
中的条目,这意味着每当该条目更改时,都将调用给定的 onNext
处理程序。
此函数将视图控制器的 props
中的条目绑定到启用 RxSwift 的用户界面元素,以便每次您的 props
更改时,用户界面元素都会自动相应地更新。
函数 bind
接受以下参数
props
中的元素的(Swift 4)键路径。textField.rx.text
或 progressView.rx.progress
。bind
变体(但不是全部)允许您提供映射函数。 此映射函数应用于指定 keyPath
处的 props
元素。 例如,您可以将其用于类型转换:您的 props
包含一个 Float
类型的值,但 UI 元素需要它为 String
类型。 指定映射 { String($0) }
将处理该问题。 SteppingUpViewController.swift
包含一个映射函数的示例,该函数将 Float
值映射到分段控件的选定索引。仅为了方便您理解:bind
函数有几个变体。 它们都是此简化代码的变体
self.props
.asObservable()
.distinctUntilChanged { $0[keyPath: keyPath] == $1[keyPath: keyPath] }
.map { $0[keyPath: keyPath] }
.bind(to: observer)
.disposed(by: disposeBag)
文件夹 Example
包含以下示例
当您忘记在视图控制器的 viewWillAppear
或 viewDidAppear
方法中调用 connection.connect()
时,会发生这种情况。 在您执行此操作时,您可能还需要验证您是否也在 viewWillDisappear
或 viewDidDisappear
中调用了 connection.disconnect()
。
在调用 bind
时,您将键路径传递给 props
对象中的元素。 由于 ReRxSwift 以某种方式确保仅在该元素实际更改时才触发,因此它将其值与先前的值进行比较。 这意味着您的 props
对象中的元素需要是 Equatable
。 当然,简单类型已经是 Equatable
,但是特别是当绑定表格视图项或集合视图项时,您需要确保类型是 Equatable
。
另一个常见的错误来源是您的 props
中元素的类型与用户界面元素期望的类型不完全匹配。 例如,您绑定到步进器的 stepValue
,它是 Double
类型,但您的 props
包含 Float
类型。 在这些情况下,您可以将映射函数作为第三个参数传递给 bind(_:to:mapping:)
,以将 props
元素转换为期望的类型。 有关示例,请参见 SteppingUpViewController.swift
。
请在 GitHub 上打开 新 issue,因为您可能遇到了 bug。(但请确保您的 Props
类型中的所有内容都是 Equatable
!)