Skip to main content
AI Assist is now on Stack Overflow. Start a chat to get instant answers from across the network. Sign up to save and share your chats.
deleted 4 characters in body
Source Link
Koh
  • 2.9k
  • 1
  • 32
  • 73
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() { fetchPostsgetPosts() } func fetchPostsgetPosts() { 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) } } 
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() { fetchPosts() } func fetchPosts() { 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) } } 
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) } } 
Source Link
Koh
  • 2.9k
  • 1
  • 32
  • 73

Swift: How to perform concurrent API calls using Combine

I am attempting to perform concurrent API calls using the Combine framework. The API calls are set up like so:

  1. First, call an API to get a list of Posts
  2. For each post, call another API to get Comments

I would like to use Combine to chain these two calls together and concurrently so that it returns an array of Post objects with each post containing the comments array.

My attempt:

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() { fetchPosts() } func fetchPosts() { 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) } } 

How do I chain getComments to getPosts so that the output of comments can be received in getPosts? Traditionally using UIKit, I would use DispatchGroup for this task.

Note that I would like to receive just a single Publisher event for posts from the APIClient so that the SwiftUI view is refreshed only once.