Observer - 观察者

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

观察者 是用来监听事件,然后它需要这个事件做出响应。例如:弹出提示框就是观察者,它对点击按钮这个事件做出响应。

如何创建观察者

Observable 一样,框架已经帮我们创建好了许多常用的观察者。例如:view 是否隐藏,button 是否可点击, label 的当前文本,imageView 的当前图片等等。另外,有一些自定义的观察者是需要我们自己创建的。

tap.subscribe(onNext: { [weak self] in
    self?.showAlert()
}, onError: { error in
    print("发生错误: \(error.localizedDescription)")
}, onCompleted: {
    print("任务完成")
})

创建观察者最直接的方法就是在 Observablesubscribe 方法后面描述,事件发生时,需要如何做出响应。而观察者就是由后面的 onNextonErroronCompleted的这些闭包构建出来的。

以上是创建观察者最常见的方法。当然你还可以通过其他的方式来创建观察者,详见 AnyObserver

AnyObserver

https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/rxswift_core/observer/any_observer.html

public protocol ObserverType {
    ...
    ...
}
public struct AnyObserver<Element> : ObserverType {
    ...
    ...
}

AnyObserver 可以用来描叙任意一种观察者。

创建方式:

let observer: AnyObserver<Element>  = AnyObserver.init { (event) in
    switch event {
    case .next(let value):
        // 序列处理
    case .error(let error):
        // error
    case .completed:
        // 完成
    }
}

eg:

准备代码:

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(jsonObj)
            observer.onCompleted()
        })
        task.resume()
        return Disposables.create {
            task.cancel()
        }
    }
}

以前的做法:

func theGeneralPractice() {
    getObservable(with: "https://api.github.com/")
        .subscribe(onNext: { (jsonObj) in
            print("Get JSON success")
            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)
}

相当于:

func testCreateObserver() {
    let observer: AnyObserver<JSON>  = AnyObserver.init { (event) in
        switch event {
        case .next(let jsonObj):
            print("Get JSON success")
            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 ?? "")
            }
        case .error(let error):
            if let err = error as? TError {
                err.printLog()
            } else {
                print(error.localizedDescription)
            }
        case .completed:
            print("completed")
        }
    }
    getObservable(with: "https://api.github.com/")
        .subscribe(observer)
        .disposed(by: disposeBag)
}

Binder

https://beeth0ven.github.io/RxSwift-Chinese-Documentation/content/rxswift_core/observer/binder.html

public protocol ObserverType {
    ...
    ...
}

public struct Binder<Value>: ObserverType {
    ...
    ...
}

Binder 主要有以下两个特征:

  • 不会处理错误事件
  • 确保绑定都是在给定 [Scheduler] 上执行(默认 MainScheduler

一旦产生错误事件,在调试环境下将执行 fatalError,在发布环境下将打印错误信息。

示例

在介绍 AnyObserver 时,我们举了这样一个例子:

let observer: AnyObserver<Bool> = AnyObserver { [weak self] (event) in
    switch event {
    case .next(let isHidden):
        self?.usernameValidOutlet.isHidden = isHidden
    default:
        break
    }
}

usernameValid
    .bind(to: observer)
    .disposed(by: disposeBag)

由于这个观察者是一个 UI 观察者,所以它在响应事件时,只会处理 next 事件,并且更新 UI 的操作需要在主线程上执行。

因此一个更好的方案就是使用 Binder

let observer: Binder<Bool> = Binder(usernameValidOutlet) { (view, isHidden) in
    view.isHidden = isHidden
}

usernameValid
    .bind(to: observer)
    .disposed(by: disposeBag)

Binder 可以只处理 next 事件,并且保证响应 next 事件的代码一定会在给定 [Scheduler] 上执行,这里采用默认的 MainScheduler

复用

由于页面是否隐藏是一个常用的观察者,所以应该让所有的 UIView 都提供这种观察者:

extension Reactive where Base: UIView {
  public var isHidden: Binder<Bool> {
      return Binder(self.base) { view, hidden in
          view.isHidden = hidden
      }
  }
}
usernameValid
    .bind(to: usernameValidOutlet.rx.isHidden)
    .disposed(by: disposeBag)

这样你不必为每个 UI 控件单独创建该观察者。这就是 usernameValidOutlet.rx.isHidden 的由来,许多 UI 观察者 都是这样创建的:

  • 按钮是否可点击 button.rx.isEnabled

    extension Reactive where Base: UIControl {
      public var isEnabled: Binder<Bool> {
          return Binder(self.base) { control, value in
              control.isEnabled = value
          }
      }
    }
    
  • label 的当前文本 label.rx.text

    extension Reactive where Base: UILabel {
      public var text: Binder<String?> {
          return Binder(self.base) { label, text in
              label.text = text
          }
      }
    }
    

你也可以用这种方式来创建自定义的 UI 观察者

eg:

准备代码:

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(imageView.rx.image)
    .disposed(by: disposeBag)

其中 imageView.rx.image 定义中有 Binder 的定义:

extension Reactive where Base: UIImageView {
    /// Bindable sink for `image` property.
    public var image: Binder<UIImage?> {
        return Binder(base) { imageView, image in
            imageView.image = image
        }
    }
}

相当于:

func testCreateImageViewBinderObserver() {
    let observer: Binder<UIImage> = Binder.init(imageView) { (imageView, image) in
        imageView.image = image
    }

    getImage()
        .asDriver(onErrorJustReturn: #imageLiteral(resourceName: "placeholderImg"))
        .drive(observer)
        .disposed(by: disposeBag)
    // getImage()
    //     .observeOn(MainScheduler.instance)
    //     .bind(to: observer)
    //     .disposed(by: disposeBag)
}

总结

AnyObserver

let observer: AnyObserver<Element>  = AnyObserver.init { (event) in
    switch event {
    case .next(let value):
        // 序列处理
    case .error(let error):
        // error
    case .completed:
        // 完成
    }
}
someObservableVar.subscribe(observer).disposed(by: disposeBag)

Binder

let observer: Binder<UIImage> = Binder.init(imageView) { (imageView, image) in
    imageView.image = image
}
let placeholderImage = #imageLiteral(resourceName: "placeholderImg")
someImageObservableVar.asDriver(onErrorJustReturn: placeholderImage).drive(observer).disposed(by: disposeBag)
// 或
// someImageObservableVar.observeOn(MainScheduler.instance).bind(to: observer).disposed(by: disposeBag)

results matching ""

    No results matching ""