RxSwift初步探索

266 阅读3分钟

前言

写了不少swift代码,用rxswift了不少了,但是很多时候只是做代码的搬运工,只是会简单的使用,甚至有时候也一知半解的乱用,写的代码跑出来的结果跟预想的不太一样,应为对其原理一知半解,所以也不太好调试。终于笔者鼓起勇气打算开始系统的学一学,理一理rxswift的原理。

函数响应式

函数式

函数式的思维是把显示世界的事物和事物之间的联系抽象到程序世界(对运算过程进行抽象) 函数式具有以下特点

  • 函数是头等公民,函数可以作为参数
  • 所有的改变都在只在函数内部,不会改变任何东西,高内聚
  • 所有变量只有一次赋值,所有函数的输出是不可变

响应式

函数式的思维是对数据流或者某种变化做出反应,一样东西发生了变化,另一样东西跟着变就是发生了响应

具体我看个案例,我们有需求:对一个数组的所有元素进行如下操作,

  1. 对所有元素加1
  2. 取出>4的元素
  3. 取出所有的偶数

按照传统的思路可以写出如下代码:

  let array = [1, 2, 3, 4, 5, 6, 7]
  for num in array {
    let number = num + 1
    if num > 4 {
      if (number % 2 == 0) {
        print(number)
      }
    }
  }

按照rx的思路可以写出如下代码:

    array.map { $0 + 1 }
      .filter { $0 > 4}
      .filter { $0 % 2 == 0 }
      .forEach { print($0) }

如果我的需求2条件和3条件换个位置,那么传统版本调整的代码比rx的比较大。 Rx有以下优点:复合复用清晰易用稳定

RX体验

我们有如下界面和需求:

 /**
 * 当用户输入用户名时,如果用户名不足 5 个字就给出红色提示语,并且无法输入密码,当用户名符合要求时才可以输入密码。

 * 同样的,当用户输入的密码不足 6 个字时也给出红色提示语。当用户名和密码有一个不符合要求时底部的登录按钮不可点击,只有当用户名和密码同时有效时按钮才可点击。

 * 当点击登录按钮后弹出一个提示框,这个提示框只是用来做演示而已。
 */

image.png

用rx实现

 // 将text序列通过orEmpty(nil map成"")
      // 转成符合条件的bool序列
      let usernameValid = usernameTextFiled.rx.text.orEmpty
        .map {
          $0.count >= 5
        }
      // usernameValid序列绑定到usernameTextFiled的isHidden
      usernameValid.bind(to: usernameValidLabel.rx.isHidden)
        .disposed(by: disposeBag)
     
      let passwordValid = passwordTextFiled.rx.text.orEmpty
        .map {
          $0.count >= 6
        }
      passwordValid.bind(to: passwordValidLabel.rx.isHidden)
        .disposed(by: disposeBag)
      
      //组合usernameValid和passwordValid最新的状态输出同时满足的bool序列
      //bool序列绑定到按钮的isEnabled
      Observable.combineLatest(usernameValid, passwordValid) {
        $0 && $1
      }
      .bind(to: loginBtn.rx.isEnabled)
      .disposed(by: disposeBag)
      
      loginBtn.rx.tap.asObservable()
        .subscribe { _ in
          print("login")
        }
        .disposed(by: disposeBag)

.rx的方法是啥,我们点进去

image.png 有个ReactiveCompatible的协议,有rx的实例属性和类属性的,分别返回Reactive这个结构体和结构体的类型。并且很有意思的是给NSObject加了拓展,继承了ReactiveCompatible的协议,此也是万物皆序列的证据之一,另外的证据后面也会设计到。

继续举几个例子:

//定时器,无线序列,没隔1s发一个元素
  Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
    .subscribe { event in
      print(event)
    }
    .disposed(by: disposeBag)
  
  // 监听UIScrollView的contentOffset
  UIScrollView().rx.contentOffset
    .subscribe { e in
      print(e)
    }
    .disposed(by: disposeBag)
  
  // 监听UITextField的text的变化
  UITextField().rx.text.orEmpty.changed
    .subscribe { text in
      print(text)
    } onError: { e in
      print(e)
    }
    .disposed(by: disposeBag)

  // 监听UIButton的touchDown事件
  UIButton().rx.controlEvent(.touchDown)
    .subscribe { _ in
      print("tap")
    }
    .disposed(by: disposeBag)
  
  // 是个ControlProperty
  UISwitch().rx.value
    .subscribe { bool in
      print(bool)
    }
    .disposed(by: disposeBag)

  // 网络请求,并且返回的结果和错误分层很好,不用再写if判断
  URLSession.shared.rx.response(request: URLRequest(url: URL(string: "")!))
    .subscribe { res, data in
      print(data)
    } onError: { err in
      print(err)
    } onCompleted: {
      print("onCompleted")
    } onDisposed: {
      print("onCompleted")
    }
    .disposed(by: disposeBag)

我们看到rx的用途很广泛

RxSwift核心流程

image.png RxSwift有三部曲,创建,订阅,发送信号,通过匿名序列管道做中间介来传递消息,具体流程下篇文章详细分解

总结

  • RxSwift是函数响应式的,具有复合,复用,清晰,易用,稳定的优点
  • NSObject继承了ReactiveCompatible的协议,里面有rx的类属性和对象属性,万物皆序列
  • RxSwift有三部曲,创建,订阅,发送信号