准备

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

https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/rxswift_core/observable/single.html

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>

SingleObservable 的另外一个版本。不像 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

https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/rxswift_core/observable/completeable.html

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>

CompletableObservable 的另外一个版本。不像 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>

MaybeObservable 的另外一个版本。它介于 SingleCompletable 之间,它要么只能发出一个元素,要么产生一个 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

https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/rxswift_core/observable/driver.html

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

https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/rxswift_core/observable/control_event.html

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 监听(主线程监听)
  • 共享状态变化

results matching ""

    No results matching ""