准备
typealias JSON = Any
struct TError: Error {
var errorCode: Int = 0
var errorString: String = ""
var errorData: Any?
func printLog() {
print(errorCode)
print(errorString)
if let data = errorData as? Data {
let str = String.init(data: data, encoding: String.Encoding.utf8)
print(str ?? "NULL Error Data")
}
}
}
Observable - 可被监听的序列
https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/rxswift_core/observable.html
public protocol ObservableConvertibleType {
...
...
}
public protocol ObservableType : ObservableConvertibleType {
...
...
}
public class Observable<Element> : ObservableType {
...
...
}
如何创建序列
现在我们已经可以把生活中的许多事物看作是一个序列了。那么我们要怎么创建这些序列呢?
实际上,框架已经帮我们创建好了许多常用的序列。例如:button
的点击,textField
的当前文本,switch
的开关状态,slider
的当前数值等等。
另外,有一些自定义的序列是需要我们自己创建的。这里介绍一下创建序列最基本的方法,例如,我们创建一个 [0, 1, ... 8, 9]
的序列:
let numbers: Observable<Int> = Observable.create { observer -> Disposable in
observer.onNext(0)
observer.onNext(1)
observer.onNext(2)
observer.onNext(3)
observer.onNext(4)
observer.onNext(5)
observer.onNext(6)
observer.onNext(7)
observer.onNext(8)
observer.onNext(9)
observer.onCompleted()
return Disposables.create()
}
创建序列最直接的方法就是调用 Observable.create
,然后在构建函数里面描述元素的产生过程。
observer.onNext(0)
就代表产生了一个元素,他的值是 0
。后面又产生了 9 个元素分别是 1, 2, ... 8, 9
。最后,用 observer.onCompleted()
表示元素已经全部产生,没有更多元素了。
func getObservable(with url: String) -> Observable<JSON> {
return Observable<JSON>.create { (observer) -> Disposable in
guard let url = URL.init(string: url) else {
let err = TError.init(errorCode: 10, errorString: "url error", errorData: nil)
observer.onError(err)
return Disposables.create()
}
let request = URLRequest.init(url: url)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
if let err = error {
observer.onError(err)
return
}
guard let jsonData = data, let jsonObj = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableLeaves) else {
let err = TError.init(errorCode: 11, errorString: "json error", errorData: data)
observer.onError(err)
return
}
// 测试多个事件
// observer.onNext(1234)
observer.onNext(jsonObj)
observer.onCompleted()
// onCompleted之后不发送
observer.onNext(2222222)
observer.onCompleted()
})
task.resume()
return Disposables.create {
task.cancel()
}
}
}
在闭包回调中,如果任务失败,就调用 observer.onError(error!)
。如果获取到目标元素,就调用 observer.onNext(jsonObject)
。由于我们的这个序列只有一个元素,所以在成功获取到元素后,就直接调用 observer.onCompleted()
来表示任务结束。最后 Disposables.create { task.cancel() }
说明如果数据绑定被清除(订阅被取消)的话,就取消网络请求。
这样一来我们就将传统的闭包回调转换成序列了。然后可以用 subscribe
方法来响应这个请求的结果:
func testObservable() {
getObservable(with: githubStr).subscribe(onNext: { (jsonObj) in
print("Get JSON success")
if jsonObj is Int {
print(jsonObj)
return
}
guard JSONSerialization.isValidJSONObject(jsonObj) else { return }
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObj, options: .prettyPrinted) {
let jsonStr = String.init(data: jsonData, encoding: String.Encoding.utf8)
print(jsonStr ?? "")
}
}, onError: { (error) in
if let error = error as? TError {
error.printLog()
} else {
print(error.localizedDescription)
}
}, onCompleted: {
print("completed")
}).disposed(by: disposeBag)
}
Event - 事件
public enum Event<Element> {
case next(Element)
case error(Swift.Error)
case completed
}
- next - 序列产生了一个新的元素
- error - 创建序列时产生了一个错误,导致序列终止
- completed - 序列的所有元素都已经成功产生,整个序列已经完成
你可以合理的利用这些 Event
来实现业务逻辑。
Single
public struct PrimitiveSequence<Trait, Element> {
let source: Observable<Element>
init(raw: Observable<Element>) {
self.source = raw
}
}
public enum SingleTrait { }
public typealias Single<Element> = PrimitiveSequence<SingleTrait, Element>
Single 是 Observable
的另外一个版本。不像 Observable
可以发出多个元素,它要么只能发出一个元素,要么产生一个 error
事件。
- 发出一个元素,或一个
error
事件 - 不会共享状态变化
一个比较常见的例子就是执行 HTTP 请求,然后返回一个应答或错误。不过你也可以用 Single 来描述任何只有一个元素的序列。
如何创建 Single
创建 Single 和创建 Observable 非常相似:
func getRepo(_ repo: String) -> Single<[String: Any]> {
return Single<[String: Any]>.create { (single) -> Disposable in
guard let url = URL.init(string: "https://api.github.com/repos/\(repo)") else {
let err = TError.init(errorCode: 10, errorString: "url error", errorData: nil)
single(.error(err))
return Disposables.create()
}
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
if error != nil {
let err = TError.init(errorCode: 20, errorString: "request error", errorData: data)
single(.error(err))
return
}
guard let jsonData = data,
let jsonObj = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableLeaves),
let result = jsonObj as? [String: Any] else {
let err = TError.init(errorCode: 30, errorString: "json error", errorData: data)
single(.error(err))
return
}
single(.success(result))
// 只会运行第一个single
single(.success(["1":2]))
})
task.resume()
return Disposables.create {
task.cancel()
}
}
}
之后,你可以这样使用 Single:
func testSingle() {
getRepo("ReactiveX/RxSwift")
.subscribe(onSuccess: { (dict) in
print(dict)
}, onError: { (error) in
guard let err = error as? TError else {
print(error.localizedDescription)
return
}
err.printLog()
})
.disposed(by: disposeBag)
}}
订阅提供一个 SingleEvent
的枚举:
public enum SingleEvent<Element> {
case success(Element)
case error(Swift.Error)
}
- success - 产生一个单独的元素
- error - 产生一个错误
你同样可以对 Observable
调用 .asSingle()
方法,将它转换为 Single,事件的对应关系如下:
Event<Element> |
=> | SingleEvent<Element> |
---|---|---|
1 * next(Element) + completed |
=> | success(Element) |
n * next(Element) + completed or error(Swift.Error) |
=> | error(Swift.Error) |
eg:
func testObservableAsSingle() {
getObservable(with: githubStr)
.asSingle()
.subscribe(onSuccess: { (jsonObj) in
// 1*onNext + 1*onCompleted
print("Get JSON success")
if jsonObj is Int {
print(jsonObj)
return
}
guard JSONSerialization.isValidJSONObject(jsonObj) else {
return
}
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObj, options: .prettyPrinted) {
let jsonStr = String.init(data: jsonData, encoding: String.Encoding.utf8)
print(jsonStr ?? "")
}
}, onError: { (error) in
// n*onNext + 1*onCompleted || onError
if let error = error as? TError {
error.printLog()
} else {
print(error.localizedDescription)
}
})
.disposed(by: disposeBag)
}
Completable
public struct PrimitiveSequence<Trait, Element> {
let source: Observable<Element>
init(raw: Observable<Element>) {
self.source = raw
}
}
public enum CompletableTrait { }
public typealias Completable = PrimitiveSequence<CompletableTrait, Swift.Never>
Completable 是 Observable
的另外一个版本。不像 Observable
可以发出多个元素,它要么只能产生一个 completed
事件,要么产生一个 error
事件。
- 发出零个元素
- 发出一个
completed
事件或者一个error
事件 - 不会共享状态变化
Completable 适用于那种你只关心任务是否完成,而不需要在意任务返回值的情况。它和 Observable<Void>
有点相似。
如何创建 Completable
创建 Completable 和创建 Observable 非常相似:
func getCompletable() -> Completable {
return Completable.create { (completable) -> Disposable in
guard let url = URL.init(string: "http://www.baidu.com/") else {
completable(.error(TError.init(errorCode: 10, errorString: "url error", errorData: nil)))
return Disposables.create()
}
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
if let errInfo = error {
completable(.error(errInfo))
} else {
completable(.completed)
}
})
task.resume()
return Disposables.create {
task.cancel()
}
}
}
之后,你可以这样使用 Completable:
func testCompletable() {
getCompletable()
.subscribe(onCompleted: {
print("Completable onCompleted")
}, onError: { (error) in
if let err = error as? TError {
err.printLog()
return
}
print(error.localizedDescription)
})
.disposed(by: disposeBag)
}
Maybe
https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/rxswift_core/observable/maybe.html
public struct PrimitiveSequence<Trait, Element> {
let source: Observable<Element>
init(raw: Observable<Element>) {
self.source = raw
}
}
public enum MaybeTrait { }
public typealias Maybe<Element> = PrimitiveSequence<MaybeTrait, Element>
Maybe 是 Observable
的另外一个版本。它介于 Single
和 Completable
之间,它要么只能发出一个元素,要么产生一个 completed
事件,要么产生一个 error
事件。
- 发出一个元素或者一个
completed
事件或者一个error
事件 - 不会共享状态变化
如果你遇到那种可能需要发出一个元素,又可能不需要发出时,就可以使用 Maybe。
如何创建 Maybe
创建 Maybe 和创建 Observable 非常相似:
func getMaybe() -> Maybe<Data> {
return Maybe.create { (maybe) -> Disposable in
guard let url = URL.init(string: "http://www.baidu.com/") else {
maybe(.error(TError.init(errorCode: 10, errorString: "url error", errorData: nil)))
return Disposables.create()
}
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
if let errInfo = error {
maybe(.error(errInfo))
return
}
if let data = data {
maybe(.success(data))
return
}
// 无错误也无数据返回
maybe(.completed)
})
task.resume()
return Disposables.create {
task.cancel()
}
}
}
之后,你可以这样使用 Maybe:
testMaybe() {
getMaybe()
.subscribe(onSuccess: { (data) in
print(data.debugDescription)
}, onError: { (err) in
if let err = err as? TError {
err.printLog()
return
}
print(err.localizedDescription)
}, onCompleted: {
print("Completed With No Data")
})
.disposed(by: disposeBag)
}
订阅提供一个 MaybeEvent
的枚举:
public enum MaybeEvent<Element> {
case success(Element)
case error(Swift.Error)
case completed
}
你同样可以对 Observable
调用 .asMaybe()
方法,将它转换为 Maybe。事件的对应关系如下:
Event<Element> |
=> | MaybeEvent<Element> |
---|---|---|
1 * next(Element) + completed |
=> | success(Element) |
n * next(Element) + completed or error(Swift.Error) |
=> | error(Swift.Error) |
1 * completed |
=> | completed |
func testObservableAsMaybe() {
getObservable(with: githubStr)
.asMaybe()
.subscribe(onSuccess: { (jsonObj) in
// 1*onNext + 1*onCompleted
print("Get JSON success")
if jsonObj is Int {
print(jsonObj)
return
}
guard JSONSerialization.isValidJSONObject(jsonObj) else { return }
if let jsonData = try? JSONSerialization.data(withJSONObject: jsonObj, options: .prettyPrinted) {
let jsonStr = String.init(data: jsonData, encoding: String.Encoding.utf8)
print(jsonStr ?? "")
}
}, onError: { (error) in
// n*onNext + 1*onCompleted || onError
if let error = error as? TError {
error.printLog()
} else {
print(error.localizedDescription)
}
}, onCompleted: {
// 1*onCompleted
print("completed")
})
.disposed(by: disposeBag)
}
Driver
public protocol ObservableConvertibleType {
associatedtype E
func asObservable() -> Observable<E>
}
public protocol SharedSequenceConvertibleType : ObservableConvertibleType {
associatedtype SharingStrategy: SharingStrategyProtocol
func asSharedSequence() -> SharedSequence<SharingStrategy, E>
}
public struct SharedSequence<S: SharingStrategyProtocol, Element> : SharedSequenceConvertibleType {
...
...
}
public struct DriverSharingStrategy: SharingStrategyProtocol {
public static var scheduler: SchedulerType { return SharingScheduler.make() }
public static func share<E>(_ source: Observable<E>) -> Observable<E> {
return source.share(replay: 1, scope: .whileConnected)
}
}
public typealias Driver<E> = SharedSequence<DriverSharingStrategy, E>
Driver(司机?) 是一个精心准备的特征序列。它主要是为了简化 UI 层的代码。不过如果你遇到的序列具有以下特征,你也可以使用它:
- 不会产生
error
事件 - 一定在
MainScheduler
监听(主线程监听) - 共享状态变化
这些都是驱动 UI 的序列所具有的特征。
为什么要使用 Driver ?
我们举个例子来说明一下,为什么要使用 Driver。
这是文档简介页的例子:
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)
这段代码的主要目的是:
- 取出用户输入稳定后的内容
- 向服务器请求一组结果
- 将返回的结果绑定到两个 UI 元素上:
tableView
和 显示结果数量的label
那么这里存在什么问题?
- 如果
fetchAutoCompleteItems
的序列产生了一个错误(网络请求失败),这个错误将取消所有绑定,当用户输入一个新的关键字时,是无法发起新的网络请求。 - 如果
fetchAutoCompleteItems
在后台返回序列,那么刷新页面也会在后台进行,这样就会出现异常崩溃。 - 返回的结果被绑定到两个 UI 元素上。那就意味着,每次用户输入一个新的关键字时,就会分别为两个 UI 元素发起 HTTP 请求,这并不是我们想要的结果。
一个更好的方案是这样的:
let results = query.rx.text
.throttle(0.3, scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.observeOn(MainScheduler.instance) // 结果在主线程返回
.catchErrorJustReturn([]) // 错误被处理了,这样至少不会终止整个序列
}
.share(replay: 1) // HTTP 请求是被共享的
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)
在一个大型系统内,要确保每一步不被遗漏是一件不太容易的事情。所以更好的选择是合理运用编译器和特征序列来确保这些必备条件都已经满足。
以下是使用 Driver 优化后的代码:
let results = query.rx.text.asDriver() // 将普通序列转换为 Driver
.throttle(0.3, scheduler: MainScheduler.instance)
.flatMapLatest { query in
fetchAutoCompleteItems(query)
.asDriver(onErrorJustReturn: []) // 仅仅提供发生错误时的备选返回值
}
results
.map { "\($0.count)" }
.drive(resultCount.rx.text) // 这里改用 `drive` 而不是 `bindTo`
.disposed(by: disposeBag) // 这样可以确保必备条件都已经满足了
results
.drive(resultsTableView.rx.items(cellIdentifier: "Cell")) {
(_, result, cell) in
cell.textLabel?.text = "\(result)"
}
.disposed(by: disposeBag)
首先第一个 asDriver
方法将 ControlProperty
转换为 Driver
然后第二个变化是:
.asDriver(onErrorJustReturn: [])
任何可被监听的序列都可以被转换为 Driver
,只要他满足 3 个条件:
- 不会产生
error
事件 - 一定在
MainScheduler
监听(主线程监听) - 共享状态变化
那么要如何确定条件都被满足?通过 Rx 操作符来进行转换。asDriver(onErrorJustReturn: [])
相当于以下代码:
let safeSequence = xs
.observeOn(MainScheduler.instance) // 主线程监听
.catchErrorJustReturn(onErrorJustReturn) // 无法产生错误
.share(replay: 1, scope: .whileConnected)// 共享状态变化
return Driver(raw: safeSequence) // 封装
最后使用 drive
而不是 bindTo
drive
方法只能被 Driver
调用。这意味着,如果你发现代码所存在 drive
,那么这个序列不会产生错误事件并且一定在主线程监听。这样你可以安全的绑定 UI 元素。
eg:
func testObservableAsDriver() {
func getImage() -> Observable<UIImage> {
return Observable<UIImage>.create { (observer) -> Disposable in
let downloadToken = SDWebImageDownloader.shared().downloadImage(
with: URL.init(string: "https://avatars1.githubusercontent.com/u/11990850"),
options: SDWebImageDownloaderOptions.highPriority,
progress: nil,
completed: { (image, data, error, finished) in
if let img = image {
observer.onNext(img)
observer.onCompleted()
return
}
if let err = error {
observer.onError(err)
return
}
observer.onError(TError.init(errorCode: 10, errorString: "UNKNOW ERROR", errorData: data))
}
)
return Disposables.create {
SDWebImageDownloader.shared().cancel(downloadToken)
}
}
}
getImage()
.asDriver(onErrorJustReturn: #imageLiteral(resourceName: "placeholderImg"))
.drive(self.imageView.rx.image)
.disposed(by: disposeBag)
}
ControlEvent
public protocol ObservableType : ObservableConvertibleType {
func subscribe<O: ObserverType>(_ observer: O) -> Disposable where O.E == E
}
public protocol ControlEventType : ObservableType {
func asControlEvent() -> ControlEvent<E>
}
public struct ControlEvent<PropertyType> : ControlEventType {
...
...
}
ControlEvent 专门用于描述 UI 控件所产生的事件,它具有以下特征:
- 不会产生
error
事件 - 一定在
MainScheduler
订阅(主线程订阅) - 一定在
MainScheduler
监听(主线程监听) - 共享状态变化
func testControlEvent() {
// extension Reactive where Base: UIButton {
//
// /// Reactive wrapper for `TouchUpInside` control event.
// public var tap: ControlEvent<Void> {
// return controlEvent(.touchUpInside)
// }
// }
let disposeBag = DisposeBag()
self.btn.rx.tap
.subscribe(onNext: { [weak self] in
let ac = UIAlertController.init(title: "TEST TAP(touchUpInside)", message: "testControlEvent", preferredStyle: UIAlertControllerStyle.alert)
ac.addAction(UIAlertAction.init(title: "确定", style: UIAlertActionStyle.cancel, handler: nil))
self?.present(ac, animated: true, completion: nil)
})
.disposed(by: disposeBag)
self.btn.rx.controlEvent(UIControlEvents.touchDragExit)
.subscribe(onNext: { [weak self] in
let ac = UIAlertController.init(title: "TEST touchDragExit", message: "testControlEvent", preferredStyle: UIAlertControllerStyle.alert)
ac.addAction(UIAlertAction.init(title: "确定", style: UIAlertActionStyle.cancel, handler: nil))
self?.present(ac, animated: true, completion: nil)
})
.disposed(by: disposeBag)
}
总结
Observable => Event
public enum Event<Element> {
case next(Element)
case error(Swift.Error)
case completed
}
Single => SingleEvent
- 发出一个元素,或一个
error
事件 - 不会共享状态变化
public enum SingleEvent<Element> {
case success(Element)
case error(Swift.Error)
}
Event<Element> |
=> | SingleEvent<Element> |
---|---|---|
1 * next(Element) + completed |
=> | success(Element) |
n * next(Element) + completed or error(Swift.Error) |
=> | error(Swift.Error) |
Completable
要么只能产生一个 completed
事件,要么产生一个 error
事件。适用于那种你只关心任务是否完成,而不需要在意任务返回值的情况。它和 Observable<Void>
有点相似。
- 发出零个元素
- 发出一个
completed
事件或者一个error
事件 - 不会共享状态变化
Maybe => MaybeEvent
- 发出一个元素或者一个
completed
事件或者一个error
事件 - 不会共享状态变化
public enum MaybeEvent<Element> {
case success(Element)
case error(Swift.Error)
case completed
}
Event<Element> |
=> | MaybeEvent<Element> |
---|---|---|
1 * next(Element) + completed |
=> | success(Element) |
n * next(Element) + completed or error(Swift.Error) |
=> | error(Swift.Error) |
1 * completed |
=> | completed |
Driver
observableValue.asDriver(onErrorJustReturn: ElementType)
将Observable<ElementType>
转为Driver<ElementType>
类型
- 不会产生
error
事件 - 一定在
MainScheduler
监听(主线程监听) - 共享状态变化
ControlEvent
control.rx.controlEvent(UIControlEvents.touchUpInside).subscribe(...)
ControlEvent 专门用于描述 UI 控件所产生的事件
- 不会产生
error
事件 - 一定在
MainScheduler
订阅(主线程订阅) - 一定在
MainScheduler
监听(主线程监听) - 共享状态变化