RxSwift: предотвращение нескольких сетевых запросов

В настоящее время у меня возникает проблема с несколькими сетевыми запросами, выполняемыми при использовании RxSwift Observables. Я понимаю, что если создать холодную наблюдаемую область и иметь несколько наблюдателей, она будет выполнять свой блок каждый раз, когда на нее подписываются.

Я попытался создать наблюдаемую общую подписку, которая выполняет сетевой запрос один раз, и несколько подписчиков будут уведомлены о результате. Ниже это то, что я пытался.

Цепочка событий

  1. Создайте модель представления с событием касания uibutton
  2. Создайте serviceStatus Observable в качестве открытого свойства в модели представления. Эта наблюдаемая сопоставляется с buttonTapped Observable. Затем он отфильтровывает статус "Загрузка". Возвращенный Observable имеет функцию shareReplay(1), выполненную для возврата к общей подписке.
  3. Создайте сервис Executing Observable в качестве открытого свойства в модели представления. Эта наблюдаемая отображается из сервиса Observable. Он вернет истину, если статус "Загрузка"
  4. Привязать uilabel к сервису Status Observable
  5. Свяжите индикатор активности с сервисом Executing Observable.

При нажатии кнопки сервисный запрос выполняется три раза, и я ожидаю, что он будет выполнен только один раз. Что-то выделяется как неправильное?

Код

class ViewController {

    let disposeBag = DisposeBag()
    var button: UIButton!
    var resultLabel: UILabel!
    var activityIndicator: UIActivityIndicator!

    lazy var viewModel = { // 1
        return ViewModel(buttonTapped: self.button.rx.tap.asObservable())
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        self.viewModel.serviceStatus.bindTo(self.resultLabel.rx_text).addDispsoableTo(disposeBag) // 4
        self.viewModel.serviceExecuting.bindTo(self.activityIndicator.rx_animating).addDispsoableTo(disposeBag) // 5
    }
}

class ViewModel {

    public var serviceStatus: Observable<String> { // 2
        let serviceStatusObseravble = self.getServiceStatusObservable()
        let filtered = serviceStatusObseravble.filter { status in
            return status != "Loading"
        }
        return filtered
    }

    public var serviceExecuting: Observable<Bool> { // 3
        return self.serviceStatus.map { status in
            return status == "Loading"
        }
        .startWith(false)
    }

    private let buttonTapped: Observable<Void>

    init(buttonTapped: Observable<Void>) {
        self.buttonTapped = buttonTapped
    }

    private func getServiceStatusObservable() -> Observable<String> {
        return self.buttonTapped.flatMap { _ -> Observable<String> in
            return self.createServiceStatusObservable()
        }
    }

    private func createServiceStatusObservable() -> Observable<String> {
        return Observable.create({ (observer) -> Disposable in

        someAsyncServiceRequest() { result }
            observer.onNext(result)
        })

        return NopDisposable.instance
    })
    .startWith("Loading")
    .shareReplay(1)
}

РЕДАКТИРОВАТЬ:

Исходя из разговора ниже, вот что я искал...

Мне нужно было применить функцию share() к Observable, возвращенному из метода getServiceStatusObservable(), а не к Observable, возвращенному из метода createServiceStatusObservable(). Для наблюдения за текущим состоянием было добавлено несколько наблюдателей. Это означало, что наблюдаемое выполнение сетевого запроса выполнялось N раз (N - число наблюдателей). Теперь каждый раз, когда нажимается кнопка, сетевой запрос выполняется один раз, что мне и было нужно.

private func getServiceStatusObservable() -> Observable<String> {
    return self.buttonTapped.flatMap { _ -> Observable<String> in
        return self.createServiceStatusObservable()
    }.share()
}

1 ответ

Решение

.shareReplay(1) будет применяться только к одному экземпляру наблюдаемого. При создании в createServiceStatusObservable() поведение совместного использования повлияет только на одно значение, возвращаемое этой функцией.

class ViewModel {
  let serviceStatusObservable: Observable<String>

  init(buttonTapped: Observable<Void>) {
    self.buttonTapped = buttonTapped
    self.serviceStatusObservable = Observable.create({ (observer) -> Disposable in
        someAsyncServiceRequest() { result in
            observer.onNext(result)
        }

        return NopDisposable.instance
    })
    .startWith("Loading")
    .shareReplay(1)
  }

  private func getServiceStatusObservable() -> Observable<String> {
    return self.buttonTapped.flatMap { [weak self] _ -> Observable<String> in
      return self.serviceStatusObservable
    }
  }
}

С этой версией serviceStatusObservable создается только один раз, следовательно, его побочный эффект будет передаваться при каждом его использовании, так как это один и тот же экземпляр.

Другие вопросы по тегам