本文主要是RxSwift Demo的体验
RxSwift 介绍
-
RxSwift = ReactiveX + Swift
- ReactiveX(简称 Rx):是一个可以帮助我们简化异步编程的框架
- Swift:表示是Rx的swift版本
-
RxSwift 作为一个响应式编程框架,有以下两点好处:
-
聚合逻辑
:即通过RxSwift提供的各种操作符实现逻辑的聚合 -
响应式编程
:即将所有事件都描述成一个可监听的序列,并提供大量功能各异的操作符,通过声明式的语句来完成数据的获取,转换,结合及绑定
-
为什么要使用RxSwift?
-
复合
- Rx 就是复合的代名词 -
复用
- 因为它易复合 -
清晰
- 因为声明都是不可变更的 -
易用
- 因为它抽象了异步编程,使我们统一了代码风格 -
稳定
- 因为 Rx 是完全通过单元测试的 -
高大上
- 代码档次比原生高很多
如果对于响应式编程还不了解的同学,可以先阅读# iOS 底层原理37:链式编程 中对响应式编程的介绍
初体验
通过 Cocoapods 导入
pod 'RxSwift', '6.5.0'
pod 'RxCocoa', '6.5.0'
RxSwift 的使用步骤分为以下几步:
- 创建序列(
万物皆可 rx
) - 订阅信号
- 发送信号
- 输出结果
主要从以下几方面来体验
- KVO
- Target-Action:UIbutton
- 代理:delegate
- 手势
- 通知:Notification
- timer
- 网络请求:URLSession
- 多个任务间有依赖关系
- 等待多个并发任务完成后处理结果
KVO
在Swift中实现 OC 的KVO
- 传统实现
KVO 三部曲
- 监听
- 响应
- 销毁
class Person: NSObject {
//由于swift是静态语言,而KVO是运行时发生的,所以需要加 dynamic 关键字
@objc dynamic var name: String = "CJL"
}
//1-设置监听
func setupKVO(){
self.person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
}
//2-回调
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
print("响应")
print(change as Any)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("来了")
person.name = "\(person.name) 6"
print(person.name)
}
//3-移除监听
deinit{
self.removeObserver(self, forKeyPath: "name", context: nil)
}
- Rx实现
func setupKVO(){
self.person.rx.observeWeakly(String.self, "name")
.subscribe { value in
print(value as Any)
}
.disposed(by: disposeBag)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("来了")
person.name = "\(person.name) 6"
}
Target-Action
这里以
UIButton
点击事件为例
- 传统实现
var button: UIButton = {
let btn = UIButton(type: .custom)
btn.frame = CGRect(x: 100, y: 100, width: 200, height: 100)
btn.setTitle("按钮点击", for: .normal)
btn.backgroundColor = UIColor.lightGray
return btn
}()
func setupButton(){
button.addTarget(self, action: #selector(didClickButton), for: .touchUpInside)
}
@objc func didClickButton(){
print("点击按钮")
}
- Rx实现
func setupButton(){
button.rx.controlEvent(.touchUpInside)
.subscribe { _ in
print("点击事件")
}
.disposed(by: disposeBag)
}
代理
这里以
UITextFiledDelegate
代理方法为例
- 传统实现
func textFieldDidChangeSelection(_ textField: UITextField) {
print(textField.text)
}
- Rx实现
func setupTextField(){
textFiled.rx.text.orEmpty.changed
.subscribe { text in
print(text)
}
.disposed(by: disposeBag)
}
此时还可以将 TextField 跟 button进行绑定,将textfiled输入的文本作为 button的标题
func setupTextField(){
//textFiled跟button绑定,输入的内容作为button的title
textFiled.rx.text
.bind(to: button.rx.title())
.disposed(by: disposeBag)
}
手势
这里以
UITapGestureRecognizer
点击手势为例
- 传统实现
func setupGestureRecongizer(){
let tap = UITapGestureRecognizer()
self.view.addGestureRecognizer(tap)
self.view.isUserInteractionEnabled = true
tap.addTarget(self, action: #selector(tapAction(_:)))
}
@objc func tapAction(_ tap: UITapGestureRecognizer){
print(tap.view)
}
- Rx实现
func setupGestureRecongizer(){
let tap = UITapGestureRecognizer()
self.view.addGestureRecognizer(tap)
self.view.isUserInteractionEnabled = true
tap.rx.event
.subscribe { tap in
print(tap.view)
}
.disposed(by: disposeBag)
}
通知
这里以
keyboardWillShowNotification
(键盘弹起通知)为例
- 传统实现
func setupNotification(){
// 监听键盘弹出通知
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name:UIResponder.keyboardWillShowNotification,object: nil)
}
@objc func keyboardWillShow(_ noti: Notification){
print("键盘弹起")
}
- Rx实现
func setupNotification(){
// 监听键盘弹出通知
NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
.subscribe { noti in
print("键盘弹起")
}
.disposed(by: disposeBag)
}
timer
实现计时器功能
- 传统实现
5s倒计时
func setupTimer(){
var countDownNum = 5
let countdownTimer = Timer(timeInterval: 1.0, repeats: true) { timer in
if countDownNum == 0 {
// 销毁计时器
timer.invalidate()
// countDownNum = 5
print(">>> Timer has Stopped!")
} else {
print(">>> Countdown Number: \(countDownNum)")
countDownNum -= 1
}
}
// 设置宽容度
countdownTimer.tolerance = 0.2
// 添加到当前 RunLoop,mode为默认。
RunLoop.current.add(countdownTimer, forMode: .default)
// 开始计时
countdownTimer.fire()
}
- Rx实现
正向计时
注:RxSwift 中的 timer并不是我们常见的几种timer定义的,而是自定义的,即不断创建序列,发送信号来实现的定时器效果,这个后面篇章会详细讲解
func setupTimer(){
var timer: Observable<Int> = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
timer.subscribe { num in
print(num)
}
.disposed(by: disposeBag)
}
网络请求
- 传统实现
func setupNetwork() {
let url = URL(string: "https://www.baidu.com")
URLSession.shared.dataTask(with: url!) { data, response, error in
print(String.init(data: data!, encoding: .utf8)!)
}.resume()
}
- Rx实现
func setupNetwork() {
let url = URL(string: "https://www.baidu.com")
URLSession.shared.rx.response(request: URLRequest(url: url))
.subscribe { response, data in
print(response)
}
.disposed(by: disposeBag)
}
多个任务间有依赖关系
通过用户名密码取得 Token 然后通过 Token 取得用户信息,
- 传统实现
// 用回调的方式封装接口
enum API {
/// 通过用户名密码取得一个 token
static func token(username: String, password: String,
success: (String) -> Void,
failure: (Error) -> Void) { }
/// 通过 token 取得用户信息
static func userinfo(token: String,
success: (String) -> Void,
failure: (Error) -> Void) { }
}
func setupTaskDependency(){
/// 通过用户名和密码获取用户信息
API.token(username: "111", password: "222") { token in
API.userinfo(token: token) { userinfo in
print("获取用户信息成功: \(userinfo)")
} failure: { error in
print("获取用户信息失败: \(error)")
}
} failure: { error in
print("获取用户信息失败: \(error)")
}
}
- Rx实现
避免了回调地狱,使得代码易读、已维护
/// 用 Rx 封装接口
enum API {
/// 通过用户名密码取得一个 token
static func token(username: String, password: String) -> Observable<String> {
return Observable<String>.create{ observer in
observer.onNext("1")
return Disposables.create()
}
}
/// 通过 token 取得用户信息
static func userInfo(token: String) -> Observable<String> {
return Observable<String>.create{ observer in
observer.onNext("2")
return Disposables.create()
}
}
}
func setupTaskDependency(){
API.token(username: "111", password: "222")
.flatMapLatest(API.userInfo)
.subscribe(onNext: { userInfo in
print("获取用户信息成功: \(userInfo)")
}, onError: { error in
print("获取用户信息失败: \(error)")
})
.disposed(by: disposeBag)
}
等待多个并发任务完成后处理结果
需要将两个网络请求合并成一个
- 传统实现
let queue1:DispatchQueue = DispatchQueue.init(label: "textQueue1")
let queue2:DispatchQueue = DispatchQueue.init(label: "textQueue2")
// 队列queue加入队列组
queue1.async (group: self.gropQueue){
/*任务1*/
print("获取老师信息接口")
}
queue2.async (group: self.gropQueue){
/*任务2*/
print("获取老师评论接口")
}
/*2个异步任务完成后执行界面处理操作*/
self.gropQueue.notify(queue: DispatchQueue.main) {
/*界面处理*/
print("统一处理")
}
- Rx实现
用几行代码实现复杂的异步操作
/// 用 Rx 封装接口
enum API2 {
/// 取得老师的详细信息
static func teacher(teacherId: Int) -> Observable<String> {
return Observable<String>.create{ observer in
observer.onNext("取得老师的详细信息")
return Disposables.create()
}
}
/// 取得老师的评论
static func teacherComments(teacherId: Int) -> Observable<[String]> {
return Observable<[String]>.create{ observer in
observer.onNext(["取得老师的评论"])
return Disposables.create()
}
}
}
func setupConcurrentTasks(){
/// 同时取得老师信息和老师评论
Observable.zip(
API2.teacher(teacherId: 110),
API2.teacherComments(teacherId: 130)
).subscribe(onNext: { (teacher, comments) in
print("获取老师信息成功: \(teacher)")
print("获取老师评论成功: \(comments.count) 条")
}, onError: { error in
print("获取老师信息或评论失败: \(error)")
})
.disposed(by: disposeBag)
}
疑问点
从上面的实现看,主要关注以下方面:
- 其他对象调用的 rx 到底是什么?为什么都可以调用 rx?
- timer 的创建为什么跟其他对象有所不同?
- Observable、Observer是什么?
其他对象调用的 rx 到底是什么?为什么都可以调用 rx?
- 点击 rx 进入其源码实现
/// A type that has reactive extensions.
public protocol ReactiveCompatible {
/// Extended type 关联属性
associatedtype ReactiveBase
/// Reactive extensions.
static var rx: Reactive<ReactiveBase>.Type { get set }
/// Reactive extensions.
var rx: Reactive<ReactiveBase> { get set }
}
//协议的扩展
extension ReactiveCompatible {
/// Reactive extensions.
public static var rx: Reactive<Self>.Type {
get { Reactive<Self>.self }
// this enables using Reactive to "mutate" base type
// swiftlint:disable:next unused_setter_value
set { }
}
/// Reactive extensions.
public var rx: Reactive<Self> {
get { Reactive(self) }
// this enables using Reactive to "mutate" base object
// swiftlint:disable:next unused_setter_value
set { }
}
}
import Foundation
// NSObject 遵循了 rx 所在的协议
/// Extend NSObject with `rx` proxy.
extension NSObject: ReactiveCompatible { }
- 从源码可以看出 rx 是 ReactiveCompatible 协议的属性,而NSObject 遵循了 ReactiveCompatible,而我们常说
万物皆对象
,对象溯源到底最终都指向 NSObject(即 rx 是 NSObject的扩展),所以这里得出一个结论:万物即可 rx
其余问题我们将在下一节来进行一一探索
网友评论