I have a requirement to send two independent requests to two remote APIs and need to process both responses at once when both requests are completed. I did the basic implementation using Zip operator. It works really fine in the happy scenario. Please check below sample code.
import Foundation
import Combine
enum NetowrkError: Error {
case decodingError
case requestFailed
}
struct StudentDTO: Codable {
let name: String
let age: Int
let addressId: Int
}
struct AddressDTO: Codable {
let id: Int
let town: String
}
struct Student {
let name: String
let age: Int
let town: String
}
func m1<T: Codable>(url: String, type: T.Type) -> Future<T, NetowrkError> {
return Future { promise in
//Send request using URLSessionDatatask
}
}
Publishers.Zip(
m1(url: "",type: [StudentDTO].self),
m1(url: "",type: [AddressDTO].self)
).sink(receiveCompletion: { print($0) },
receiveValue: { studentList, addresses in
//Process Both Resutls and join Student and Address to have a single Model
let addressDict = addresses.reduce(into: [Int: String]()) {
print($1)
$0[$1.id] = $1.town
}
let students = studentList.map { student in
return Student(name: student.name, age: student.age, town: addressDict[student.addressId] ?? "")
}
//self?.processStudents(students: students)
})
But when it comes to error handling with the Zip operator it seems a bit difficult. Because the Zip operator emits only when both requests get successful. My requirement is to show an error message when a request to Studen API get failed but should be able to proceed in the app even if the call to address the endpoint get failed. How can I do it with Combine?
2
Answers
When a Swift Combine publisher fails it completes. You can use the
.sink { completion in
operator to handle your error. This will even work if only one publisher emits an error.e.g.:
Edit:
A more complete implementation would look like this:
Edit 2:
If you want to handle your errors separately you would need to do that inside your
.Zip
operator..mapError(...
seems appropriate for that.e.g.:
In your case
replaceError(with:)
should help. With this function you can replace errors with a default value.It would look somethink like this: