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?
CodePudding user response:
In your case replaceError(with:)
should help. With this function you can replace errors with a default value.
It would look somethink like this:
Publishers.Zip(
m1(url: "",type: [StudentDTO].self),
m1(url: "",type: [AddressDTO].self).replaceError(with: []).setFailureType(to: Error.self)
)
CodePudding user response:
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.:
.sink { completion in
switch completion{
case .finished:
break
case .failure(let error):
//handle error here
}
}
Edit:
A more complete implementation would look like this:
Publishers.Zip(
m1(url: "",type: [StudentDTO].self),
m1(url: "",type: [AddressDTO].self)
)
.sink(receiveCompletion: { completion in
switch completion{
case .finished:
break
case .failure(let error):
// handle error here
}
} , 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)
})