Skip to content

Commit e62a7d9

Browse files
authored
feat(session): Simplify config API (#32)
1 parent 3db3350 commit e62a7d9

6 files changed

Lines changed: 81 additions & 93 deletions

File tree

README.md

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,7 @@ You can also use a `Session` object. [`Session`](https://pjechris.github.io/Simp
5252

5353
```swift
5454

55-
let session = Session(
56-
baseURL: URL(string: "https://github.com")!,
57-
encoder: JSONEncoder(),
58-
decoder: JSONDecoder()
59-
)
55+
let session = Session(baseURL: URL(string: "https://github.com")!)
6056

6157
try await session.response(for: .login(UserBody(username: "pjechris", password: "MyPassword")))
6258

@@ -65,9 +61,24 @@ try await session.response(for: .login(UserBody(username: "pjechris", password:
6561
A few words about Session:
6662

6763
- `baseURL` will be prepended to all call paths
68-
- You can skip encoder and decoder if you use JSON
64+
- By default JSON is used for both encoding and decoding, so you don't have to configure anything for JSON APIs
6965
- You can provide a custom `URLSession` instance if ever needed
7066

67+
### Customizing encoders/decoders
68+
69+
Encoders and decoders are configured per content type through a `SessionConfiguration`. Pass a `ContentDataCodersConfiguration` to register the coders you need:
70+
71+
```swift
72+
let session = Session(
73+
baseURL: URL(string: "https://github.com")!,
74+
configuration: SessionConfiguration(
75+
data: ContentDataCodersConfiguration()
76+
.encoding(.json, with: JSONEncoder())
77+
.decoding(.json, with: JSONDecoder())
78+
)
79+
)
80+
```
81+
7182
## Send a body
7283

7384
Request support two body types:

Sources/SimpleHTTP/ContentData/ContentDataCoderConfiguration.swift

Lines changed: 0 additions & 76 deletions
This file was deleted.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import Foundation
2+
3+
public typealias ContentDataEncodersConfiguration = [HTTPContentType: ContentDataEncoder]
4+
public typealias ContentDataDecodersConfiguration = [HTTPContentType: ContentDataDecoder]
5+
6+
/// Defines the list of encoders and decoders to use.
7+
public struct ContentDataCodersConfiguration {
8+
public var encoders: ContentDataEncodersConfiguration
9+
public var decoders: ContentDataDecodersConfiguration
10+
public let defaultType: HTTPContentType
11+
12+
public init(
13+
default: HTTPContentType,
14+
encoders: ContentDataEncodersConfiguration,
15+
decoders: ContentDataDecodersConfiguration,
16+
) {
17+
self.encoders = encoders
18+
self.decoders = decoders
19+
self.defaultType = `default`
20+
}
21+
22+
/// Creates a configuration with default coders and decoders.
23+
///
24+
/// - Note: default encoder/decoder is set to JSON
25+
public init() {
26+
self.init(
27+
default: .json,
28+
encoders: [
29+
.json: JSONEncoder(),
30+
.formURLEncoded: FormURLEncoder()
31+
],
32+
decoders: [
33+
.json: JSONDecoder()
34+
]
35+
)
36+
}
37+
}
38+
39+
extension ContentDataCodersConfiguration {
40+
/// defines a single encoder to use for encoding `contentType` requests. If an encoder was already defined it will be replaced with the new value
41+
public func encoding(_ contentType: HTTPContentType, with encoder: ContentDataEncoder) -> Self {
42+
var copy = self
43+
copy.encoders[contentType] = encoder
44+
return copy
45+
}
46+
47+
/// defines a single decoder for decoding `contentType` responses. If a decoder was already defined it will be replaced with the new value
48+
public func decoding(_ contentType: HTTPContentType, with decoder: ContentDataDecoder) -> Self {
49+
var copy = self
50+
copy.decoders[contentType] = decoder
51+
return copy
52+
}
53+
}

Sources/SimpleHTTP/Session/Session.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public class Session {
4444
public func response<Output: Decodable>(for request: Request<Output>) async throws -> Output {
4545
let result = try await dataPublisher(for: request)
4646

47-
guard let decoder = config.data.decoder[result.contentType] else {
47+
guard let decoder = config.data.decoders[result.contentType] else {
4848
throw SessionConfigurationError.missingDecoder(result.contentType)
4949
}
5050

@@ -77,10 +77,10 @@ extension Session {
7777
// FIXME: we also check body inside toURLRequest
7878
switch modifiedRequest.body {
7979
case .encodable:
80-
encoder = config.data.encoder[requestContentType]
80+
encoder = config.data.encoders[requestContentType]
8181
case .multipart, .none:
8282
// this one is supposed to never be nil
83-
encoder = config.data.encoder[config.data.defaultType]
83+
encoder = config.data.encoders[config.data.defaultType]
8484
}
8585

8686
guard let encoder else {

Sources/SimpleHTTP/Session/SessionConfiguration.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Foundation
33
/// a type defining some parameters for a `Session`
44
public struct SessionConfiguration {
55
/// data encoders/decoders configuration per content type
6-
let data: ContentDataCoderConfiguration
6+
let data: ContentDataCodersConfiguration
77
/// queue on which to decode data
88
let decodingQueue: DispatchQueue
99
/// an interceptor to apply custom behavior on the session requests/responses.
@@ -16,7 +16,7 @@ public struct SessionConfiguration {
1616
/// - Parameter decodeQueue: queue on which to decode data
1717
/// - Parameter interceptors: interceptor list to apply on the session requests/responses
1818
public init(
19-
data: ContentDataCoderConfiguration = .init(),
19+
data: ContentDataCodersConfiguration = .init(),
2020
decodingQueue: DispatchQueue = .main,
2121
interceptors: CompositeInterceptor = []) {
2222
self.data = data
@@ -26,14 +26,14 @@ public struct SessionConfiguration {
2626

2727
/// - Parameter dataError: Error type to use when having error with data
2828
public init<DataError: Error & Decodable>(
29-
data: ContentDataCoderConfiguration,
29+
data: ContentDataCodersConfiguration,
3030
decodingQueue: DispatchQueue = .main,
3131
interceptors: CompositeInterceptor = [],
3232
dataError: DataError.Type
3333
) {
3434
self.init(data: data, decodingQueue: decodingQueue, interceptors: interceptors)
35-
self.errorConverter = { [decoder=data.decoder] data, contentType in
36-
guard let decoder = decoder[contentType] else {
35+
self.errorConverter = { [decoders=data.decoders] data, contentType in
36+
guard let decoder = decoders[contentType] else {
3737
throw SessionConfigurationError.missingDecoder(contentType)
3838
}
3939
return try decoder.decode(dataError, from: data)

Tests/SimpleHTTPTests/Session/SessionTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import XCTest
33

44
class SessionAsyncTests: XCTestCase {
55
let baseURL = URL(string: "https://sessionTests.io")!
6-
let data = ContentDataCoderConfiguration(
6+
let data = ContentDataCodersConfiguration(
77
default: .json,
8-
encoder: [.json: JSONEncoder()],
9-
decoder: [.json: JSONDecoder()]
8+
encoders: [.json: JSONEncoder()],
9+
decoders: [.json: JSONDecoder()]
1010
)
1111

1212
func test_response_responseIsValid_decodedOutputIsReturned() async throws {

0 commit comments

Comments
 (0)