如果您还不了解它们,这两个都是很棒的框架
使用这两个框架,您可以构建非常优秀的应用程序架构。 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!)