Skip to content

Commit

Permalink
Test case and example for using date with a propertyWrapper.
Browse files Browse the repository at this point in the history
  • Loading branch information
fabianfett committed Mar 18, 2020
1 parent 29fc2d7 commit 118e9b7
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 2 deletions.
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Add `pure-swift-json` as dependency to your `Package.swift`:

```swift
dependencies: [
.package(url: "https://github.com/fabianfett/pure-swift-json.git", .upToNextMajore(from: "0.1.0")),
.package(url: "https://github.com/fabianfett/pure-swift-json.git", .upToNextMajor(from: "0.1.0")),
],
```

Expand Down Expand Up @@ -125,7 +125,7 @@ struct MyEvent: Decodable {
guard let timestamp = MyEvent.dateFormatter.date(from: dateString) else {
let dateFormat = String(describing: MyEvent.dateFormatter.dateFormat)
throw DecodingError.dataCorruptedError(forKey: .eventTime, in: container, debugDescription:
"Expected date to be in format `\(dateFormat)`, but `\(dateFormat) does not forfill format`")
"Expected date to be in format `\(dateFormat)`, but `\(dateString) does not forfill format`")
}
self.eventTime = timestamp
}
Expand All @@ -143,6 +143,44 @@ struct MyEvent: Decodable {

You can find more information about [encoding and decoding custom types in Apple's documentation](https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types).

Of course you can use `@propertyWrapper`s to make this more elegant:

```swift
import Foundation

@propertyWrapper
struct DateStringCoding: Decodable {
var wrappedValue: Date

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
guard let date = Self.dateFormatter.date(from: dateString) else {
let dateFormat = String(describing: Self.dateFormatter.dateFormat)
throw DecodingError.dataCorruptedError(in: container, debugDescription:
"Expected date to be in format `\(dateFormat)`, but `\(dateString) does not forfill format`")
}
self.wrappedValue = date
}

private static let dateFormatter: DateFormatter = Self.createDateFormatter()
private static func createDateFormatter() -> DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}
}

struct MyEvent: Decodable {
@DateStringCoding
var eventTime: Date
}
```

Checkout the a full example in the test file [DateCodingTests](https://github.com/fabianfett/pure-swift-json/blob/master/Tests/JSONCodingTests/DateCodingTests.swift).

### UTF-16 and UTF-32

If your input is [UTF-16](https://en.wikipedia.org/wiki/UTF-16) or [UTF-32](https://en.wikipedia.org/wiki/UTF-32) encoded, you can easily convert it to UTF-8:
Expand Down Expand Up @@ -173,3 +211,4 @@ Focus areas for the time being:
- [@weissi](https://github.com/weissi) thanks for answering all my questions and for opening tickets [SR-12125](https://bugs.swift.org/browse/SR-12125) and [SR-12126](https://bugs.swift.org/browse/SR-12126)
- [@dinhhungle](https://github.com/dinhhungle) thanks for your quality assurance. It helped a lot!
- [@Ro-M](https://github.com/Ro-M) thanks for checking my README.md
- [@Trzyipolkostkicukru](https://github.com/Trzyipolkostkicukru) thanks for your advice on `@propertyWrappers` and for finding typos.
83 changes: 83 additions & 0 deletions Tests/JSONCodingTests/DateCodingTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import XCTest
@testable import PureSwiftJSONCoding

class DateCodingTests: XCTestCase {

@propertyWrapper
struct DateStringCoding: Codable {
var wrappedValue: Date

init(date: Date) {
self.wrappedValue = date
}

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
guard let date = Self.dateFormatter.date(from: dateString) else {
let dateFormat = String(describing: Self.dateFormatter.dateFormat)
throw DecodingError.dataCorruptedError(in: container, debugDescription:
"Expected date to be in format `\(dateFormat)`, but `\(dateString) does not forfill format`")
}
self.wrappedValue = date
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
let dateString = Self.dateFormatter.string(from: self.wrappedValue)
try container.encode(dateString)
}

private static let dateFormatter: DateFormatter = Self.createDateFormatter()
private static func createDateFormatter() -> DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}
}

struct MyEvent: Codable {
@DateStringCoding
var eventTime: Date

init(eventTime: Date) {
self._eventTime = DateStringCoding(date: eventTime)
}
}

func testDecodeDatePropertyWrapper() {
do {
let dateString = "2020-03-18T13:11:10.000Z"
let json = #"{"eventTime": "\#(dateString)"}"#
let result = try PureSwiftJSONCoding.JSONDecoder().decode(MyEvent.self, from: [UInt8](json.utf8))

let components = DateComponents(
calendar: Calendar(identifier: .gregorian), timeZone: TimeZone(secondsFromGMT: 0),
year: 2020, month: 03, day: 18, hour: 13, minute: 11, second: 10, nanosecond: 0)
XCTAssertEqual(result.eventTime, components.date)
}
catch {
XCTFail("Unexpected error: \(error)")
}
}

func testEncodeDatePropertyWrapper() {
do {
let dateString = "2020-03-18T13:11:10.000Z"
let json = #"{"eventTime":"\#(dateString)"}"#

let components = DateComponents(
calendar: Calendar(identifier: .gregorian), timeZone: TimeZone(secondsFromGMT: 0),
year: 2020, month: 03, day: 18, hour: 13, minute: 11, second: 10, nanosecond: 0)

let event = MyEvent(eventTime: components.date!)
let bytes = try PureSwiftJSONCoding.JSONEncoder().encode(event)
XCTAssertEqual(String(decoding: bytes, as: Unicode.UTF8.self), json)
}
catch {
XCTFail("Unexpected error: \(error)")
}
}
}

0 comments on commit 118e9b7

Please # to comment.