RxSwift,为什么不使用.never()进行测试

时间:2018-04-06 00:30:29

标签: swift mvvm rx-swift

我正在阅读教程:

https://marcosantadev.com/mvvmc-with-swift/

讨论MVVM-C设计模式。我真的很难理解在那里使用.never() observable的方式和原因(以及为什么我们除了测试超时之外还要使用.never())。

任何人都可以在.never()代码(不在测试中)中提供swift可观察用法的合理示例,并解释为什么有必要以及有哪些替代方案?

3 个答案:

答案 0 :(得分:1)

也许您的ViewModel具有不同的配置(或者您在同一协议下有不同的viewModel),其中一个不需要向其观察者发送任何更新。您可能希望能够将一个observable定义为.never(),而不是说这个特定情况(您将作为可选项实现)不存在observable。这在我看来更干净。

免责声明 - 我不是RxSwift的用户,但我假设never与ReactiveSwift相似,即一个永不发送任何值的信号。

答案 1 :(得分:1)

我解决了从View到ViewModel的所有操作。用户点击按钮?很好,信号传递给ViewModel。这就是为什么我在ViewModel中有多个输入observable。所有可观测量都是optional。它们是optional,因为有时候我会编写测试而不是真的想提供所有虚假的可观测量来测试一些单一的功能。所以,我提供了其他可观察量nil。但是使用nil不是很方便,所以我为所有optional可观察对象提供了一些默认行为:

private extension ViewModel {

    func observableNavigation() -> Observable<Navigation.Button> {
        return viewOutputFactory().observableNavigation ?? Observable.never()
    }

    func observableViewState() -> Observable<ViewState> {
        return viewOutputFactory().observableViewState ?? Observable.just(.didAppear)
    }
}

如您所见,如果我为nil传递observableViewState,我将其替换为just(.didAppear),因为ViewModel逻辑在很大程度上取决于视图状态。另一方面,如果我通过nil observableNavigation我提供never(),因为我认为不会触发非导航按钮。

但这整个故事只是我的观点。我打赌你会找到自己的地方来使用这个从不运营商。

答案 2 :(得分:0)

这是一个开放式问题,可以有很多答案,但我发现自己在很多情况下都无法解决。解决问题的方法有很多,但最近,我正在简化一些具有级联故障转移的设备连接代码,我想确定我上次扫描设备的尝试是否产生任何结果。

为了做到这一点,我想创建一个 observable,它只在没有看到任何结果的情况下被处理时发出“无扫描结果”事件,相反,如果有,则不发出任何内容。

为了简洁起见,我从我的代码中删除了其他细节,但本质上:

func connect(scanDuration: TimeInterval) -> Observable<ConnectionEvent> {
    let scan = scan(for: scanDuration).share(replay: 1)
    let connection: Observable<ConnectionEvent> =
        Observable.concat(Observable.from(restorables ?? []),
                          connectedPeripherals(),
                          scan)
        .flatMapLatest { [retainedSelf = self] in retainedSelf.connect(to: $0) }
    
    let scanDetector = scan
        .toArray()  // <-- sum all results as an array for final count
        .asObservable()
        .flatMap { results -> Observable<ConnectionEvent> in
            results.isEmpty // if no scan results
                ? Observable.just(.noDevicesAvailable) // emit event
                : Observable.never() } // else, got results, no action needed

    // fold source and stream detector into common observable
    return Observable.from([
        connection
            .filter { $0.isConnected }
            .flatMapLatest { [retained = self] event -> Observable<ConnectionEvent> in
                retained.didDisconnect(peripheral: event.connectedPeripheral!.peripheral)
                    .startWith(event) },
        scanDetector])
        .switchLatest()
}

对于反点,我在输入时意识到,还有一种更简单的方法可以满足我的需求,那就是在我的 concat 中添加一个发出 observable 的最终错误,它会进行故障转移,直到遇到最后的错误案例,所以我不需要后面的错误检测流。

        Observable.concat(Observable.from(restorables ?? []),
                      connectedPeripherals(),
                      scan,
                      hardFailureEmitNoScanResults())

也就是说,在很多情况下,我们可能想要监听和过滤下游,而 concat 技术不可用。