【发布时间】:2021-09-20 07:05:36
【问题描述】:
我正在尝试使用 Combine 框架执行并发 API 调用。 API 调用的设置如下:
- 首先,调用API获取
Posts的列表 - 对于每个
post,调用另一个API来获取Comments
我想使用 Combine 将这两个调用同时链接在一起,以便它返回一个 Post 对象数组,每个 post 都包含 cmets 数组。
我的尝试:
struct Post: Decodable {
let userId: Int
let id: Int
let title: String
let body: String
var comments: [Comment]?
}
struct Comment: Decodable {
let postId: Int
let id: Int
let name: String
let email: String
let body: String
}
class APIClient: ObservableObject {
@Published var posts = [Post]()
var cancellables = Set<AnyCancellable>()
init() {
getPosts()
}
func getPosts() {
let urlString = "https://jsonplaceholder.typicode.com/posts"
guard let url = URL(string: urlString) else {return}
URLSession.shared.dataTaskPublisher(for: url)
.receive(on: DispatchQueue.main)
.tryMap({ (data, response) -> Data in
guard
let response = response as? HTTPURLResponse,
response.statusCode >= 200 else {
throw URLError(.badServerResponse)
}
return data
})
.decode(type: [Post].self, decoder: JSONDecoder())
.sink { (completion) in
print("Posts completed: \(completion)")
} receiveValue: { (output) in
//Is there a way to chain getComments such that receiveValue would contain Comments??
output.forEach { (post) in
self.getComments(post: post)
}
}
.store(in: &cancellables)
}
func getComments(post: Post) {
let urlString = "https://jsonplaceholder.typicode.com/posts/\(post.id)/comments"
guard let url = URL(string: urlString) else {
return
}
URLSession.shared.dataTaskPublisher(for: url)
.receive(on: DispatchQueue.main)
.tryMap({ (data, response) -> Data in
guard
let response = response as? HTTPURLResponse,
response.statusCode >= 200 else {
throw URLError(.badServerResponse)
}
return data
})
.decode(type: [Comment].self, decoder: JSONDecoder())
.sink { (completion) in
print("Comments completed: \(completion)")
} receiveValue: { (output) in
print("Comment", output)
}
.store(in: &cancellables)
}
}
如何将getComments 链接到getPosts 以便可以在getPosts 中接收cmets 的输出?传统上使用 UIKit,我会使用 DispatchGroup 来完成这项任务。
请注意,我希望只接收来自 APIClient 的帖子的单个 Publisher 事件,以便 SwiftUI 视图仅刷新一次。
【问题讨论】:
-
有没有像 rxjs 那样的 CombineLatest?
-
@DanChase 有一个 CombineLatest,但我想这个方法会监听 2 个发布者。知道如何在上述用例中应用它吗?
-
@Koh 对不起,我误读了用例。不是一个实际的答案,但过去我已经通过在后端创建一个组合结构来解决这个问题,并且只在 HTTP 端进行一次调用。在后端,API 表面可以调用多个业务层函数并创建一个结构来返回。在我正在进行的一个当前项目中,它已经增长到 8 个 HTTP Get,并且我开始遇到一些问题,一些问题先于其他人返回并给用户造成混乱,以及停止浏览器的问题。我相信 HTTP 1.1 的限制为 6 .. 我希望这比我的 prev 更有帮助。评论。
-
@Koh 更多地考虑 combineLatest,我认为我的思考过程是关于使用 combineLatest 和 2 个 observables,一个用于 master,一个用于 detail,每个循环。但我越想这个想法就越糟糕,这导致了我在上面的评论。