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 in
                        print("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 RxSwift

typealias 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.frame
        self.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()
        })
    }
    // addtarget
    func setupButton() {
        button.setTitle("登录", for: .normal)
        button.backgroundColor = UIColor.orange
        button.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)
    }
    // delegate
    func setupUIScrollView() {
        scrollView.frame = CGRect(x: 0, y: 100, width: 390, height: 1000)
        scrollView.backgroundColor = UIColor.red
        scrollView.rx.contentOffset
                    .subscribe(onNext: { contentOffset in
                        print("contentOffset: \(contentOffset)")
                    })
        self.view.addSubview(scrollView)
    }
    // observable
    func netWorkToGetJson() {
        let json: Observable<JSON> = Observable.create { (observer) -> Disposable in
            let 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 in
                guard 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 in
            print("getJsonSuccess: \(json)")
        },onError: { error in
            print("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 UIKit

class 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.white
        nameTextField.placeholder = "please input your name"
        self.addSubview(nameTextField)
        passwordTextField.frame = CGRect(x: 20, y: 250, width: 250, height: 30)
        passwordTextField.backgroundColor = UIColor.white
        passwordTextField.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.green
        self.addSubview(loginButton)
    }
}

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

你可能感兴趣的:(rxswift,swift,ios)