【问题标题】:RxSwift, why using .never() not for testingRxSwift,为什么不使用 .never() 进行测试
【发布时间】:2018-09-15 22:24:28
【问题描述】:

我正在学习教程:

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

其中谈到了MVVM-C 设计模式。我很难理解.never() observable 在那里使用的方式和原因(以及通常为什么除了测试超时之外我们还想使用.never())。

谁能在swift 代码(不在测试中)中给出一个合理的.never() 可观察用法示例,并解释为什么它是必要的以及有哪些替代方法?

【问题讨论】:

    标签: swift mvvm rx-swift


    【解决方案1】:

    我解决了从 View 到 ViewModel 的所有操作。用户点击按钮?好的,信号被传递到 ViewModel。这就是为什么我在 ViewModel 中有多个输入 observables。所有的 observables 都是optional。它们是optional,因为有时我编写测试并且不想提供所有虚假的 observable 来测试某个单一功能。因此,我将其他可观察的数据提供为 nil。但是使用nil 不是很方便,所以我为所有optional observables 提供了一些默认行为,如下所示:

    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(),因为我认为不会触发任何导航按钮。

    但这整个故事只是我的观点。我打赌你会找到自己的地方来使用这个 never 运算符。

    【讨论】:

      【解决方案2】:

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


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

      【讨论】:

        【解决方案3】:

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

        为此,我想创建一个 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()
        }
        

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

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

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

        【讨论】:

          猜你喜欢
          • 2012-06-07
          • 2020-11-07
          • 2015-06-11
          • 2019-02-07
          • 1970-01-01
          • 1970-01-01
          • 2017-03-31
          • 2011-11-19
          • 2019-02-01
          相关资源
          最近更新 更多