> 文章列表 > RxSwift(一)初窥门径

RxSwift(一)初窥门径

RxSwift(一)初窥门径

前言

这几天学习了一些关于响应式框架的知识,由于 Combine 不支持 iOS13.0以下,所以先学习他的前辈 RxSwift 记录一下。

什么是 RxSwift?

RxSwift 是一个 Rx 基于 Swift 的函数响应式框架,配合 swift 语言的特性,可以写出来更简洁可读性高的代码。
毕竟关键的两个点在于函数响应式,下面一一来解释一下。

函数式编程 & 编程范式

首先对于函数肯定很熟悉了,这里的函数并不是我们想当然认为的函数,而是函数式编程
函数式编程是一种编程范式,我们平常常用的面向对象编程,以及面向对象编程都是命令式编程,怎么解释呢?
命令式编程由一组详细的指令组成,让计算机以一定的顺序执行。之所以被称作“命令式”是因为开发者以非常具体的方式,准确地规定计算机必须做什么。
命令式编程强调描述程序怎么样一步一步地运行。 举个🌰,现在有一个数组,取出其中大于4的元素我们,并且在其中取出是偶数的数,我们正常会使用 for 循环来写。
命令式编程

let array = [1,2,3,4,5,6,7,8,9,10]var tempArray = []for number in array {if (number > 4 && number % 2 == 0) {tempArray.append(number)}}

我们会写一个有顺序的程序,一步一步告诉计算机该怎么做?

下面试试函数式编程

let array = [1,2,3,4,5,6,7,8,9,10]
var tempArray = array.filter{$0 > 4 && $0 % 2 == 0}

我们告诉了计算机,从数组中过滤出大于4并且是偶数的数。我们无需告诉计算机怎么做?只需要告诉计算机干什么。函数式编程也是声明式编程的子集。
函数式编程: 在函数式编程中,函数被认为是一等公民,意味着可以将它们赋值给变量,作为参数传入其他函数,或者由函数返回,通过函数的组合解决问题。RxSwift 就为我们提供了很多高阶函数去处理流。

响应式编程

响应式编程有三个核心概念 数据流,函数式编程,异步观察
数据流:比如说我们常用的 UITextField,假设我们在其中输入文字 “chabuduoxs”,在联想搜索的时候我们会监听输入文字的改变,会接受到 c-ch-cha-chab-chabu-…-chabuduoxs的这样一系列数据,实际上这样的一系列数据就形成了一个数据流,计算机的输入和读取其实都是流。
函数式编程我们提过了,那么异步观察又是什么呢?
实际上就是数据流变化,观察者监听做出对应操作的过程,只不过数据流只关心自己可不可以发送消息,而不管观察者是否能够响应,在 iOS 中我们经常会响应一些事件,比如说 button,tap等。在原生开发中触发对象和响应方法是分离的,尤其是经典的 KVO 三部曲,非常折磨。

RxSwift 能做什么?

首先就帮我们简化原来繁琐的代码。

button点击事件

原先的写法:

 	testbutton.addTarget(self, action: pressButton(), for: .touchUpInside)func pressButton() {print("button click")}

RxSwift:

 	testbutton.rx.tap.subscribe(onNext: {print("button click")})

scrollView 代理

原先的话需要先代理,然后重写方法。
RxSwift:

 scrollView.rx.contentOffset.subscribe(onNext: { contentOffset inprint("contentOffset: \\(contentOffset)")})

还有通知等方法都可以大大简化。

Demo

看 API 还是太干了,写个Demo感受一下。
场景如下:首先是一个登录注册页面,当用户输入的用户名合法时,密码才允许输入,当账号和密码都符合要求时,登录按钮就可以被点击,从而完成登录操作。
如果不用 RxSwift 可能是这样写的,先实现 textfield 的代理,重写 textfield 的代理方法,然后在里面做判断 if…然后解锁密码框的可用状态,再监听密码框,这块还得做一下区分,究竟是哪个 textField,然后怎么怎么样,非常繁琐。
如果用RxSwift呢?

	func configIsLogin() {let usernameValid = loginView.nameTextField.rx.text.orEmpty.map{$0 == "Chabuduoxs"}.share(replay: 1)usernameValid.bind(to: loginView.passwordTextField.rx.isEnabled)let passwordValid = loginView.passwordTextField.rx.text.orEmpty.map{$0 == "123456"}.share(replay: 1)let everythingValid = Observable.combineLatest(usernameValid, passwordValid) {$0 && $1}.share(replay: 1)everythingValid.bind(to: loginView.loginButton.rx.isEnabled)loginView.loginButton.rx.tap.subscribe(onNext: {print("登录成功")self.netWorkToGetJson()})}

仅仅需要这几行代码就可以实现了这个逻辑了,非常奇妙的体验,完整代码如下,可以感受一下:

//
//  ViewController.swift
//  Rx1
//
//  Created by wangbo.almost on 2023/4/7.
//import UIKit
import RxCocoa
import RxSwifttypealias JSON = Any
class ViewController: UIViewController {var button: UIButton = UIButton()var scrollView: UIScrollView = UIScrollView()var loginView: LoginView = LoginView()override func viewDidLoad() {super.viewDidLoad()// Do any additional setup after loading the view.self.setupLoginView()self.configIsLogin()}func setupLoginView() {loginView.frame = self.view.frameself.view.addSubview(loginView)}func configIsLogin() {let usernameValid = loginView.nameTextField.rx.text.orEmpty.map{$0 == "Chabuduoxs"}.share(replay: 1)usernameValid.bind(to: loginView.passwordTextField.rx.isEnabled)let passwordValid = loginView.passwordTextField.rx.text.orEmpty.map{$0 == "123456"}.share(replay: 1)let everythingValid = Observable.combineLatest(usernameValid, passwordValid) {$0 && $1}.share(replay: 1)everythingValid.bind(to: loginView.loginButton.rx.isEnabled)loginView.loginButton.rx.tap.subscribe(onNext: {print("登录成功")self.netWorkToGetJson()})}// addtargetfunc setupButton() {button.setTitle("登录", for: .normal)button.backgroundColor = UIColor.orangebutton.setTitleColor(.black, for: .normal)button.frame = CGRect(x: 100, y: 100, width: 100, height: 30)button.rx.tap.subscribe(onNext: {print("button tapped")})self.view .addSubview(button)}// delegatefunc setupUIScrollView() {scrollView.frame = CGRect(x: 0, y: 100, width: 390, height: 1000)scrollView.backgroundColor = UIColor.redscrollView.rx.contentOffset.subscribe(onNext: { contentOffset inprint("contentOffset: \\(contentOffset)")})self.view.addSubview(scrollView)}// observablefunc netWorkToGetJson() {let json: Observable<JSON> = Observable.create { (observer) -> Disposable inlet urlString:String = "https://www.baidu.com"let url = URL(string: urlString)let urlRequest = URLRequest(url: url!)let task = URLSession.shared.dataTask(with: urlRequest) {data, _, error inguard error == nil else {observer.onError(error!)return}guard let data = data, let jsonObject = try?JSONSerialization.jsonObject(with: data, options: .mutableLeaves) else {// 处理json异常 return}observer.onNext(jsonObject)observer.onCompleted()}task.resume()return Disposables.create{task.cancel()}}json.subscribe(onNext: { json inprint("getJsonSuccess: \\(json)")},onError: { error inprint("getJsonFailed: \\(error)")},onCompleted: {print("getJsonCompleted")})}func handleArray(_ array:Array<Int>) -> Array<Int>{var tempArray = array.filter{$0 > 4 && $0 % 2 == 0}return tempArray}
}
//
//  LoginView.swift
//  Rx1
//
//  Created by wangbo.almost on 2023/4/7.
//import UIKitclass LoginView: UIView {var nameLabel: UILabel = UILabel()var passwordLabel: UILabel = UILabel()var nameTextField: UITextField = UITextField()var passwordTextField: UITextField = UITextField()var loginButton: UIButton = UIButton(type: .roundedRect)override init(frame: CGRect) {super.init(frame: frame)self.setupView()}required init?(coder aDecoder: NSCoder) {super.init(coder: aDecoder)self.setupView()}func setupView() {nameLabel.text = "UserName"nameLabel.font = UIFont.systemFont(ofSize: 30)nameLabel.frame = CGRect(x: 20, y: 80, width: 200, height: 30)self.addSubview(nameLabel)passwordLabel.text = "Password"passwordLabel.font = UIFont.systemFont(ofSize: 30)passwordLabel.frame = CGRect(x: 20, y: 210, width: 200, height: 30)self.addSubview(passwordLabel)nameTextField.frame = CGRect(x: 20, y: 120, width: 250, height: 30)nameTextField.backgroundColor = UIColor.whitenameTextField.placeholder = "please input your name"self.addSubview(nameTextField)passwordTextField.frame = CGRect(x: 20, y: 250, width: 250, height: 30)passwordTextField.backgroundColor = UIColor.whitepasswordTextField.placeholder = "please input your password"self.addSubview(passwordTextField)loginButton.frame = CGRect(x: 120, y: 320, width: 150, height: 40)loginButton.setTitle("Login", for: .normal)loginButton.setTitleColor(.blue, for: .normal)loginButton.backgroundColor = UIColor.greenself.addSubview(loginButton)}
}

这块还有一段网络请求的优化部分,放到下一次具体分析RxSwift时分析,后续会持续完善,本次仅仅体验一下,小小的demo已经可以感受到搭配swift会很简洁。