|
| 1 | +## Defining a ResponseType |
| 2 | + |
| 3 | +### `Response` (associatedtype, required) |
| 4 | + |
| 5 | +The `Response` is an associated type which defines the response from the server. Note that this is just type information which helpers, such as the built-in `URLSession` extensions, can use to know how to handle particular types. For instance, if this type conforms to `Decodable`, then a JSON decoder is used on the data coming from the server. If it's typealiased to `Void`, then the extension can know to ignore the response. If it's `Data`, then it can deliver the response data unmodified. |
| 6 | + |
| 7 | +### `ErrorResponse` (associatedtype, optional, defaults to `EmptyResponse`) |
| 8 | + |
| 9 | +An `ErrorResponse` type can be associated to define what value conforming to `Decodable` to use when parsing an error response from the server. This can be useful if your server returns a different JSON structure when there's an error versus a success. Often in a project, this can be defined globally and `typealias` can be used to associate this global type on all `RequestType`s. |
| 10 | + |
| 11 | +### `Body` (associatedtype, optional, defaults to `EmptyResponse`) |
| 12 | + |
| 13 | +When POST-ing JSON to your server, a `Body` conforming to `Encodable` can be associated. This value will be encoded as JSON into the body of the HTTP request. |
| 14 | + |
| 15 | +### `PathComponents` (associatedtype, defaults to `Void`) |
| 16 | + |
| 17 | +If a `PathComponents` type is associated, properties of that type can be utilized in the `path` of the `Endpoint` using a path string interpolation syntax: |
| 18 | + |
| 19 | +```Swift |
| 20 | +struct DeleteRequest: RequestType { |
| 21 | + static let endpoint: Endpoint<DeleteRequest> = Endpoint( |
| 22 | + method: .delete, |
| 23 | + path: "calendar/v3/calendars/\(path: \.calendarId)/events\(path: \.eventId)" |
| 24 | + ) |
| 25 | + |
| 26 | + typealias Response = Void |
| 27 | + |
| 28 | + struct PathComponents { |
| 29 | + let calendarId: String |
| 30 | + let eventId: String |
| 31 | + } |
| 32 | + |
| 33 | + let pathComponents: PathComponents |
| 34 | +} |
| 35 | +``` |
| 36 | + |
| 37 | +### `Parameters` (associatedtype, defaults to `Void`) |
| 38 | + |
| 39 | +A `Parameters` type, in a similar way to `PathComponents`, holds properties that can be referenced in the `Endpoint` as `Parameter<Parameters>` in order to define form parameters used in the body or query parameters attached to the URL. The enum type is defined as: |
| 40 | + |
| 41 | +```Swift |
| 42 | +public enum Parameter<T> { |
| 43 | + case form(String, path: PartialKeyPath<T>) |
| 44 | + case formValue(String, value: PathRepresentable) |
| 45 | + case query(String, path: PartialKeyPath<T>) |
| 46 | + case queryValue(String, value: PathRepresentable) |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +With this enum, either hard-coded values can be injected into the `Endpoint` (with `.formValue(_:value:)` or `.queryValue(_:value:)`) or key paths can define which reference properties in the `Parameters` associated type to define a form or query parameter that is needed at the time of the request. |
| 51 | + |
| 52 | +### `HeaderValues` (associatedtype, defaults to `Void`) |
| 53 | + |
| 54 | +Custom headers can be included in your `Endpoint` definition by associating a type with `HeaderValues` in your `RequestType`. These properties can be referenced by key paths in the `Endpoint` definition: |
| 55 | + |
| 56 | +```Swift |
| 57 | +static let endpoint: Endpoint<UserRequest> = Endpoint( |
| 58 | + method: .get, |
| 59 | + path: "/request", |
| 60 | + headers: [ |
| 61 | + "X-TYPE": HeaderField.field(path: \UserRequest.HeaderValues.type), |
| 62 | + "X-VALUE": .fieldValue(value: "value"), |
| 63 | + .keepAlive: .fieldValue(value: "timeout=5, max=1000") |
| 64 | + ] |
| 65 | +) |
| 66 | +``` |
| 67 | + |
| 68 | +Custom keys in the headers dictionary can be defined ad-hoc using a String, or by extending an encapsulating type `Headers`. Basic named headers, such as `.keepAlive`, `.accept`, etc., are already defined as part of the library. |
| 69 | + |
| 70 | +### `BodyEncoder` (associatedtype, defaults to `JSONEncoder`) |
| 71 | + |
| 72 | +This, coupled with the `bodyEncoder` property, can define custom encoders for the associated `Body` type when turning it into `Data` attached to the request. For instance, this can be customizations of the date encoding strategy or even completely different encoders for XML or other data formats. |
| 73 | + |
| 74 | +### `ResponseDecoder` (associatedtype, defaults to `JSONEncoder`) |
| 75 | + |
| 76 | +Similar to custom body encoding, the `ResponseDecoder` with the `responseDecoder` property can customize the decoder used for parsing responses from the server. |
| 77 | + |
| 78 | +### `ErrorDecoder` (associatedtype, defaults to `JSONDecoder`) |
| 79 | + |
| 80 | +Similar to `ResponseDecoder`, this allows customization of the decoder used when errors are encountered and parsed using the `ErrorResponse` type. |
| 81 | + |
| 82 | +### `endpoint` (static var, required) |
| 83 | + |
| 84 | +The `Endpoint` static var defines how all the pieces defined in the `RequestType` go together. When creating a `RequestType`, it's usually the last step, since it requires all the properties of the `RequestType` defined in order to put them together. |
| 85 | + |
| 86 | +An `Endpoint` is generic type with the type parameter conforming to `RequestType`, or equivalently `Self` since the static let is defined as part of the `RequestType` protocol. |
0 commit comments