ios – Decoding Server Responses for 401 Standing Code Utilizing Generic Networking Layer

0
31
ios – Decoding Server Responses for 401 Standing Code Utilizing Generic Networking Layer


I bought the next code on-line and I’m making an attempt to switch it in a means that I can get entry to the decoded response for 401. Server sends the next 401 response as JSON.

{ message: 'Username or password is wrong', success: false }

However after I use the HTTPClient under it merely throws NetworkError.unauthorized with out giving me the message. How can I replace the load perform to get entry of the server response.

struct HTTPClient {
    
    personal let session: URLSession
    
    init() {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = ["Content-Type": "application/json"]
        self.session = URLSession(configuration: configuration)
    }
    
    func load(_ useful resource: Useful resource) async throws -> T {
        
        var request = URLRequest(url: useful resource.url)
        
        // Set HTTP technique and physique if wanted
        change useful resource.technique {
            case .get(let queryItems):
                var parts = URLComponents(url: useful resource.url, resolvingAgainstBaseURL: false)
                parts?.queryItems = queryItems
                guard let url = parts?.url else {
                    throw NetworkError.badRequest
                }
                request.url = url
                
            case .publish(let information), .put(let information):
                request.httpMethod = useful resource.technique.title
                request.httpBody = information
                
            case .delete:
                request.httpMethod = useful resource.technique.title
        }
        
        // Set customized headers
        if let headers = useful resource.headers {
            for (key, worth) in headers {
                request.setValue(worth, forHTTPHeaderField: key)
            }
        }
        
        let (information, response) = attempt await session.information(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw NetworkError.invalidResponse
        }
        
        // Examine for particular HTTP errors
        change httpResponse.statusCode {
            case 200...299:
                break // Success
            case 400:
                throw NetworkError.badRequest
            case 401:
                throw NetworkError.unauthorized
            case 403:
                throw NetworkError.forbidden
            case 404:
                throw NetworkError.notFound
            default:
                throw NetworkError.unknownStatusCode(httpResponse.statusCode)
        }
        
        do {
            let outcome = attempt JSONDecoder().decode(useful resource.modelType, from: information)
            return outcome
        } catch {
            throw NetworkError.decodingError(error)
        }
    }
}

UPDATE:

NetworkError Appears to be like like the next:

extension NetworkError: LocalizedError {
    
    var errorDescription: String? {
        change self {
            case .badRequest:
                return NSLocalizedString("Unhealthy Request (400): Unable to carry out the request.", remark: "badRequestError")
            case .unauthorized:
                return NSLocalizedString("Unauthorized (401): Authentication is required.", remark: "unauthorizedError")
            case .forbidden:
                return NSLocalizedString("Forbidden (403): You do not have permission to entry this useful resource.", remark: "forbiddenError")
            case .notFound:
                return NSLocalizedString("Not Discovered (404): The requested useful resource couldn't be discovered.", remark: "notFoundError")
            case .serverError(let errorMessage):
                return NSLocalizedString(errorMessage, remark: "serverError")
            case .decodingError:
                return NSLocalizedString("Unable to decode efficiently.", remark: "decodingError")
            case .invalidResponse:
                return NSLocalizedString("Invalid response.", remark: "invalidResponse")
            case .unknownStatusCode(let statusCode):
                return NSLocalizedString("Unknown error with standing code: (statusCode).", remark: "unknownStatusCodeError")
        }
    }
}

UPDATE 2:

I up to date the load perform and now after I get 401 I throw LoginError. Does that look proper? Since HTTPClient is a generic community layer it feels bizarre to throw such particular errors?

 // Examine for particular HTTP errors
        change httpResponse.statusCode {
            case 200...299:
                break // Success
            case 400:
                throw NetworkError.badRequest
            case 401:
                let response = attempt? JSONDecoder().decode(ErrorResponse.self, from: information)
                throw LoginError.loginFailed(response?.message ?? "Login error")
            case 403:
                throw NetworkError.forbidden
            case 404:
                throw NetworkError.notFound
            default:
                throw NetworkError.unknownStatusCode(httpResponse.statusCode)
        }

UPDATE 3:

Contained in the HTTPClient load perform, I up to date the code to throw the identical NetworkError as proven under:

  case 401:
                let response = attempt JSONDecoder().decode(ErrorResponse.self, from: information)
                throw NetworkError.unauthorized(response)

The caller (AuthenticationController) handles the decision after which throw the suitable error.

func login(username: String, password: String) async throws -> LoginResponse  {
        
        let physique = ["username": username, "password": password]
        let bodyData = attempt JSONEncoder().encode(physique)
        
        let useful resource = Useful resource(url: Constants.login, technique: .publish(bodyData), modelType: LoginResponse.self)
        do {
            let response = attempt await httpClient.load(useful resource)
            return response
        } catch NetworkError.unauthorized(let errorResponse) {
            throw LoginError.loginFailed(errorResponse.message ?? "Unable to login.")
        } catch {
            throw error
        }
        
    }

LEAVE A REPLY

Please enter your comment!
Please enter your name here