Skip to content

Commit

Permalink
feat: added convenience decoding strategies
Browse files Browse the repository at this point in the history
  • Loading branch information
philprime committed Dec 6, 2023
1 parent 13c1044 commit 85556c3
Show file tree
Hide file tree
Showing 12 changed files with 114 additions and 1 deletion.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,51 @@ struct Request: Postie.Request {
}
```

#### Response parsing strategies

As the response body might differ depending on various factors, you can control the parsing using "decoding strategies".

By default if you use `ResponseBody` to parse the response body, it will use the `DefaultBodyStrategy` (which expects an HTTP status code 2XX or 3XX).

The same applies to the `ResponseErrorBody` to parse the response body for a status code of 400 or above, which is using the `DefaultErrorBodyStrategy`.

For your convenience we added a couple of convenience strategies in `ResponseBody` and `ResponseErrorBody`, you can use with the `ResponseBodyWrapper` and `ResponseErrorBodyWrapper`.

If you want to implement a custom decoding strategy, all you need to do is define a struct implementing the protocol `ResponseBodyDecodingStrategy` or `ResponseErrorBodyDecodingStrategy`.

**Example:***

```swift
struct CustomBodyDecodingStrategy {
public static func allowsEmptyContent(for _: Int) -> Bool {
return false
}

public static func validate(statusCode: Int) -> Bool {
// e.g. only decode if the status code is 999
statusCode == 999
}
}

struct Request: Postie.Request {
struct Response: Decodable {
struct CreatedResponseBody: JSONDecodable {
...
}

@ResponseBody<CreatedResponseBody>.Status201 var createdBody: CreatedResponseBody

struct CustomResponseBody: JSONDecodable {
...
}

@ResponseBodyWrapper<CustomResponseBody, CustomBodyDecodingStrategy> var customBody
}
}
```

Note: Due to technical limitations of the `Codable` protocol in Swift, it is currently not possible to have a non-static/dynamic decoding strategy.

#### Response headers

Use the property wrapper `@ResponseHeader<Strategy>` inside the response type.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
public extension ResponseBody {

typealias OptionalContent = ResponseBodyWrapper<Body, OptionalContentStrategy>

typealias Status200 = ResponseBodyWrapper<Body, ValidateStatus200BodyStrategy>
typealias Status303 = ResponseBodyWrapper<Body, ValidateStatus303BodyStrategy>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
public extension ResponseErrorBody {
typealias Status400 = ResponseErrorBodyWrapper<Body, ValidateStatus400BodyStrategy>
typealias Status401 = ResponseErrorBodyWrapper<Body, ValidateStatus401BodyStrategy>
typealias Status403 = ResponseErrorBodyWrapper<Body, ValidateStatus403BodyStrategy>
typealias Status404 = ResponseErrorBodyWrapper<Body, ValidateStatus404BodyStrategy>
typealias Status410 = ResponseErrorBodyWrapper<Body, ValidateStatus410BodyStrategy>
typealias Status422 = ResponseErrorBodyWrapper<Body, ValidateStatus422BodyStrategy>
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
public struct ValidateStatus200BodyStrategy: ResponseBodyDecodingStrategy {
public static func allowsEmptyContent(for _: Int) -> Bool {
return false
}

public static func validate(statusCode: Int) -> Bool {
statusCode == HTTPStatusCode.ok.rawValue
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
public struct ValidateStatus201BodyStrategy: ResponseBodyDecodingStrategy {
public static func allowsEmptyContent(for _: Int) -> Bool {
return false
}

public static func validate(statusCode: Int) -> Bool {
statusCode == HTTPStatusCode.created.rawValue
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
public struct ValidateStatus303BodyStrategy: ResponseBodyDecodingStrategy {
public static func allowsEmptyContent(for _: Int) -> Bool {
return true
}

public static func validate(statusCode: Int) -> Bool {
statusCode == HTTPStatusCode.seeOther.rawValue
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public struct ValidateStatus400BodyStrategy: ResponseErrorBodyDecodingStrategy {
public static func isError(statusCode: Int) -> Bool {
statusCode == HTTPStatusCode.badRequest.rawValue
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public struct ValidateStatus401BodyStrategy: ResponseErrorBodyDecodingStrategy {
public static func isError(statusCode: Int) -> Bool {
statusCode == HTTPStatusCode.unauthorized.rawValue
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public struct ValidateStatus403BodyStrategy: ResponseErrorBodyDecodingStrategy {
public static func isError(statusCode: Int) -> Bool {
statusCode == HTTPStatusCode.forbidden.rawValue
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public struct ValidateStatus404BodyStrategy: ResponseErrorBodyDecodingStrategy {
public static func isError(statusCode: Int) -> Bool {
statusCode == HTTPStatusCode.notFound.rawValue
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public struct ValidateStatus410BodyStrategy: ResponseErrorBodyDecodingStrategy {
public static func isError(statusCode: Int) -> Bool {
statusCode == HTTPStatusCode.gone.rawValue
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public struct ValidateStatus422BodyStrategy: ResponseErrorBodyDecodingStrategy {
public static func isError(statusCode: Int) -> Bool {
statusCode == HTTPStatusCode.unprocessableEntity.rawValue
}
}

0 comments on commit 85556c3

Please sign in to comment.