2016-12-01 5 views
5

私はrxswiftを初めて使っているので、答えは明らかですが、現時点では自分で解決策を見つけることはできません。RxSwiftを使用してリクエスト、flatMapなどをチェーンするのは適切ですか?

は、私は2つの機能を持っている:

func downloadAllTasks() -> Observable<[Task]> 
func getTaskDetails(taskId: Int64) -> Observable<TaskDetails> 

第1のネットワーク要求を使用してオブジェクトのタスクのリストをダウンロードし、sepcificタスクのタスクの詳細をダウンロードする二番目は、私が欲しい

(それはIDが使用されます)達成するためにすべてのタスクをダウンロードし、各タスクの詳細をダウンロードし、すべてのタスクの詳細が準備ができたときにイベントを購読したい。

だから私はObservable < [TaskDetails]に何とか申し込むべきだと思うが、どうやってそれを行うのか分からない。

 downloadAllTasks() 
     .flatMap{ 
      ... // flatMap? something else? 
     } 
     .subscribe(
      onNext: { details in 
       print("tasks details: \(details.map{$0.name})") 
     }) 
     .addDisposableTo(disposeBag) 

// EDITシルヴァンMosbergerの答えに

おかげで私は解決策に非常に近いです。残った1つの問題。出力は

$$$ task details array debug -> subscribed 
$$$ single task details downloaded 
$$$ single task details downloaded 
$$$ single task details downloaded 

あなたがSEそれらのすべてが何らかの理由でtoArray(の結果のために適切しかし、ダウンロードされたことができますように利用できる3つのタスクがあります)です

downloadAllTasks() 
     .flatMap{ Observable.from($0) } 
     .map{ $0.id } 
     .flatMap{ [unowned self] id in 
      self.getTaskDetails(taskId: id).catchError{ error in 
       print("$$$ Error downloading task \(id)") 
       return .empty() 
      } 
     } 
     .do(onNext: { _ in 
      print(" $$$ single task details downloaded") 
     }) 
     .toArray() 
     .debug("$$$ task details array debug", trimOutput: false) 
     .subscribe({ _ in 
      print("$$$ all tasks downloaded") 
     }) 
     .addDisposableTo(disposeBag) 

:今、私はこのような何かを持っています - (Observable<[TaskDetails]>)は、すべてのタスクの詳細が準備できたら、 "onNext"を生成しません。

//編集もう一度

[OK]を、私は観測を提供する機能の簡易版を追加している、多分それは何か役立つ

あなたは、可能な限り Observable Sを使用したいRxSwiftで
func downloadAllTasks() -> Observable<Task> { 
    return Observable.create { observer in 

      //... network request to download tasks 
      //... 

      for task in tasks { 
       observer.onNext(task) 
      } 
      observer.onCompleted() 

     return Disposables.create() 
    } 
} 


func getTaskDetails(id: Int64) -> Observable<TaskDetails> { 
    return Observable.create { observer in 

     //... network request to download task details 
      //... 

     observer.onNext(taskDetails) 

     return Disposables.create() 
    } 
} 

答えて

8

、したがって、 downloadAllTasksメソッドをリファクタリングしてObservable<Task>を返すことをお勧めします。これは、だけではなく、直接配列を発するの要素をループすることにより、かなり些細なことする必要があります

// In downloadAllTasks() -> Observable<Task> 
for task in receivedTasks { 
    observable.onNext(task) 
} 

これは、何らかの理由でできない場合は、そのためのオペレータはRxSwiftでもあります:

// Converts downloadAllTasks() -> Observable<[Task]> to Observable<Task> 
downloadAllTasks().flatMap{ Observable.from($0) } 

次のコードでは、リファクタリングされたdownloadAllTasks() -> Observable<Task>メソッドを使用します。これは、よりクリーンなアプローチであるためです。Observable<TaskDetails>を取得するためのdownloadAllTasks機能付き

あなたはその後、 mapあなたのタスクは、そのidは(あなたの Taskタイプは id: Int64性質を持っていると仮定した場合)を取得することができ

flatMap

let details : Observable<TaskDetails> = downloadAllTasks() 
    .map{ $0.id } 
    .flatMap(getTaskDetails) 

次にあなたがtoArray()演算子を使用することができます配列全体を集め、配列内のすべての要素を含むイベントを送出します。つまり、wiは配列内のすべての要素を含むイベントを送出します。 thout型注釈とタスクを共有することは(あなたが一度だけそれらをダウンロードしませんが):

let tasks = downloadAllTasks().share() 

let allDetails = tasks 
    .map{ $0.id } 
    .flatMap(getTaskDetails) 
    .toArray() 

EDIT:詳細のダウンロードのいずれかでエラーが発生したときに、この観測は、エラーになりますので注意してください。私はこれを防止するための最良の方法は何を正確にわからないんだけど、これは動作しません:

let allDetails = tasks 
    .map{ $0.id } 
    .flatMap{ id in 
     getTaskDetails(id: id).catchError{ error in 
      print("Error downloading task \(id)") 
      return .empty() 
     } 
    } 
    .toArray() 

EDIT2:あなたのgetTaskDetailsが完了したことがないこと、観察を返す場合、それは働くつもりではありません。ここでJSONPlaceholderを使用して、(String代わりのTaskDetailsと)getTaskDetailsの簡単なリファレンス実装です:

func getTaskDetails(id: Int64) -> Observable<String> { 
    let url = URL(string: "https://jsonplaceholder.typicode.com/posts/\(id)")! 
    return Observable.create{ observer in 
     let task = URLSession.shared.dataTask(with: url) { data, response, error in 
      if let error = error { 
       observer.onError(error) 
      } else if let data = data, let result = String(data: data, encoding: .utf8) { 
       observer.onNext(result) 
       observer.onCompleted() 
      } else { 
       observer.onError("Couldn't get data") 
      } 
     } 
     task.resume() 

     return Disposables.create{ 
      task.cancel() 
     } 
    } 
} 
+0

おかげでたくさんのきれいな説明は、。そして、私が望むようにほとんど働いています。ほとんど、私は最後の部分に問題があるbeArause - toArray()。この部分がなければ、サブスクライバはタスクの詳細ダウンロードイベントについて適切に通知されます。 toArrayを使用すると、すべてのタスクの詳細がダウンロードされたときにonNextで通知されますが、onNextはまったく呼び出されません。何が問題になるか見ていますか? – Wujo

+0

@Wujo単一のタスクの詳細ダウンロードでエラーが発生すると、最後の 'Observable'はエラーになります。あなたは '.toArray()'の前に '.debug() 'でそれをチェックできるはずです。 –

+0

getTaskDetails中に問題が発生していないため、すべてのタスクの詳細が適切にダウンロードされています。元の投稿を編集して、より明確に指摘しようとしました。 – Wujo

関連する問題