ReRxSwift 2.3.0

ReRxSwift 2.3.0

Stefan van den Oord 维护。



 
依赖于
ReSwift~> 6
RxSwift~> 6
RxCocoa~> 6
 

ReRxSwift 2.3.0

  • 作者:
  • Stefan van den Oord

ReRxSwift

Swift Version 4.1 Build Status API Documentation Carthage compatible CocoaPods Version Badge Supported Platforms Badge license

RxSwift 绑定 ReSwift

目录

简介

如果您还不知道,这两个框架都很棒

  • ReSwift 是一个小型框架,它以 Swift 语言实现了 单向数据流架构。它受到了 JavaScript 实现该架构的启发: Redux
  • RxSwiftReactiveX 的 Swift 实现。在其他事物中,它允许您轻松地将属性绑定到用户界面元素。

使用这两个框架,您可以构建非常出色的应用程序架构。RxSwift 允许您将应用程序状态绑定到 UI,而 ReSwift 则在响应操作时发布状态更新。但我们更进一步。

类似于 react-redux,ReRxSwift 允许您创建具有 propsactions 的视图控制器。视图控制器从它们的 props 中读取所有需要的数据(而不是直接从状态中),并通过调用 actions 中定义的回调来更改数据(而不是直接分发 ReSwift 操作)。这有一些优点

  • 关注点分离更佳。更容易理解你的控制器到底做什么,以及它使用什么数据。换句话说,这便于进行局部推理。
  • 单元测试。由于关注点分离,你可以轻松地单元测试视图控制器,一直到接口构建器连接。
  • 更好的可重用性。重用你的视图控制器就像指定从状态到props和从ReSwift动作到actions的不同映射。 (请参阅下面的“开放问题”章节。)
  • 快速原型设计。你可以轻松地使用模拟的propsactions,以便你得到一个工作的UI层原型。不编写任何应用程序的业务逻辑,你可以这样实现你的表示层,使得用真实状态和动作替换模拟变得非常简单。

安装

使用这个库最简单的方法是通过CocoapodsCarthage。对于CocoaPods,在你的Podfile中添加以下内容

pod 'ReRxSwift', '~> 2.0'

对于Carthage,在你的Cartfile中添加以下内容

github "svdo/ReRxSwift" ~> 2.0

使用

本节假设存在一个全局变量store,它包含你的应用程序的商店,并且它的类型是Store<AppState>。你有一个控制器来管理一个文本框;文本框显示状态中的某个值,在editingDidEnd时,你触发一个动作将文本框的内容存储回状态。要使用ReRxSwift为你的控制器MyViewController,你可以按照以下步骤操作。

  1. 为你的控制器创建一个扩展,使其成为Connectable,定义你的视图控制器需要的PropsActions

    extension MyViewController: Connectable {
        struct Props {
            let text: String
        }
        struct Actions {
            let updatedText: (String) -> Void
        }
    }
  2. 定义你的状态如何映射到上述Props类型

    private let mapStateToProps = { (appState: AppState) in
        return MyViewController.Props(
            text: appState.content
        )
    }
  3. 定义发出的动作

    private let mapDispatchToActions = { (dispatch: @escaping DispatchFunction) in
        return MyViewController.Actions(
            updatedText: { newText in dispatch(SetContent(newContent: newText)) }
        )
    }
  4. 定义连接并将其连接起来

    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()
        }
    }
  5. 绑定文本框的文本,使用Swift 4键路径引用Props中的text属性

    override func viewDidLoad() {
        super.viewDidLoad()
        connection.bind(\Props.text, to: textField.rx.text)
    }
  6. 调用动作

    @IBAction func editingChanged(_ sender: UITextField) {
        actions.updatedText(sender.text ?? "")
    }

这是样本应用程序中的SimpleTextFieldViewController。该视图控制器带有一套完整的单元测试:SimpleTextFieldViewControllerSpec

API

以下是API最重要组件的高级描述。还有ReRxSwift的完整API文档可用于。

可连接

这是您的视图控制器必须遵守的协议。它要求您添加一个 connection 属性。它提供您可以在视图控制器中使用的 propsactions。通常,您可以这样声明 connection

class MyViewController: Connectable {
    let connection = Connection(
        store: store,
        mapStateToProps: mapStateToProps,
        mapDispatchToActions: mapDispatchToActions
    )
}

有关更多信息,请参考 Connection 构造函数文档。

props

这包含了您使用 mapStateToProps 创建的 Props 对象。换句话说:它包含了视图控制器使用的一切数据,自动从您的应用程序状态中提取出来。当在 Connection 中使用 bind 方法时,您可能不需要直接使用此 props 属性。

actions

这包含了您使用 mapDispatchToProps 创建的 Actions 对象。换句话说:它指定在调用您 actions 定义的回调时,必须派发哪个 ReSwift 行动。

Connection

Connection 负责将您应用程序状态映射到视图控制器的 props,以及在视图控制器 actions 中调用函数时派发映射的动作。

Constructor(store, mapStateToProps, mapDispatchToActions)

要创建您的 Connection 实例,您需要用三个参数来构造它

  • store:您的应用程序的 ReSwift 存储。
  • mapStateToProps:一个函数,它从您应用程序的状态中获取值并将它们放入视图控制器中的 props 对象。这将您的应用程序状态与视图控制器数据解耦。
  • mapDispatchToActions:一个函数,指定您视图控制器可以调用哪些操作,以及对每个操作,ReSwiftaction 需要被发送。

connect()

调用此方法将导致连接订阅 ReSwift 存储并接收应用程序状态更新。从您的视图控制器的 viewWillAppearviewDidAppear 方法中调用此方法。

disconnect()

调用此方法会导致连接从 ReSwift 存储中取消订阅。从您的视图控制器的 viewWillDisappearviewDidDisappear 中调用此方法。

subscribe(keyPath, onNext)

订阅您视图控制器 props 中的条目,这意味着每当该条目发生变化时,将调用所给的 onNext 处理器。

bind(keyPath, to, mapping)

这个函数将您的视图控制器中的 props 中的一个条目绑定到一个启用了 RxSwift 的用户界面元素上,因此每当您的 props 发生变化时,用户界面元素将相应地自动更新。

bind 函数接受以下参数

  • keyPath:指向您要在 props 中绑定的用户界面元素的那个元素的(Swift 4)key path。
  • to:RxSwift 反应式属性包装器,例如 textField.rx.textprogressView.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文件夹包含以下示例

  • SimpleTextField:ReRxSwift最基本的使用场景,包含一个其值已绑定并有一个动作的文本框。
  • SteppingUp:具有多个绑定值和动作的使用场景,同时展示了在绑定时如何转换值。
  • TableAndCollection:展示了如何结合使用ReRxSwift、RxSwift、RxCocoa和RxDataSources,以拥有非常简单的可自动动画更新的表格/集合视图。

常见问题解答(FAQ)

当应用状态变化时,我的props没有更新?

这发生在你忘记在你的视图控制器viewWillAppearviewDidAppear方法中调用connection.connect()的时候。趁此机会,你可能还想验证你也在viewWillDisappearviewDidDisappear中调用了connection.disconnect()

当我调用connection.bind()时,我得到了编译器错误?

调用bind时,您需要将一个键路径传递给props对象中的元素。由于ReRxSwift确保只有当该元素实际发生变化时才会触发,它会将它的值与之前的值进行比较。这意味着您的props对象中的元素需要实现Equatable协议。当然,简单类型已经实现了Equatable,但在绑定表格视图项或集合视图项时,您需要确保类型实现了Equatable

另一个常见的错误来源是您的props中元素的类型与用户界面元素预期的不完全匹配。例如,您将stepValue(一个Double类型)绑定到一个步进器上来,但您的props包含了Float。在这些情况下,您可以将映射函数作为bind(_:to:mapping:)的第三个参数传递,以将props元素转换为期望的类型。请参见SteppingUpViewController.swift以获取示例。

我已经仔细检查了一切,但我仍然收到错误!

请在GitHub上创建一个新问题,因为您可能遇到了一个错误。(但请确保您的Props类型中的所有内容都是Equatable!)