正确使用RxSwift来链接请求,flatMap或其他东西?

冷杉的整个我是rxswift新来的,所以我想答案是显而易见的,但目前我无法自己find解决scheme。

我有两个function:

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

第一个是使用networking请求下载任务对象列表,第二个下载任务详细信息的特定任务(使用它的ID)

我想要实现的是下载所有任务,然后为每个任务我想下载它的细节,并订阅所有任务细节准备就绪的事件。

所以我想我应该订阅Observable <[TaskDetails]>,但我不知道该怎么做。

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

//编辑

感谢Silvan Mosberger的回答,我更接近解决scheme。 还有一个问题。 现在我有这样的东西:

  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个任务可用,因此您可以正确下载所有这些任务但是由于某些原因,一旦所有任务细节准备就绪,toArray() – ( Observable<[TaskDetails]> )的结果不会生成“onNext”。

//再次编辑

好吧,我添加了提供observablesfunction的简化版本,也许它会帮助一些东西

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

使用RxSwift时,您希望尽可能使用Observable ,因此我build议您重构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(假设您的Tasktypes具有id: Int64属性),并使用downloadAllTasks函数获取一个Observable<TaskDetails>

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

然后,可以使用toArray()运算符来收集整个序列,并发出包含数组中所有元素的事件:

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

总之,没有键入注释和共享任务(所以你不会只下载一次):

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

编辑:请注意,当任何细节下载遇到错误时,此Observable将会出错。 我不完全确定什么是防止这种情况的最佳方法,但是这确实起作用:

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

编辑2:它不会工作,如果你的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() } } }