前言
这几天学习了一些关于响应式框架的知识,由于 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: ObservableJSON> = 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:ArrayInt>) -> ArrayInt>{
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会很简洁。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net