2017-01-16 46 views
1

現在、RxSwift Observablesを使用しているときに複数のネットワーク要求が実行されている問題が発生しています。私は、冷たい観察可能なものを作成し、複数の観察者がいる場合、観察可能なものは、購読するたびにそのブロックを実行することを理解しています。RxSwift:複数のネットワーク要求を防止する

ネットワーク要求を一度実行する共有サブスクリプションオブザーバブルを作成しようとしましたが、複数のサブスクライバに結果が通知されます。以下は私が試したことです。イベント

  1. シーケンスuibutton

  2. のタップイベントとビューモデルを作成し、ビューモデルのパブリックプロパティとしてserviceStatus観測を作成します。このObservableは、buttonTapped Observableからマップされます。次に、「読み込み中」ステータスをフィルタリングします。返されるObservableには、共有サブスクリプションを返すためにshareReplay(1)が実行されます。
  3. serviceExecutingをビューモデルのパブリックプロパティとして監視可能に作成します。このobservableは、observiceableのserviceStatusからマップされます。ステータスが「読み込み中」の場合はtrueを返します。
  4. サービスステータスにuilabelをバインドします。Observable
  5. serviceExecuting Observableにアクティビティインジケータをバインドします。

ボタンをタップすると、サービス要求が3回実行され、そこで1回だけ実行されることが期待されます。何かが間違っているように目立つのですか

コード

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

EDIT:以下の会話に基づいて

、次は私が探していたものです...

私はシェアを適用するために必要な( )関数は、getServiceStatusObservable()メソッドから返されたObservableではなく、createServiceStatusObservable()メソッドから返されたObservableではなく返されます。現在の状態を検査するために、この観測に複数のオブザーバーが追加されました。これは、ネットワーク要求を実行しているオブザーバブルがN回実行されていることを意味しました(Nはオブザーバの数です)。今すぐボタンがタップされるたびに、ネットワーク要求が一度実行されます。これは必要なものです。

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

答えて

4

.shareReplay(1)は、オブザーバブルの1つのインスタンスにのみ適用されます。 createServiceStatusObservable()にそれを作成するとき、共有動作はこの関数によって返される1つの値にのみ影響します。このバージョンで

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は一度だけ作成され、従ってそれは副作用のITが同じインスタンスであるように、それが使用されるたびに共有されます。

+0

これは良い点であり、ボタンの最初のタップで問題が解決されたようです。 shareReplay()をshare()に更新しました。ユーザーが新しいサービスリクエストを実行するために複数回ボタンをタップできるように、結果をキャッシュしたくないからです。これは、ボタンの後続のタップがサービスへの複数のリクエストをもたらすため、他のすべてのオブザーバブルで同じことを行う必要があることを意味します。 –

+0

最初のサブスクリプションの購読を中止する前に他のオブザーバブルへのサブスクリプションが行われている限り、ネットワーク要求にのみ 'share'を置くことができます。 (この場合、新しい要求が実行されます)。 'share()'は、[here](https://github.com/ReactiveX/RxSwift/blob/3c55a309a24fbe3dbc480431798eec072b633b85/RxSwift/Observables/Observable%2BBinding.swift#L136)で文書化されています。 – tomahh

+0

Gotcha。私はgetServiceStatusObservable()メソッドに関してここで混乱していると思いました。ボタンをタップすると、登録されたブロックが3回呼び出されます。 Observable in return self.serviceStatusObservable } –

関連する問題