Skip to content

Commit 261af5f

Browse files
committed
Allow DataParser to return typed objects
DataParser should not be required to return Any and have objects checked for type safety at runtime. This allows us to use the Swift build time type checking system with very minimal changes to existing code. The only API breaking change is that Request classes using the default JSONDataParser should now conform to JSONRequest, instead of just Request. This is because protocol extensions cannot declare default associated types.
1 parent 6bb7961 commit 261af5f

File tree

4 files changed

+21
-11
lines changed

4 files changed

+21
-11
lines changed

Sources/APIKit/DataParser/DataParser.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import Foundation
22

33
/// `DataParser` protocol provides inteface to parse HTTP response body and to state Content-Type to accept.
44
public protocol DataParser {
5+
associatedtype Parsed
6+
57
/// Value for `Accept` header field of HTTP request.
68
var contentType: String? { get }
79

810
/// Return `Any` that expresses structure of response such as JSON and XML.
911
/// - Throws: `Error` when parser encountered invalid format data.
10-
func parse(data: Data) throws -> Any
12+
func parse(data: Data) throws -> Parsed
1113
}

Sources/APIKit/Request.swift

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Result
1111
public protocol Request {
1212
/// The response type associated with the request type.
1313
associatedtype Response
14+
associatedtype Parser: DataParser
1415

1516
/// The base URL.
1617
var baseURL: URL { get }
@@ -41,7 +42,7 @@ public protocol Request {
4142
var headerFields: [String: String] { get }
4243

4344
/// The parser object that states `Content-Type` to accept and parses response body.
44-
var dataParser: DataParser { get }
45+
var dataParser: Parser { get }
4546

4647
/// Intercepts `URLRequest` which is created by `Request.buildURLRequest()`. If an error is
4748
/// thrown in this method, the result of `Session.send()` turns `.failure(.requestError(error))`.
@@ -53,15 +54,16 @@ public protocol Request {
5354
/// The default implementation of this method is provided to throw `RequestError.unacceptableStatusCode`
5455
/// if the HTTP status code is not in `200..<300`.
5556
/// - Throws: `Error`
56-
func intercept(object: Any, urlResponse: HTTPURLResponse) throws -> Any
57+
func intercept(object: Parser.Parsed, urlResponse: HTTPURLResponse) throws -> Parser.Parsed
5758

5859
/// Build `Response` instance from raw response object. This method is called after
5960
/// `intercept(object:urlResponse:)` if it does not throw any error.
6061
/// - Throws: `Error`
61-
func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response
62+
func response(from object: Parser.Parsed, urlResponse: HTTPURLResponse) throws -> Response
6263
}
6364

6465
public extension Request {
66+
6567
public var parameters: Any? {
6668
return nil
6769
}
@@ -86,15 +88,11 @@ public extension Request {
8688
return [:]
8789
}
8890

89-
public var dataParser: DataParser {
90-
return JSONDataParser(readingOptions: [])
91-
}
92-
9391
public func intercept(urlRequest: URLRequest) throws -> URLRequest {
9492
return urlRequest
9593
}
9694

97-
public func intercept(object: Any, urlResponse: HTTPURLResponse) throws -> Any {
95+
public func intercept(object: Parser.Parsed, urlResponse: HTTPURLResponse) throws -> Parser.Parsed {
9896
guard 200..<300 ~= urlResponse.statusCode else {
9997
throw ResponseError.unacceptableStatusCode(urlResponse.statusCode)
10098
}
@@ -146,3 +144,13 @@ public extension Request {
146144
return try response(from: passedObject, urlResponse: urlResponse)
147145
}
148146
}
147+
148+
public protocol JSONRequest: Request {}
149+
150+
public extension JSONRequest {
151+
152+
public var dataParser: JSONDataParser {
153+
return JSONDataParser(readingOptions: [])
154+
}
155+
156+
}

Tests/APIKitTests/SessionTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ class SessionTests: XCTestCase {
175175
waitForExpectations(timeout: 1.0, handler: nil)
176176
}
177177

178-
struct AnotherTestRequest: Request {
178+
struct AnotherTestRequest: JSONRequest {
179179
typealias Response = Void
180180

181181
var baseURL: URL {

Tests/APIKitTests/TestComponents/TestRequest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Foundation
22
import APIKit
33

4-
struct TestRequest: Request {
4+
struct TestRequest: JSONRequest {
55
var absoluteURL: URL? {
66
let urlRequest = try? buildURLRequest()
77
return urlRequest?.url

0 commit comments

Comments
 (0)