Skip to content

Commit

Permalink
Merge pull request #4 from juri/decoder-support
Browse files Browse the repository at this point in the history
Decoder support
  • Loading branch information
juri committed Jul 11, 2023
2 parents f790334 + de2c33d commit 0612c43
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ print(date.timeIntervalSinceReferenceDate)
// output: 710590440.0
```

There's a helper function you can use with Foundation's `JSONDecoder`:

```swift
import Parse3339

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom(Parse3339.parseFromDecoder(_:))
```

For `Package.swift` snippets and documentation, visit the [Swift Package Index page](https://swiftpackageindex.com/juri/Parse3339).

## Speed and memory usage
Expand Down
4 changes: 4 additions & 0 deletions Sources/Parse3339/Documentation.docc/Documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@ print(date.timeIntervalSinceReferenceDate)
### Parser output

- ``Parts``

### Codable support

- ``parseFromDecoder(_:)``
30 changes: 30 additions & 0 deletions Sources/Parse3339/Parse3339.swift
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,36 @@ public func parse(_ seq: some Sequence<UInt8>) -> Parts? {
return nil
}

// MARK: JSONDecoder support

/// Helper function for using Parse3339 with Codable types.
///
/// Use `parseFromDecoder(_:)` with a custom date decoding strategy. The mechanism depends on
/// the `TopLevelDecoder` implementation. With `JSONDecoder` you can use the `dateDecodingStrategy`
/// property with a `custom` value like this:
///
/// ```swift
/// let decoder = JSONDecoder()
/// decoder.dateDecodingStrategy = .custom(Parse3339.parseFromDecoder(_:))
/// ```
///
/// `parseFromDecoder` first decodes a `String` and then parses the string. For formats other than JSON you may get
/// better performance by using an implementation that feeds bytes to ``parse(_:)-9on3x``.
public func parseFromDecoder(_ decoder: some Decoder) throws -> Date {
let container = try decoder.singleValueContainer()
let str = try container.decode(String.self)
guard let parsed = parse(str) else {
throw DecodingError.typeMismatch(
Date.self,
DecodingError.Context(
codingPath: [],
debugDescription: "The string '\(str)' could not be parsed as a date"
)
)
}
return parsed.date
}

// MARK: - Private

private enum ZoneDirection {
Expand Down
40 changes: 40 additions & 0 deletions Tests/Parse3339Tests/Parse3339Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,46 @@ final class Parse3339Tests: XCTestCase {
}
}
}

// MARK: Decodable

func testDecodable() throws {
struct Payload: Codable {
let message: String
let date: Date
}

let dateComponents = DateComponents(
timeZone: TimeZone(identifier: "Europe/Helsinki"),
year: 2023,
month: 7,
day: 11,
hour: 8,
minute: 49,
second: 0
)
let date = Calendar(identifier: .gregorian).date(from: dateComponents)!
let payload = Payload(message: "hello world", date: date)

let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601

let json = try encoder.encode(payload)

var didCallParse = false
func wrappedParse(_ decoder: any Decoder) throws -> Date {
didCallParse = true
return try parseFromDecoder(decoder)
}

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom(wrappedParse(_:))
let decoded = try decoder.decode(Payload.self, from: json)

XCTAssertTrue(didCallParse)
XCTAssertEqual(decoded.message, "hello world")
XCTAssertEqual(decoded.date, date)
}
}

let isoFormatter: ISO8601DateFormatter = {
Expand Down

0 comments on commit 0612c43

Please sign in to comment.