First, don't name your custom type Date
- it conflicts with Date
of the standard library. I renamed it to DateInfo
:
struct DateInfo: Decodable {
var createdAt: Date
var updatedAt: Date
}
Then, to decode the dates into Date
, you need to set the dateDecodingStrategy
on the JSONDecoder
to choose the date format. In your example, this is a standard iso8601 format, but with fractional seconds, and (thanks to @LeoDabus) which the built-in decoding strategy .iso8601
doesn't support:
So, without fractional seconds, it would have been done like so:
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let dateInfo = try decoder.decode(DateInfo.self, from: jsonData)
But with fractional seconds, it requires some manual work to decode and format using ISO8601DateFormatter
. As a matter of convenience, we could create extensions with the custom formatter and the date decoding strategy:
extension Formatter {
static var customISO8601DateFormatter: ISO8601DateFormatter = {
let formatter = ISO8601DateFormatter()
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return formatter
}()
}
extension JSONDecoder.DateDecodingStrategy {
static var iso8601WithFractionalSeconds = custom { decoder in
let dateStr = try decoder.singleValueContainer().decode(String.self)
let customIsoFormatter = Formatter.customISO8601DateFormatter
if let date = customIsoFormatter.date(from: dateStr) {
return date
}
throw DecodingError.dataCorrupted(
DecodingError.Context(codingPath: decoder.codingPath,
debugDescription: "Invalid date"))
}
}
and the usage is similar to built-in strategy, except using a custom one:
decoder.dateDecodingStrategy = .iso8601WithFractionalSeconds