2017-01-16 294 views
1

我目前在使用RxSwift Observables時執行多個網絡請求時會出現問題。我明白,如果一個人創建了一個冷觀察者,並且它有多個觀察者,那麼觀察者將在每次訂閱時執行它的塊。RxSwift:防止多個網絡請求

我試圖創建一次執行網絡請求的共享訂閱observable,並將多個訂戶通知結果。以下是我所嘗試的。事件

  1. 序列與一個UIButton

  2. 的點擊事件創建視圖模型創建serviceStatus觀察爲上視圖模型公共財產。這個Observable從buttonTapped Observable映射而來。然後過濾掉「加載」狀態。返回的Observable在其上執行shareReplay(1)以返回共享訂閱。
  3. 在視圖模型上創建serviceExecuting Observable作爲公共屬性。這個可觀察值是從serviceStatus Observable映射而來的。如果狀態是「加載」
  4. 綁定的UILabel到serviceStatus可觀察
  5. 綁定活動的指標爲serviceExecuting可觀察到它會返回true。

當按鈕被點擊時,服務請求被執行三次,我期待它只被執行一次。有什麼事情脫穎而出嗎?

代碼

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) 
} 

編輯:

基於下面的對話,下面就是我一直在尋找...

我需要申請份額( )函數返回從getServiceStatusObservable()方法返回的Observable,而不是從createServiceStatusObservable()方法返回的Observable。有多名觀察員被添加到這個觀察者來檢查當前狀態。這意味着執行網絡請求的observable被執行了N次(N是觀察者的數量)。現在每次點擊按鈕時,網絡請求都會執行一次,這正是我所需要的。

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

回答

4

.shareReplay(1)將只適用於observable的一個實例。在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僅創建一次,因此它的副作用將共享每次它被使用,因爲它是相同的實例

+0

這是一個很好的地方,它看起來像解決了第一次點擊按鈕的問題。我更新shareReplay()to share(),因爲我不希望緩存結果,因爲用戶可以多次點擊該按鈕來執行新的服務請求。這是否意味着我應該對所有其他可觀察的事件做同樣的事情,因爲按鈕的後續點擊會導致對該服務的多個請求。 –

+0

只要在第一次訂閱取消訂閱之前完成對其他觀察對象的訂閱,就可以僅在網絡請求上保留「共享」。 (在這種情況下,將執行新的請求)。 'share()'記錄在[這裏](https://github.com/ReactiveX/RxSwift/blob/3c55a309a24fbe3dbc480431798eec072b633b85/RxSwift/Observables/Observable%2BBinding.swift#L136)。 – tomahh

+0

哎呀。我猜想我在這裏對getServiceStatusObservable()方法感到困惑。點擊按鈕時,已註冊的塊被調用三次。 {[weak self] _ - > Observable in return self.serviceStatusObservable } –