2016-12-01 121 views
5

冷杉的整個我是新的rxswift所以我想答案是顯而易見的,但目前我無法自己找到解決方案。正確使用RxSwift來鏈接請求,flatMap或其他東西?

我有兩個功能:

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

第一種是下載任務列表中使用網絡的請求對象,第二個下載的sepcific任務的任務詳細信息(使用它的id)

我想要的是什麼實現是下載所有任務,然後爲每個任務我想下載其詳細信息,並訂閱所有任務詳細信息準備就緒的事件。

所以我想我應該訂閱某種方式Observable < [TaskDetails]>但我不知道該怎麼做。

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

//編輯

由於西爾Mosberger答案我更接近的解決方案。還有一個問題。現在,我有這樣的事情:

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) 

輸出是

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

有3個任務,所以你可以SE所有的人都正確但下載出於某種原因指定者的結果中提供() - (Observable<[TaskDetails]>)在所有任務細節準備好後都不會生成「onNext」。

//編輯再次

好吧,我加入的提供可觀的功能簡化版本,也許這將有助於東西

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

隨着RxSwift你要儘可能使用Observable S,因此,我建議您重構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>方法,因爲它是更簡潔的方法。

然後,您可以map你的任務,以獲得他們的ID(假設你的Task類型具有id: Int64財產)和flatMapdownloadAllTasks函數來獲得一個Observable<TaskDetails>

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

然後你可以使用toArray()運營商收集整個序列併發出包含數組中所有元素的事件:

let allDetails : Observable<[TaskDetails]> = details.toArray() 

總之,wi thout類型註釋和共享的任務(這樣你就不會下載他們只有一次):

let tasks = downloadAllTasks().share() 

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

編輯:請注意,當任何一個細節下載遇到錯誤時這可觀察就會報錯。我不完全知道什麼是防止這種情況的最好辦法,但這樣做的工作:

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返回一個可觀察的,從來沒有完成。下面是getTaskDetails一個簡單的參考實現(與String代替TaskDetails),使用JSONPlaceholder

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

尼斯和清潔的解釋,非常感謝。它幾乎可以按我的需要工作。幾乎,因爲我有問題與最後部分 - toArray()。如果沒有這部分,用戶將得到有關任務細節下載事件的正確通知使用toArray時,我希望在下載所有任務細節時,我會在訂閱者的onNext中收到通知,但onNext根本不會被調用。你看到什麼可能是一個問題? – Wujo

+0

@Wujo當單個任務細節下載遇到錯誤時,最終的'Observable'將出錯。你應該可以在'.toArray()'之前用'.debug()'來檢查。我編輯了我的答案,如果你想要它完成,即使有錯誤 –

+0

getTaskDetails期間的問題沒有出錯,所有的任務細節都正確下載。我編輯了我原來的帖子,試圖更清楚地指出它。 – Wujo