返回 导航

Swift

hangge.com

Swift - RxSwift的使用详解72(ReactorKit架构1:安装配置、基本用法)

作者:hangge | 2018-06-27 08:10
    在之前的文章中,我介绍了 MVVM 架构(点击查看),以及第三方的 RxFeedback 架构(点击查看)。下面我接着再介绍另一个优秀的第三方架构:ReactorKit

一、基本介绍

1,什么是ReactorKit?

(1)ReactorKit 是一个轻量的响应式编程框架。它结合了 Flux 和响应式编程。用户行为和页面状态都是通过序列相互传递。同时这些序列都是单向的:
  • 页面(View)只能发出用户行为。
  • 而反应器(Reactor)只能发出状态。

(2)使用 ReactorKit 可以让我们代码分工变的更加清晰明朗,也便于后期的管理和维护。

2,安装配置

(1)RxFeedback 依赖于 RxSwift,关于 RxSwift 的安装配置可以看我之前的文章:

(2)接着把 ReactorKit 库下载到本地。

(3)将下载下来的源码包中 ReactorKit 文件夹拖拽至我们项目中即可。

二、代码结构

1,整体架构

(1)使用 ReactorKit 架构后代码根据职能分为 View(视图)和 Reactor(响应器)两部分:
  • View 是用户直接操作的层级,我们通过监测用户在 View 上的行为,转化成 Action 反馈给 Reactor
  • Reactor 处理之后又把响应状态 State 传递给 View 层,View 这边显示最终的传递的状态。

(2)简单来说就是 View 层只发出行为,而 Reactor 只发出状态,相互把对方所需要的东西传递给对方,构成一条响应式的序列。

2,Reactor 代码结构

(1)Reactor 是与 UI 相互独立的一层,它的作用就是将业务逻辑从 View 中抽离。也就是说每一个 View 都有对应的 Reactor ,并且将所有的逻辑代理都较给 ReactorReactor 接收到 View 层发出的 Action,然后通过内部操作,将 Action 转换为 State。)

(2)定义一个 Reactor 需要遵守 Reactor 协议,该协议定义了如下内容:
  • 四个响应属性:ActionMutationStateinitialState
  • 两个响应方法:mutate()reduce()

(3)这些响应属性和响应方法的作用,以及相互关系如下:
  • Action:描述用户行为
  • Mutation:描述状态变更( 它可以看作是 Action State 的桥梁)
  • State:描述当前状态
  • initialState:描述初始化状态
  • mutate():处理 Action 执行一些业务逻辑,并转换为 Mutation
  • reduce(): 通过旧的 State 以及 Mutation 创建一个新的 State

3,View 代码结构

(1)View 为数据展示层,不管是 UIViewController 还是 UIView 都可以看作是 ViewView 主要负责发出 Action,同时将 State 绑定到 UI 组件上。

(2)定义一个 View 只需要让它遵循 ReactorKit View StoryboardView 协议即可:
  • 如果 ViewController 是纯代码开发的:则其遵守 View 协议。
  • 如果 ViewController Storyboard 开发的:则其遵守 StoryboardView 协议。

(3)View 中需要定义如下内容:
  • disposeBag 属性:协议属性。当 View reactor 变化时,之前的 disposeBag 会自动 disposed
  • bind(reactor:) 方法:实现用户输入绑定和状态输出绑定。

(4)协议中的 bind() 方法不需要我们手动去调用。遵循 View 协的类将自动获得一个 reactor 属性。当 View reactor 属性被设置时,bind() 方法就会被自动调用。而 reactor 属性的设置又分两种情况:
  • 纯代码开发的 ViewController 则在其创建实例对象后,直接注入 reactor
let detailViewController = DetailViewController()
detailViewController.reactor = DetailViewReactor() //设置reactor
import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController, StoryboardView {

    var disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //设置reactor,会自动触发bind()方法
        self.reactor = ViewReactor()
    }
    
    //处理绑定事件(该方法会在 self.reactor 变化时自动触发)
    func bind(reactor: ViewReactor) {
       
    }
}

三、一个简单的样例

1,效果图

(1)界面上有 1 个文本标签(label)、2 个按钮(button)、1 个活动指示器(activityIndicatorView)。
(2)文本标签显示显示一个初始数字 0
(3)点击按钮可以对这个初始数字进行“加1”或“减1”操作。
(4)为模拟异步操作,每次对数字的“加1”或“减1”操作都会延迟 1 秒钟,同时下方会显示出活动指示器。等到数字改变后,活动指示器又自动消失。
(5)在上一次“加1”或“减1”操作结束前(活动指示器还未消失),多次点击按钮是无效的。
            

2,准备工作

(1)首先我们在 storyboard 中添加 2 个按钮、1 个文本标签。
(2)接着在文本标签下方放置一个 Activity Indicator View,同时设置当其动画停止时自动隐藏。
(3)最后将这个 4UI 控件与代码做 @IBOutlet 关联。

3,样例代码

(1)ViewReactor.swift(反应器)
import RxSwift

final class ViewReactor: Reactor {
    //代表用户行为
    enum Action {
        case increase //加一操作
        case decrease //减一操作
    }
    
    //代表状态变化
    enum Mutation {
        case increaseValue //数字加一
        case decreaseValue //数字减一
        case setLoading(Bool) //设置加载状态
    }
    
    //代表页面状态
    struct State {
        var value: Int //当前数字
        var isLoading: Bool  //当前加载状态
    }
    
    //初始页面状态
    let initialState: State
    
    init() {
        self.initialState = State(
            value: 0, //数字默认为0
            isLoading: false  //默认加载状态为false
        )
    }
    
    //实现 Action -> Mutation 的转换
    func mutate(action: Action) -> Observable<Mutation> {
        switch action {
        case .increase:
            //如果当前正在加载中则不继续
            guard !self.currentState.isLoading else { return Observable.empty() }
            //依次执行下面三个状态变化动作
            return Observable.concat([
                Observable.just(Mutation.setLoading(true)),
                Observable.just(Mutation.increaseValue)
                    .delay(1, scheduler: MainScheduler.instance), //数字改变延迟1秒钟
                Observable.just(Mutation.setLoading(false)),
                ])
            
        case .decrease:
            //如果当前正在加载中则不继续
            guard !self.currentState.isLoading else { return Observable.empty() }
            //依次执行下面三个状态变化动作
            return Observable.concat([
                Observable.just(Mutation.setLoading(true)),
                Observable.just(Mutation.decreaseValue)
                    .delay(1, scheduler: MainScheduler.instance),  //数字改变延迟1秒钟
                Observable.just(Mutation.setLoading(false)),
                ])
        }
    }
    
    //实现 Mutation -> State 的转换
    func reduce(state: State, mutation: Mutation) -> State {
        //从旧状态那里复制一个新状态
        var state = state
        
        //根据状态变化设置响应的状态值
        switch mutation {
        case .increaseValue:
            state.value += 1
            
        case .decreaseValue:
            state.value -= 1
            
        case let .setLoading(isLoading):
            state.isLoading = isLoading
        }
        
        //返回新状态
        return state
    }
}

(2)ViewController.swift(视图)
import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController, StoryboardView {
    
    @IBOutlet weak var label: UILabel!  //显示数字的文本标签
    @IBOutlet weak var minus: UIButton! //减一按钮
    @IBOutlet weak var plus: UIButton! //加一按钮
    @IBOutlet weak var activityIndicator: UIActivityIndicatorView!  //活动指示器
    
    var disposeBag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //设置reactor,会自动触发bind()方法
        self.reactor = ViewReactor()
    }
    
    //处理绑定事件(该方法会在 self.reactor 变化时自动触发)
    func bind(reactor: ViewReactor) {

        //Action(实现 View -> Reactor 的绑定)
        plus.rx.tap  //按钮点击事件
            .map { Reactor.Action.increase }  //转换为 Action.increase
            .bind(to: reactor.action)  //绑定到 reactor.action
            .disposed(by: disposeBag)
        
        minus.rx.tap  //按钮点击事件
            .map { Reactor.Action.decrease }  //转换为 Action.increase
            .bind(to: reactor.action)  //绑定到 reactor.action
            .disposed(by: disposeBag)
        
        // State(实现 Reactor -> View 的绑定)
        reactor.state.map { $0.value }  //得到最新数字值
            .distinctUntilChanged()
            .map { "\($0)" }   //装成字符串
            .bind(to: label.rx.text)  //绑定到文本标签上
            .disposed(by: disposeBag)
        
        reactor.state.map { $0.isLoading } //活动指示器的绑定
            .distinctUntilChanged()
            .bind(to: activityIndicator.rx.isAnimating)
            .disposed(by: disposeBag)
    }
}
评论

全部评论(0)

回到顶部