RxSwift
ps: 学习笔记只是随便记一记,加快学习速度和映像,没有参考意义,因为其实官方文档的每个行都是重点。
TODOs:
- [√] 过一遍文档
- 过一遍playgound和单元测试相关内容
- 学习demo,抄一遍吧,映像会很深刻
- 详细研究内容,先参考文档,不懂的再查资料
- 应用实战
- 《RxSwift Reactive Programming with Swift》这本书真神,解答一切疑惑,尤其是实战上的,要看完
ps: 没有实战研究太枯燥,过一遍文档后,会一边实战一边研究
why
Rx支持以声明方式构建apps。
-
view和data绑定
-
失败重试: 比如网络请求失败重试;
-
Delegates
-
KVO
-
Notifications
-
Transient state: 以搜索为例,程序中有很多的状态需要处理,而RX可以轻松解决,怎么解决的要有空再看看;
-
Compositional disposal: 以列表中的cell图片显示为例,程序中有很多细节处理,,而RX可以轻松解决,怎么解决的要有空再看看;
-
Aggregating network requests: 请求的合并;
-
State: 单向数据流?? 没搞懂;
-
Easy integration: 易于集成,以URLSession为例,介绍了怎么讲请求转化Observable
extension Reactive where Base: URLSession {
public func response(request: URLRequest) -> Observable<(Data, HTTPURLResponse)> {
return Observable.create { observer in
let task = self.base.dataTask(with: request) { (data, response, error) in
guard let response = response, let data = data else {
observer.on(.error(error ?? RxCocoaURLError.unknown))
return
}
guard let httpResponse = response as? HTTPURLResponse else {
observer.on(.error(RxCocoaURLError.nonHTTPResponse(response: response)))
return
}
observer.on(.next(data, httpResponse))
observer.on(.completed)
}
task.resume()
return Disposables.create(with: task.cancel)
}
}
}
- Benefits
简单来说,使用Rx可以是你的代码:
- 可组合 <- 因为Rx是作曲的昵称
- 可重复使用的 <- 因为它是可组合的
- 声明性 <- 因为定义是不可变的,只有数据发生变化
- 可理解和简洁 <- 提高抽象级别并消除瞬态
- 稳定 <- 因为Rx代码经过了彻底的单元测试
- 较少有状态 <- 因为您将应用程序建模为单向数据流
- 没有泄漏 <- 因为资源管理很容易
- It’s not all or nothing
Getting Started
- Observables aka Sequences
Every Observable sequence is just a sequence. The key advantage for an Observable vs Swift’s Sequence is that it can also receive elements asynchronously.This is the kernel of RxSwift, documentation from here is about ways that we expand on that idea.
- Observable(ObservableType) is equivalent to Sequence
- ObservableType.subscribe method is equivalent to Sequence.makeIterator method.
- Observer (callback) needs to be passed to ObservableType.subscribe method to receive sequence elements instead of calling next() on the returned iterator.
貌似是说Observable sequence其实就是sequence,只是能接收异步的元素,所以会有一堆函数式的操作,比如map,filter等等;而subscribe就相当于遍历。
enum Event<Element> {
case next(Element) // next element of a sequence
case error(Swift.Error) // sequence failed with error
case completed // sequence terminated successfully
}
class Observable<Element> {
func subscribe(_ observer: Observer<Element>) -> Disposable
}
protocol ObserverType {
func on(_ event: Event<Element>)
}
- Disposing
另一种方式终止observed sequence
2.1 Dispose Bags
Dispose bags are used to return ARC like behavior to RX.
When a DisposeBag is deallocated, it will call dispose on each of the added disposables.
好像是被用于ARC。比如一个ViewController
可以申明属性let disposeBag = DisposeBag()
, 这样ViewController
销毁,导致disposeBag销毁,这样就清空所有的observerd sequence
2.2 Take until
Additional way to automatically dispose subscription on dealloc is to use takeUntil operator.
sequence
.takeUntil(self.rx.deallocated)
.subscribe {
print($0)
}
- Implicit Observable guarantees
subscribe的执行过程不会被任何event打断
- Creating your own Observable (aka observable sequence)
Observable的创建不会执行创建外的任何逻辑,直到subscribe
func searchWikipedia(searchTerm: String) -> Observable<Results> {}
let searchForMe = searchWikipedia("me") // no requests are performed, no work is being done, no URL requests were fired
let cancel = searchForMe
// sequence generation starts now, URL requests are fired
.subscribe(onNext: { results in
print(results)
})
create Observable
func myJust<E>(_ element: E) -> Observable<E> {
return Observable.create { observer in
observer.on(.next(element))
observer.on(.completed)
return Disposables.create()
}
}
myJust(0)
.subscribe(onNext: { n in
print(n)
})
- Creating an Observable that performs work
demo
- Sharing subscription and share operator
-
demo
-
share与非share的区别
share:每次subscribe,会共用create闭包的内容,即多次subscribe,create闭包不会执行多次 非share:每次subscribe,都会独立执行create闭包的内容,各不干扰
- share的作用:很多observable,会bind多次,比如SimpleValidationViewController.swift中,用户名输入框的校验,即绑定密码框是否可以,又绑定用户名提示是否显示,share可以共用事件,实现事件发出1次,多个observer监听,并实现业务逻辑绑定,避免无意义的校验事件多次发出
subscribe
- Operators
Rx.playground有演示,另外,Rx.playground是很好的使用playground的模板
7.1 Custom operators
通过demo看,还蛮简单
7.2 Life happens
没看懂
-
Playgrounds
-
Error handling
没看懂
- Debugging Compile Errors
编译错误,说的是swift类型推断偶尔抽风
- Debugging
介绍了使用debug operator
11.1 Enabling Debug Mode
这个要参考一下
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == 'RxSwift'
target.build_configurations.each do |config|
if config.name == 'Debug'
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['-D', 'TRACE_RESOURCES']
end
end
end
end
end
- Debugging memory leaks
没看懂
- KVO
13.1 rx.observe
监听strong属性
13.2 rx.observeWeakly
监听weak属性
13.3 Observing structs
RxCocoa仅做好了CGRect, CGSize and CGPoint的监听
- UI layer tips
14.1 Threading
必须在主线程更新UI,observeOn(MainScheduler.instance)
URLSession extensions don’t return result on MainScheduler by default.
14.2 Error
没看懂
14.3 Sharing subscription
没看懂share操作时间干啥的
-
Making HTTP requests
-
RXDataSources
… is a set of classes that implement fully functional reactive data sources for UITableViews and UICollectionViews.
Traits (formerly Units)
- General
Traits are simply a wrapper struct with a single read-only Observable sequence property.
struct Single<Element> {
let source: Observable<Element>
}
struct Driver<Element> {
let source: Observable<Element>
}
...
- RxSwift traits
2.1 Single
A Single is a variation of Observable that, instead of emitting a series of elements, is always guaranteed to emit either a single element or an error.
2.2 Completable
A Completable is a variation of Observable that can only complete or emit an error. It is guaranteed to not emit any elements.
2.3 Maybe
A Maybe is a variation of Observable that is right in between a Single and a Completable. It can either emit a single element, complete without emitting an element, or emit an error.
- RxCocoa traits
3.1 Driver
version_1:
let results = query.rx.text
.throttle(0.3, scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
}
results
.map { "\($0.count)" }
.bind(to: resultCount.rx.text)
.disposed(by: disposeBag)
results
.bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
cell.textLabel?.text = "\(result)"
}
.disposed(by: disposeBag)
version_2:
let results = query.rx.text
.throttle(0.3, scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.observeOn(MainScheduler.instance) // results are returned on MainScheduler
.catchErrorJustReturn([]) // in the worst case, errors are handled
}
.share(replay: 1) // HTTP requests are shared and results replayed
// to all UI elements
results
.map { "\($0.count)" }
.bind(to: resultCount.rx.text)
.disposed(by: disposeBag)
results
.bind(to: resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
cell.textLabel?.text = "\(result)"
}
.disposed(by: disposeBag)
version_3:
let results = query.rx.text.asDriver() // This converts a normal sequence into a `Driver` sequence.
.throttle(0.3, scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.asDriver(onErrorJustReturn: []) // Builder just needs info about what to return in case of error.
}
results
.map { "\($0.count)" }
.drive(resultCount.rx.text) // If there is a `drive` method available instead of `bind(to:)`,
.disposed(by: disposeBag) // that means that the compiler has proven that all properties
// are satisfied.
results
.drive(resultsTableView.rx.items(cellIdentifier: "Cell")) { (_, result, cell) in
cell.textLabel?.text = "\(result)"
}
.disposed(by: disposeBag)
3.2 Signal
没看懂
- ControlProperty / ControlEvent
也没看懂
testing
- Testing custom operators
- Testing operator compositions (view models, components)
比较短,一定要搞懂哦
tips
- 使用纯函数
- 使用内置operators
- 创建自己的convenience operators
- 创建自己的operators可以使用内置operators
- Always use operators to compose subscriptions.
- 避免嵌套subscribe
textField.rx.text.subscribe(onNext: { text in
performURLRequest(text).subscribe(onNext: { result in
...
})
.disposed(by: disposeBag)
})
.disposed(by: disposeBag)
- Preferred way of chaining disposables by using operators. 使用disposables最好的方式是使用disposed这个operator?
textField.rx.text
.flatMapLatest { text in
// Assuming this doesn't fail and returns result on main scheduler,
// otherwise `catchError` and `observeOn(MainScheduler.instance)` can be used to
// correct this.
return performURLRequest(text)
}
...
.disposed(by: disposeBag) // only one top most disposable
Math Behind Rx
更深入的东西
Hot and Cold Observables
Hot Observables: 不依赖于是否有subscribed,比如tap coordinates, mouse coordinates, UI control values, current time
Cold Observables: Don’t use resources (don’t produce heat) until observer subscribes. 比如Async operations, HTTP Connections, TCP connections, streams
Comparison with ReactiveCocoa
不知所云
find compatible
References
- http://reactivex.io/
- Reactive Extensions GitHub (GitHub)
- RxSwift RayWenderlich.com Book
- Boxue.io RxSwift Online Course (Chinese 🇨🇳)
- Erik Meijer (Wikipedia)
- Expert to Expert: Brian Beckman and Erik Meijer - Inside the .NET Reactive Framework (Rx) (video)
- Reactive Programming Overview (Jafar Husain from Netflix)
- Subject/Observer is Dual to Iterator (paper)
- Rx standard sequence operators visualized (visualization tool)
- Haskell