(五)RxSwift之特征序列Driver

399 阅读3分钟

特征序列:Driver

  • Driver 是面对UI的封装,是 SharedSequence 的别名
public typealias Driver<Element> = SharedSequence<DriverSharingStrategy, Element>

主要有以下几个特点:

  • 不会产生错误序号catchErrorJustReturn("error")
  • 在主线程执行MainScheduler()
  • 共享状态变化share(replay: 1, scope: .whileConnected)

让我们举个例子:

一般网络请求后的数据,我们都会绑定到UI上,下面我们来模拟一下这个情况。

TF的输入作为网络请求的数据,将数据的长度和内容分别绑定到LableButton

模拟网络请求

func dealwithData(inputText:String) -> Observable<Any> {
    print("请求网络了\(Thread.current)")
    return Observable<Any>.create { (ob) -> Disposable in
        if inputText == "10086" {
            ob.onError(NSError.init(domain: "马小撂", code: 10086, userInfo: nil))
        }
        DispatchQueue.global().async {
            print("发送之前看看:\(Thread.current)")
            ob.onNext(inputText)
            ob.onCompleted()
        }
        return Disposables.create()
    }
}

UI层面的绑定

let result = inputTF.rx.text.skip(1)
    .flatMap { [weak self](input) -> Observable<Any> in
        return (self?.dealwithData(inputText: input ?? ""))!
}

let _ = result.map{ "长度: \(($0 as! String).count)" }.bind(to: self.textLabel.rx.text)
let _ = result.map{ $0 as! String }.bind(to: self.button.rx.title())

这段代码会存在问题

  • 不能接受错误事件
  • 两次订阅就会有两次请求

为了解决上面的两个问题,添加两句代码

let result = inputTF.rx.text.skip(1)
    .flatMap { [weak self](input) -> Observable<Any> in
        return (self?.dealwithData(inputText: input ?? ""))!
            .observeOn(MainScheduler())//回到主线程
            .catchErrorJustReturn("检测到错误事件")
}.share(replay: 1, scope: .whileConnected)//共享状态变化

let _ = result.map{ "长度: \(($0 as! String).count)" }.bind(to: self.textLabel.rx.text)
let _ = result.map{ $0 as! String }.bind(to: self.button.rx.title())

问题解决了,但是每次都要共享状态,回到主线程, 检测error事件,是不是可以封装一下呢?🤔

作者已经为我们想到了,提供了一个特征序列Driver

//driver
let result = inputTF.rx.text.orEmpty
    .asDriver()
    .flatMap {
        return self.dealwithData(inputText: $0)
        .asDriver(onErrorJustReturn: "检测到了错误事件")
    }

let _ = result.map { "长度: \(($0 as! String).count)"}.drive(self.textLabel.rx.text)

let _ = result.map { "\($0 as! String)"}
           .drive(self.button.rx.title())

这里我们需要提供一个检测error的事件,其他的交给diver。 让我们来看看diver帮我们做了什么

asDriver()这个函数,注释是这样的 ConvertsControlPropertytoDrivertrait.

public func asDriver(onErrorRecover: @escaping (_ error: Swift.Error) -> Driver<Element>) -> Driver<Element> {
    let source = self
        .asObservable()
        .observeOn(DriverSharingStrategy.scheduler)
        .catchError { error in
            onErrorRecover(error).asObservable()
        }
    return Driver(source)
}

asDriver这个函数我们可以看见一个catchError observeOn()和外面写的一样,应该是控制线程的,我们点进去验证以下:

DriverSharingStrategy.scheduler

public struct DriverSharingStrategy: SharingStrategyProtocol {
    public static var scheduler: SchedulerType { return SharingScheduler.make() }
    public static func share<Element>(_ source: Observable<Element>) -> Observable<Element> {
        return source.share(replay: 1, scope: .whileConnected)
    }
}

SharingScheduler.make():

public private(set) static var make: () -> SchedulerType = { MainScheduler() }

返回return Driver(source)做了什么 让我们看一下Driver的初始化,上面我们知道diverSharedSequence的别名,所以我们来看一下SharedSequence的初始化。

init(_ source: Observable<Element>) {
    self._source = SharingStrategy.share(source)
}

调用了share

public static func share<Element>(_ source: Observable<Element>) -> Observable<Element> {
    return source.share(replay: 1, scope: .whileConnected)
}

又满足了共享函数

最后 DriverSharingStrategy.scheduler 其实就相当于MainScheduler(),又回到我们的主线程。 而且上面的dirver在初始化的时候帮我们调用了share(replay: 1, scope: .whileConnected) 我们需要的diver都帮我们在内部都帮我们调用了,之我们只需要绑定在我们的UI上就可以了,注意绑定的时候用drive而不是bind了。