Combine handle different type of publishers

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 switching 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.

Leave a Comment

Your email address will not be published.

Scroll to Top