ReRxSwift

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

RxSwiftReSwift 绑定

目录

简介

如果您还不了解它们,这两个都是很棒的框架

使用这两个框架,您可以构建非常优秀的应用程序架构。 RxSwift 允许您将应用程序状态绑定到您的 UI,而 ReSwift 响应 actions 发出状态更新。 我们在此基础上更进一步。

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

安装

使用此库最简单的方法是通过 CocoapodsCarthage。 对于 CocoaPods,请将以下内容添加到您的 Podfile

pod 'ReRxSwift', '~> 2.0'

对于 Carthage,请将以下内容添加到您的 Cartfile

github "svdo/ReRxSwift" ~> 2.0

用法

本节假设存在一个全局变量 store,其中包含您的应用程序的 store,并且其类型为 Store<AppState>。 您有一个管理文本字段的视图控制器; 该文本字段显示来自您状态的值,并且在 editingDidEnd 时,您触发一个 action 以将文本字段的内容存储回您的状态。 要为您的视图控制器 MyViewController 使用 ReRxSwift,请使用以下步骤。

  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. 定义要分发的 actions

    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 键路径来引用 Propstext 属性

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

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

这与示例应用程序中的 SimpleTextFieldViewController 非常相似。 该视图控制器附带完整的单元测试:SimpleTextFieldViewControllerSpec

API

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

Connectable (可连接的)

这是您的视图控制器必须遵循的协议。 它要求您添加 connection 属性。 它提供了您可以在视图控制器中使用的 propsactions。 通常,您按如下方式声明 connection

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

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

props

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

actions

这包含您使用 mapDispatchToActions 创建的 Actions 对象。 换句话说:它指定在调用由您的 actions 定义的回调时,必须分发哪个 ReSwift action。

Connection (连接)

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

构造函数 (store, mapStateToProps, mapDispatchToActions)

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

connect()

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

disconnect()

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

subscribe(keyPath, onNext)

订阅视图控制器的 props 中的条目,这意味着每当该条目更改时,都将调用给定的 onNext 处理程序。

bind(keyPath, to, mapping)

此函数将视图控制器的 props 中的条目绑定到启用 RxSwift 的用户界面元素,以便每次您的 props 更改时,用户界面元素都会自动相应地更新。

函数 bind 接受以下参数

仅为了方便您理解:bind 函数有几个变体。 它们都是此简化代码的变体

self.props
    .asObservable()
    .distinctUntilChanged { $0[keyPath: keyPath] == $1[keyPath: keyPath] }
    .map { $0[keyPath: keyPath] }
    .bind(to: observer)
    .disposed(by: disposeBag)

示例 App

文件夹 Example 包含以下示例

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 上打开 新 issue,因为您可能遇到了 bug。(但请确保您的 Props 类型中的所有内容都是 Equatable!)