Драйвер постоянно запускается между View Model и View Controller - PullRequest
1 голос
/ 08 июля 2019

Возникла проблема с Driver на RxSwift.Имейте модель представления, которая слушает initTrigger в ViewController следующим образом.

    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>, о котором я упоминал ранее.

Что я здесь не так делаю?

Спасибо за помощь.

Ответы [ 2 ]

1 голос
/ 08 июля 2019

Прежде всего вы можете использовать .distincUntilChanged() для фильтрации идентичных событий. во-вторых, проверьте, почему .getShoppingCartUseCase продолжает генерировать события, RxRealm будет отправлять обновления всякий раз, когда ShoppingCart записывается в БД, поэтому, возможно, у вас есть некоторые ненужные записи. убедитесь, что при записи в область вы используете флаг .modified, а не .all (который переопределит элемент, только если он изменился, и не вызовет событие, если он не изменился)

Если вы уверены, что вам нужно событие только один раз - вы всегда можете добавить .take(1) Также вы называете это initTrigger, но отправляете его на viewWillAppear - который можно вызвать столько раз, сколько вы вернетесь к экрану. Если вам это нужно, оденьте viewDidLoad

PS вместо .asObservable().bind(to:...) вы можете просто написать .drive(...), что является более понятным способом привязки драйверов к пользовательскому интерфейсу.

0 голосов
/ 08 июля 2019

Чтобы остановить подписку, наблюдателю необходимо выполнить одно из следующих действий:

  1. Отправить сообщение об ошибке

  2. Отправить завершенное сообщение

  3. Удалить подписку (уничтожить disposeBag)

В вашем случае ни rx.viewWillAppear ни shoppingCart не отправляют сообщения об ошибках или завершенные сообщения, так как они являются драйверами

Один из способов правильного прекращения подписки - уничтожить старый пакет 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())
            }
...