So you have a network request, which in case of a successful request, returns a 200 response and an empty body, while in case of a form error, it returns a specific status code and an error in the response.
I would suggest keeping the Output
type of your Publisher
as Void
, however, in case of a form error, decoding the error and throwing it as part of your APIError
.
struct LoginError: Decodable {
let usernameError: String
let emailError: String
}
enum APIError: Error {
case failureStatus(code: Int)
case login(LoginError)
case nonHttpResponse(description: String)
case network(Error)
}
func emptyResult() -> AnyPublisher<Void, APIError> {
return URLSession.shared.dataTaskPublisher(for: urlRequest)
.print("EMPTY RESULT")
.tryMap { data, response in
guard let httpResponse = response as? HTTPURLResponse else { throw APIError.nonHttpResponse(description: "Not http response") }
let statusCode = httpResponse.statusCode
guard (200..<300).contains(statusCode) else {
if statusCode == 442 {
let loginError = try JSONDecoder().decode(LoginError.self, from: data)
throw APIError.login(loginError)
} else {
throw APIError.failureStatus(code: statusCode)
}
}
return Void()
}.mapError { error in
switch error {
case let apiError as APIError:
return apiError
default:
return .network(error)
}
}
.eraseToAnyPublisher()
}
Then you can handle the specific error by switch
ing over the error
in sink
:
API.registration(name: name, email: email, password: password, schoolID: selectedSchool?.id ?? 0)
.print("Registration")
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { (completion) in
switch completion {
case let .failure(error):
switch error {
case .login(let loginError):
print("Login failed, \(loginError.emailError), \(loginError.usernameError)")
default:
print(error)
}
case .finished: break
}
}, receiveValue: { value in
print(value)
})
.store(in: &disposables)
CLICK HERE to find out more related problems solutions.