在视图模型和视图控制器之间不断触发驱动程序

时间:2019-07-08 04:57:49

标签: swift realm rx-swift rx-cocoa

RxSwift上的Driver出现问题。拥有一个在ViewController中监听initTrigger的视图模型,如下所示。

    let initTrigger = rx.viewWillAppear
                .mapToVoid()
                .asDriverOnErrorJustComplete()

initTrigger用于绑定到视图模型上的另一个Driver


    let shoppingCart: Driver<ShoppingCart>

    let shoppingCart = input.initTrigger
                .flatMapLatest {
                    self.getShoppingCartUseCase
                        .execute()
                        .asDriver(onErrorJustReturn: ShoppingCart())
                }

getShoppingCartUseCase.execute()返回Observable<ShoppingCart>,并且正在使用RxRealm监听对数据库的更改。

回到视图控制器,我已经像这样订阅了shoppingCart

        output?.shoppingCart
            .map {
                print("Mapping")
                return $0.lines.count == 0
            }
            .asObservable()
            .bind(to: goToCartButton.rx.isHidden)
            .disposed(by: bag)

我放置了print("Mapping"),以意识到在执行修改我的模型并触发我之前提到的Observable<ShoppingCart>的动作之后,最后一个Driver一直在被触发。

我在这里做错了什么?

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

首先,您可以使用.distincUntilChanged()过滤相同的事件。 其次,检查为什么.getShoppingCartUseCase继续发出事件,每当将ShoppingCart写入数据库时​​,RxRealm都会发送更新,因此也许您有一些不必要的写入。确保在写入领域时使用.modified标志,而不是.all(仅当项目更改时,它才会覆盖项目,如果没有更改,则不会导致事件)

如果确定只需要一次活动-您可以随时添加.take(1) 您也可以将其称为initTrigger,但可以通过viewWillAppear发送它-返回屏幕后可以调用多次。如果需要一次,请将其放在viewDidLoad

PS代替.asObservable().bind(to:...),您只需编写.drive(...),这是将驱动程序绑定到ui的更简便方法。

答案 1 :(得分:0)

要停止订阅观察者,必须执行以下操作之一:

  1. 发送错误消息

  2. 发送完成的消息

  3. 处置订阅(销毁disposeBag)

就您而言,rx.viewWillAppearshoppingCart均未发送错误或未完成的消息,因为它们是驱动程序

一种正确地停止订阅的方法是销毁旧的disposeBag bag = DisposeBag()

但不要忘记在viewWillAppear上恢复订阅

其他选项是在VC中添加一些标记,例如

var hasAppeared: Bool

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    ...
    hasAppeared = true
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisppear(animated)
    ...
    hasAppeared = false
}

仅添加过滤器

    output?.shoppingCart
        .filter({ [weak self] _ in self?.hasAppeared ?? false })
        .map {
            print("Mapping")
            return $0.lines.count == 0
        }
        .asObservable()
        .bind(to: goToCartButton.rx.isHidden)
        .disposed(by: bag)

第三种方式是停止从viewModel内部发送

let initTrigger = rx.viewWillAppear
            .mapToVoid()
            .asDriverOnErrorJustComplete()
let stopTrigger = rx.viewWillDisappear
            .mapToVoid()
            .asDriverOnErrorJustComplete()


let shoppingCart: Driver<ShoppingCart>

let shoppingCart = Observable.merge(input.initTrigger.map({ true }), 
                                    input.stopTrigger.map({ false }))
            .flatMapLatest { isRunning in
                guard isRunning else {
                    return .just(ShoppingCart())
                }

                return self.getShoppingCartUseCase
                    .execute()
                    .asDriver(onErrorJustReturn: ShoppingCart())
            }