With RxSwift, you want to use Observable whenever possible, so I recommend that you reorganize the downloadAllTasks method to return an Observable<Task> . This should be pretty trivial, just looping through the elements, instead of directly emitting an array:
// In downloadAllTasks() -> Observable<Task> for task in receivedTasks { observable.onNext(task) }
If this is not possible for any reason, RxSwift also has a statement:
// Converts downloadAllTasks() -> Observable<[Task]> to Observable<Task> downloadAllTasks().flatMap{ Observable.from($0) }
In the following code, I will use the refactored method downloadAllTasks() -> Observable<Task> , because it is a cleaner approach.
You can map to perform its tasks in order to get its identifier (if your Task type has the id: Int64 property) and flatMap using the downloadAllTasks function to get an Observable<TaskDetails> :
let details : Observable<TaskDetails> = downloadAllTasks() .map{ $0.id } .flatMap(getTaskDetails)
Then you can use the toArray() operator to collect the entire sequence and emit an event containing all the elements in the array:
let allDetails : Observable<[TaskDetails]> = details.toArray()
In short, without type annotations and task sharing (so you wonβt download them only once):
let tasks = downloadAllTasks().share() let allDetails = tasks .map{ $0.id } .flatMap(getTaskDetails) .toArray()
EDIT: Note that this Observable will be erroneous if any of the part loads encounters an error. I'm not quite sure the best way to prevent this, but this works:
let allDetails = tasks .map{ $0.id } .flatMap{ id in getTaskDetails(id: id).catchError{ error in print("Error downloading task \(id)") return .empty() } } .toArray()
EDIT2: it will not work if your getTaskDetails returns an observable that never completes. The following is a simple reference implementation of getTaskDetails (with String instead of TaskDetails ) using 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() } } }