-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from checkout/feature/granular-error-handling
Map URLSession errors into CheckoutNetworkError
- Loading branch information
Showing
9 changed files
with
178 additions
and
80 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
Sources/CheckoutNetwork/CheckoutNetworkClient+AsyncWrappers.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// | ||
// CheckoutNetworkClient+AsyncWrappers.swift | ||
// | ||
// | ||
// Created by Okhan Okbay on 20/02/2024. | ||
// | ||
|
||
import Foundation | ||
|
||
public extension CheckoutNetworkClient { | ||
|
||
func runRequest<T: Decodable>(with configuration: RequestConfiguration) async throws -> T { | ||
return try await withCheckedThrowingContinuation { continuation in | ||
runRequest(with: configuration) { (result: Result<T, Error>) in | ||
switch result { | ||
case .success(let response): | ||
continuation.resume(returning: response) | ||
case .failure(let error): | ||
continuation.resume(throwing: error) | ||
} | ||
} | ||
} | ||
} | ||
|
||
func runRequest(with configuration: RequestConfiguration) async throws { | ||
return try await withCheckedThrowingContinuation { continuation in | ||
runRequest(with: configuration) { (error: Error?) in | ||
|
||
guard let error = error else { | ||
continuation.resume(returning: Void()) | ||
return | ||
} | ||
|
||
continuation.resume(throwing: error) | ||
} | ||
} | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
Sources/CheckoutNetwork/CheckoutNetworkClient+ErrorHandling.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// | ||
// CheckoutNetworkClient+ErrorHandling.swift | ||
// | ||
// | ||
// Created by Okhan Okbay on 20/02/2024. | ||
// | ||
|
||
import Foundation | ||
|
||
extension CheckoutNetworkClient { | ||
func getErrorFromResponse(_ response: URLResponse?, data: Data?) -> Error? { | ||
guard let response = response as? HTTPURLResponse else { | ||
return CheckoutNetworkError.invalidURLResponse | ||
} | ||
|
||
guard response.statusCode != 422 else { | ||
do { | ||
let errorReason = try JSONDecoder().decode(ErrorReason.self, from: data ?? Data()) | ||
return CheckoutNetworkError.unprocessableContent(reason: errorReason) | ||
} catch { | ||
return CheckoutNetworkError.noDataResponseReceived | ||
} | ||
} | ||
|
||
guard (200..<300).contains(response.statusCode) else { | ||
return CheckoutNetworkError.unexpectedHTTPResponse(code: response.statusCode) | ||
} | ||
return nil | ||
} | ||
|
||
func convertDataTaskErrorsToCheckoutNetworkError(error: Error) -> CheckoutNetworkError { | ||
let error = error as NSError | ||
|
||
switch error.code { | ||
case NSURLErrorNotConnectedToInternet, | ||
NSURLErrorTimedOut, | ||
NSURLErrorNetworkConnectionLost, | ||
NSURLErrorInternationalRoamingOff, | ||
NSURLErrorCannotConnectToHost, | ||
NSURLErrorServerCertificateUntrusted: | ||
|
||
return .connectivity | ||
|
||
default: | ||
return .other(underlyingError: error) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,67 @@ | ||
// | ||
// CheckoutNetworkError.swift | ||
// | ||
// | ||
// | ||
// Created by Alex Ioja-Yang on 20/06/2022. | ||
// | ||
|
||
import Foundation | ||
|
||
public enum CheckoutNetworkError: Error, Equatable { | ||
|
||
/// Review the url you provided | ||
case invalidURL | ||
|
||
/// Network response was not in the 200 range | ||
case unexpectedResponseCode(code: Int) | ||
|
||
/// Network call and completion appear valid but no data was returned making the parsing impossible. Use runRequest method with NoDataResponseCompletionHandler if no data is expected (HTTP 204 is a success case with no content being returned) | ||
case noDataResponseReceived | ||
|
||
/// Network response returned with HTTP Code 422 | ||
case invalidData(reason: ErrorReason) | ||
public enum CheckoutNetworkError: LocalizedError, Equatable { | ||
|
||
/// Review the url you provided | ||
case invalidURL | ||
|
||
/// When a response could not be bound to an instance of HTTPURLResponse | ||
case invalidURLResponse | ||
|
||
/// Network response was not in the 200 range | ||
case unexpectedHTTPResponse(code: Int) | ||
|
||
/// Network call and completion appear valid but no data was returned making the parsing impossible. Use runRequest method with NoDataResponseCompletionHandler if no data is expected (HTTP 204 is a success case with no content being returned) | ||
case noDataResponseReceived | ||
|
||
/// Network response returned with HTTP Code 422 | ||
case unprocessableContent(reason: ErrorReason) | ||
|
||
/// Connectivity errors mapped from URLSession.dataTask()'s error | ||
/// | ||
/// Only the following are mapped into this error case: | ||
/// NSURLErrorNotConnectedToInternet | ||
/// NSURLErrorTimedOut | ||
/// NSURLErrorNetworkConnectionLost | ||
/// NSURLErrorInternationalRoamingOff | ||
/// NSURLErrorCannotConnectToHost | ||
/// NSURLErrorServerCertificateUntrusted | ||
case connectivity | ||
|
||
/// All the other errors that can be received from URLSession. | ||
/// Use the underlying error if you need more granular error handling. | ||
/// Underlying errors here are in NSURLErrorDomain. | ||
case other(underlyingError: NSError) | ||
|
||
public var errorDescription: String? { | ||
switch self { | ||
case .invalidURL: | ||
return "Could not instantiate a URL with the provided String value" | ||
|
||
case .invalidURLResponse: | ||
return "Could not instantiate an HTTPURLResponse with the received response value" | ||
|
||
case .unexpectedHTTPResponse(let code): | ||
return "Received an unexpected HTTP response: \(code)" | ||
|
||
case .noDataResponseReceived: | ||
return "No data is received in the response body. Use the method with NoDataResponseCompletionHandler if no data was expected" | ||
|
||
case .unprocessableContent(let reason): | ||
return "HTTP response 422 is received with: \(reason)" | ||
|
||
case .connectivity: | ||
return "There is a problem with the internet connection" | ||
|
||
case .other(let error): | ||
return "An unhandled error is produced: \(error)" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters