Skip to content

Commit

Permalink
Better response error handling (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
F0x1d committed Nov 21, 2023
1 parent f591dab commit b9bdc84
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 44 deletions.
39 changes: 17 additions & 22 deletions Papyrus/URLSession+Papyrus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extension URLSession: HTTPService {
request.httpMethod = method
request.httpBody = body
request.allHTTPHeaderFields = headers
return _Request(request: request)
return request
}

public func request(_ req: Request) async -> Response {
Expand Down Expand Up @@ -51,21 +51,23 @@ extension URLSession: HTTPService {
// MARK: `Response` Conformance

extension Response {
public var urlRequest: URLRequest { (self as! _Response).request }
public var urlResponse: URLResponse? { (self as! _Response).response }
public var urlRequest: URLRequest { (self as! _Response).urlRequest }
public var urlResponse: URLResponse? { (self as! _Response).urlResponse }
}

private struct _Response: Response {
let request: URLRequest
let response: URLResponse?
let urlRequest: URLRequest
let urlResponse: URLResponse?

var request: Request? { urlRequest }
let error: Error?
let body: Data?
let headers: [String: String]?
var statusCode: Int? { (urlResponse as? HTTPURLResponse)?.statusCode }

init(request: URLRequest, response: URLResponse?, error: Error?, body: Data?) {
self.request = request
self.response = response
self.urlRequest = request
self.urlResponse = response
self.error = error
self.body = body
let headerPairs = (response as? HTTPURLResponse)?
Expand All @@ -89,30 +91,23 @@ private struct _Response: Response {

extension Request {
public var urlRequest: URLRequest {
(self as! _Request).request
(self as! URLRequest)
}
}

private struct _Request: Request {
var request: URLRequest

public var url: URL {
get { request.url! }
set { request.url = newValue }
}

extension URLRequest: Request {
public var body: Data? {
get { request.httpBody }
set { request.httpBody = newValue }
get { httpBody }
set { httpBody = newValue }
}

public var method: String {
get { request.httpMethod ?? "" }
set { request.httpMethod = newValue }
get { httpMethod ?? "" }
set { httpMethod = newValue }
}

public var headers: [String: String] {
get { request.allHTTPHeaderFields ?? [:] }
set { request.allHTTPHeaderFields = newValue }
get { allHTTPHeaderFields ?? [:] }
set { allHTTPHeaderFields = newValue }
}
}
27 changes: 11 additions & 16 deletions PapyrusAlamofire/Alamofire+Papyrus.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ extension Session: HTTPService {
request.httpMethod = method
request.httpBody = body
request.allHTTPHeaderFields = headers
return _Request(request: request)
return request
}

public func request(_ req: PapyrusCore.Request) async -> Response {
Expand All @@ -42,6 +42,8 @@ extension Response {
}

extension DataResponse: Response {
@_implements(Response, request)
public var _request: PapyrusCore.Request? { request }
public var body: Data? { data }
public var headers: [String : String]? { response?.headers.dictionary }
public var statusCode: Int? { response?.statusCode }
Expand All @@ -55,30 +57,23 @@ extension DataResponse: Response {

extension PapyrusCore.Request {
public var urlRequest: URLRequest {
(self as! _Request).request
(self as! URLRequest)
}
}

private struct _Request: PapyrusCore.Request {
var request: URLRequest

public var url: URL {
get { request.url! }
set { request.url = newValue }
}

extension URLRequest: PapyrusCore.Request {
public var body: Data? {
get { request.httpBody }
set { request.httpBody = newValue }
get { httpBody }
set { httpBody = newValue }
}

public var method: String {
get { request.httpMethod ?? "" }
set { request.httpMethod = newValue }
get { httpMethod ?? "" }
set { httpMethod = newValue }
}

public var headers: [String: String] {
get { request.allHTTPHeaderFields ?? [:] }
set { request.allHTTPHeaderFields = newValue }
get { allHTTPHeaderFields ?? [:] }
set { allHTTPHeaderFields = newValue }
}
}
10 changes: 9 additions & 1 deletion PapyrusCore/Sources/PapyrusError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@
public struct PapyrusError: Error {
/// What went wrong.
public let message: String
/// Error related request.
public let request: Request?
/// Error related response.
public let response: Response?

/// Create an error with the specified message.
///
/// - Parameter message: What went wrong.
public init(_ message: String) {
/// - Parameter request: Error related request.
/// - Parameter response: Error related response.
public init(_ message: String, _ request: Request? = nil, _ response: Response? = nil) {
self.message = message
self.request = request
self.response = response
}
}
2 changes: 1 addition & 1 deletion PapyrusCore/Sources/Request.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Foundation

public protocol Request {
var url: URL { get set }
var url: URL? { get set }
var method: String { get set }
var headers: [String: String] { get set }
var body: Data? { get set }
Expand Down
14 changes: 10 additions & 4 deletions PapyrusCore/Sources/Response.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Foundation

public protocol Response {
var request: Request? { get }
var body: Data? { get }
var headers: [String: String]? { get }
var statusCode: Int? { get }
Expand All @@ -12,7 +13,7 @@ extension Response {
@discardableResult
public func validate() throws -> Self {
if let error { throw error }
if let statusCode, !(200..<300).contains(statusCode) { throw PapyrusError("Unsuccessful status code: \(statusCode).") }
if let statusCode, !(200..<300).contains(statusCode) { throw makePapyrusError(with: "Unsuccessful status code: \(statusCode).") }
return self
}

Expand All @@ -22,19 +23,23 @@ extension Response {

public func decode(_ type: Data.Type = Data.self, using decoder: ResponseDecoder) throws -> Data {
guard let body = try decode(Data?.self, using: decoder) else {
throw PapyrusError("Unable to return the body of a `Response`; the body was nil.")
throw makePapyrusError(with: "Unable to return the body of a `Response`; the body was nil.")
}

return body
}

public func decode<D: Decodable>(_ type: D.Type = D.self, using decoder: ResponseDecoder) throws -> D {
guard let body = try validate().body else {
throw PapyrusError("Unable to decode `\(Self.self)` from a `Response`; body was nil.")
guard let body else {
throw makePapyrusError(with: "Unable to decode `\(Self.self)` from a `Response`; body was nil.")
}

return try decoder.decode(type, from: body)
}

private func makePapyrusError(with message: String) -> PapyrusError {
PapyrusError(message, request, self)
}
}

extension Response where Self == ErrorResponse {
Expand All @@ -50,6 +55,7 @@ public struct ErrorResponse: Response {
self._error = error
}

public var request: Request? { nil }
public var body: Data? { nil }
public var headers: [String : String]? { nil }
public var statusCode: Int? { nil }
Expand Down
1 change: 1 addition & 0 deletions PapyrusPlugin/Sources/APIMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ extension FunctionDeclSyntax {

return """
let res = try await provider.request(req)
try res.validate()
return \(resultExpression)
"""
}
Expand Down
5 changes: 5 additions & 0 deletions PapyrusPlugin/Tests/APIMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ final class APIMacroTests: XCTestCase {
var req = builder(method: "GET", path: "some/path")
req.addQuery("userId", value: userId)
let res = try await provider.request(req)
try res.validate()
return try res.decode(String.self, using: req.responseDecoder)
}
Expand Down Expand Up @@ -139,6 +140,7 @@ final class APIMacroTests: XCTestCase {
var req = builder(method: "GET", path: "some/path")
req.addQuery("userId", value: userId)
let res = try await provider.request(req)
try res.validate()
return try res.decode(String.self, using: req.responseDecoder)
}
Expand Down Expand Up @@ -177,6 +179,7 @@ final class APIMacroTests: XCTestCase {
var req = builder(method: "GET", path: "some/path")
req.addField("userId", value: userId)
let res = try await provider.request(req)
try res.validate()
return try res.decode(String.self, using: req.responseDecoder)
}
Expand Down Expand Up @@ -222,6 +225,7 @@ final class APIMacroTests: XCTestCase {
req.addParameter("userId", value: userId)
req.addQuery("since", value: since)
let res = try await provider.request(req)
try res.validate()
return try res.decode(String.self, using: req.responseDecoder)
}
Expand Down Expand Up @@ -268,6 +272,7 @@ final class APIMacroTests: XCTestCase {
req.addParameter("userId", value: userId)
req.addQuery("since", value: since)
let res = try await provider.request(req)
try res.validate()
return try res.decode(String.self, using: req.responseDecoder)
}
Expand Down

0 comments on commit b9bdc84

Please sign in to comment.