Skip to content

Commit 40b05b9

Browse files
authored
Merge pull request #312 from ishkawa/feature/decodable-data-parser
Add `NonSerializedJSONDataParser` for `Foundation.Decodable`
2 parents b014089 + 02727ca commit 40b05b9

File tree

4 files changed

+68
-26
lines changed

4 files changed

+68
-26
lines changed

APIKit.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@
5050
C5B144D828D8D7DC00E30ECD /* ConcurrencyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5B144D728D8D7DC00E30ECD /* ConcurrencyTests.swift */; };
5151
C5F9A3482903E138000CB6C4 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F9A3472903E138000CB6C4 /* Request.swift */; };
5252
C5F9A34A2903E147000CB6C4 /* JSONRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F9A3492903E147000CB6C4 /* JSONRequest.swift */; };
53+
C5F9A34C2905073D000CB6C4 /* NonSerializedJSONDataParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F9A34B2905073D000CB6C4 /* NonSerializedJSONDataParser.swift */; };
54+
C5F9A34E29050A96000CB6C4 /* NonSerializedJSONDataParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5F9A34D29050A96000CB6C4 /* NonSerializedJSONDataParserTests.swift */; };
5355
C5FF1DC128A80FFD0059573D /* test.json in Resources */ = {isa = PBXBuildFile; fileRef = C5FF1DC028A80FFD0059573D /* test.json */; };
5456
C5FF1DCF28A835600059573D /* QueryParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5FF1DCD28A835600059573D /* QueryParameters.swift */; };
5557
C5FF1DD028A835600059573D /* URLEncodedQueryParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5FF1DCE28A835600059573D /* URLEncodedQueryParameters.swift */; };
@@ -137,6 +139,8 @@
137139
C5B144D728D8D7DC00E30ECD /* ConcurrencyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurrencyTests.swift; sourceTree = "<group>"; };
138140
C5F9A3472903E138000CB6C4 /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = "<group>"; };
139141
C5F9A3492903E147000CB6C4 /* JSONRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONRequest.swift; sourceTree = "<group>"; };
142+
C5F9A34B2905073D000CB6C4 /* NonSerializedJSONDataParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonSerializedJSONDataParser.swift; sourceTree = "<group>"; };
143+
C5F9A34D29050A96000CB6C4 /* NonSerializedJSONDataParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonSerializedJSONDataParserTests.swift; sourceTree = "<group>"; };
140144
C5FF1DC028A80FFD0059573D /* test.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = test.json; sourceTree = "<group>"; };
141145
C5FF1DCD28A835600059573D /* QueryParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryParameters.swift; sourceTree = "<group>"; };
142146
C5FF1DCE28A835600059573D /* URLEncodedQueryParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLEncodedQueryParameters.swift; sourceTree = "<group>"; };
@@ -282,6 +286,7 @@
282286
children = (
283287
7F698E411D9D680C00F1561D /* FormURLEncodedDataParserTests.swift */,
284288
7F698E421D9D680C00F1561D /* JSONDataParserTests.swift */,
289+
C5F9A34D29050A96000CB6C4 /* NonSerializedJSONDataParserTests.swift */,
285290
ECA831491DE4DEBE004EB1B5 /* ProtobufDataParserTests.swift */,
286291
7F698E431D9D680C00F1561D /* StringDataParserTests.swift */,
287292
);
@@ -373,6 +378,7 @@
373378
7F7048E41D9D8A08003C99F6 /* DataParser.swift */,
374379
7F7048E51D9D8A08003C99F6 /* FormURLEncodedDataParser.swift */,
375380
7F7048E61D9D8A08003C99F6 /* JSONDataParser.swift */,
381+
C5F9A34B2905073D000CB6C4 /* NonSerializedJSONDataParser.swift */,
376382
ECA831471DE4DDBF004EB1B5 /* ProtobufDataParser.swift */,
377383
7F7048E71D9D8A08003C99F6 /* StringDataParser.swift */,
378384
);
@@ -568,6 +574,7 @@
568574
7F7048F11D9D8A12003C99F6 /* SessionTaskError.swift in Sources */,
569575
ECA831481DE4DDBF004EB1B5 /* ProtobufDataParser.swift in Sources */,
570576
7F7048F31D9D8A1F003C99F6 /* URLEncodedSerialization.swift in Sources */,
577+
C5F9A34C2905073D000CB6C4 /* NonSerializedJSONDataParser.swift in Sources */,
571578
7F7048D71D9D89F2003C99F6 /* URLSessionAdapter.swift in Sources */,
572579
0969AE0F259DEC6D00C498AF /* Combine.swift in Sources */,
573580
7F7048EB1D9D8A08003C99F6 /* StringDataParser.swift in Sources */,
@@ -589,6 +596,7 @@
589596
C5B144D828D8D7DC00E30ECD /* ConcurrencyTests.swift in Sources */,
590597
7F698E601D9D680C00F1561D /* TestSessionTask.swift in Sources */,
591598
0973EE35259E2DDC00879BA2 /* CombineTests.swift in Sources */,
599+
C5F9A34E29050A96000CB6C4 /* NonSerializedJSONDataParserTests.swift in Sources */,
592600
C5FF1DD328A835680059573D /* URLEncodedQueryParametersTests.swift in Sources */,
593601
7FA1690D1D9D8C80006C982B /* HTTPStub.swift in Sources */,
594602
7F698E5A1D9D680C00F1561D /* URLSessionAdapterTests.swift in Sources */,

Demo.playground/Contents.swift

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,37 @@ import APIKit
55
PlaygroundPage.current.needsIndefiniteExecution = true
66

77
//: Step 1: Define request protocol
8-
protocol GitHubRequest: JSONRequest {}
8+
protocol GitHubRequest: Request {}
99

1010
extension GitHubRequest {
1111
var baseURL: URL {
1212
return URL(string: "https://api.github.com")!
1313
}
14+
15+
var dataParser: NonSerializedJSONDataParser {
16+
return NonSerializedJSONDataParser()
17+
}
1418
}
1519

1620
//: Step 2: Create model object
17-
struct RateLimit {
21+
struct RateLimit: Decodable {
1822
let count: Int
1923
let resetDate: Date
2024

21-
init?(dictionary: [String: AnyObject]) {
22-
guard let count = dictionary["rate"]?["limit"] as? Int else {
23-
return nil
24-
}
25-
26-
guard let resetDateString = dictionary["rate"]?["reset"] as? TimeInterval else {
27-
return nil
28-
}
25+
enum CodingKeys: String, CodingKey {
26+
case rate
27+
}
28+
enum RateCodingKeys: String, CodingKey {
29+
case limit
30+
case reset
31+
}
2932

30-
self.count = count
31-
self.resetDate = Date(timeIntervalSince1970: resetDateString)
33+
init(from decoder: Decoder) throws {
34+
let container = try decoder.container(keyedBy: CodingKeys.self)
35+
let rateContainer = try container.nestedContainer(keyedBy: RateCodingKeys.self, forKey: .rate)
36+
self.count = try rateContainer.decode(Int.self, forKey: .limit)
37+
let resetTimeInterval = try rateContainer.decode(TimeInterval.self, forKey: .reset)
38+
self.resetDate = Date(timeIntervalSince1970: resetTimeInterval)
3239
}
3340
}
3441

@@ -37,21 +44,11 @@ struct RateLimit {
3744
struct GetRateLimitRequest: GitHubRequest {
3845
typealias Response = RateLimit
3946

40-
var method: HTTPMethod {
41-
return .get
42-
}
43-
44-
var path: String {
45-
return "/rate_limit"
46-
}
47-
48-
func response(from object: Any, urlResponse: HTTPURLResponse) throws -> Response {
49-
guard let dictionary = object as? [String: AnyObject],
50-
let rateLimit = RateLimit(dictionary: dictionary) else {
51-
throw ResponseError.unexpectedObject(object)
52-
}
47+
let method: HTTPMethod = .get
48+
let path: String = "/rate_limit"
5349

54-
return rateLimit
50+
func response(from object: Data, urlResponse: HTTPURLResponse) throws -> Response {
51+
return try JSONDecoder().decode(Response.self, from: object)
5552
}
5653
}
5754

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Foundation
2+
3+
/// `NonSerializedJSONDataParser` response Data data.
4+
public class NonSerializedJSONDataParser: DataParser {
5+
/// Returns `NonSerializedJSONDataParser`.
6+
public init() {}
7+
8+
// MARK: - DataParser
9+
10+
/// Value for `Accept` header field of HTTP request.
11+
public var contentType: String? {
12+
return "application/json"
13+
}
14+
15+
/// Return `Data` that expresses structure of Data response.
16+
public func parse(data: Data) throws -> Data {
17+
return data
18+
}
19+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import XCTest
2+
import APIKit
3+
import XCTest
4+
5+
class NonSerializedJSONDataParserTests: XCTestCase {
6+
func testContentType() {
7+
let parser = NonSerializedJSONDataParser()
8+
XCTAssertEqual(parser.contentType, "application/json")
9+
}
10+
11+
func testJSONSuccess() throws {
12+
let data = try XCTUnwrap("data".data(using: .utf8))
13+
let parser = NonSerializedJSONDataParser()
14+
15+
let object = try parser.parse(data: data)
16+
XCTAssertEqual(object, data)
17+
}
18+
}

0 commit comments

Comments
 (0)