RxSwift: предотвращение нескольких сетевых запросов
В настоящее время у меня возникает проблема с несколькими сетевыми запросами, выполняемыми при использовании RxSwift Observables. Я понимаю, что если создать холодную наблюдаемую область и иметь несколько наблюдателей, она будет выполнять свой блок каждый раз, когда на нее подписываются.
Я попытался создать наблюдаемую общую подписку, которая выполняет сетевой запрос один раз, и несколько подписчиков будут уведомлены о результате. Ниже это то, что я пытался.
Цепочка событий
- Создайте модель представления с событием касания uibutton
- Создайте serviceStatus Observable в качестве открытого свойства в модели представления. Эта наблюдаемая сопоставляется с buttonTapped Observable. Затем он отфильтровывает статус "Загрузка". Возвращенный Observable имеет функцию shareReplay(1), выполненную для возврата к общей подписке.
- Создайте сервис Executing Observable в качестве открытого свойства в модели представления. Эта наблюдаемая отображается из сервиса Observable. Он вернет истину, если статус "Загрузка"
- Привязать uilabel к сервису Status Observable
- Свяжите индикатор активности с сервисом 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
создается только один раз, следовательно, его побочный эффект будет передаваться при каждом его использовании, так как это один и тот же экземпляр.