WebHook does not use valid ISO8601 Date

While I am on a Vapor based back-end in Swift, I am noticing that the Webhook is not sending a valid ISO8601 Date and Swift’s JSONDecoder is unable to decode them using the iso8601 decoding strategy.

Dates are being sent in the following style: 2018-02-22T13:42:47.483Z

Take the following Swift / Xcode playground as an example:

// define the json to test
let json = "{\"published_at\":\"2018-02-22T13:42:47.483Z\"}"

// Dummy Swift Model
struct MyModel: Codable {
    var publishedAt: Date
    
    enum CodingKeys: String, CodingKey {
        case publishedAt = "published_at"
    }
}

// Swift 4 JSON Decoder
var decoder = JSONDecoder()
if #available(OSX 10.12, *) {
    // Decode dates as ISO8601
    decoder.dateDecodingStrategy = .iso8601
}

// Get a Data representation of the JSON string
if let data = json.data(using: .utf8) {
    // decode the JSON data using the ISO8601 strategy
    do {
        // this call can throw
        let foo = try decoder.decode(MyModel.self, from: data)
        // dump foo if we successfully decoded the json data
        print("Success!")
        dump(foo)
    } catch {
        print("Failure :'(")
        dump(error)
    }
}

This will actually fail with the following error:

Failure :'(
▿ Swift.DecodingError.dataCorrupted
  ▿ dataCorrupted: Swift.DecodingError.Context
    ▿ codingPath: 1 element
      - __lldb_expr_113.MyModel.CodingKeys.publishedAt
    - debugDescription: "Expected date string to be ISO8601-formatted."
    - underlyingError: nil

Of course I can create my own custom date formatter to handle this, but as this is documented as being an ISO8601 date, this should work.

To answer my own question… Apple’s default ISO8601 JSON decoding strategy does not seem to decode all iso8601 styles :confused: (go figure…)

This does work:

extension DateFormatter {
    static let iso8601Full: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter
    }()
}

// Swift 4 JSON Decoder
var decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(DateFormatter.iso8601Full)

That sure looks like a correctly formatted ISO date string to me. I read the Wikipedia article on that format just now and it looks to me like several of the examples there. Can you say why you think that format is wrong?

Searching for Swift 4 ISO date problems brings up this link which explains that Swift 4 JSON decoder appears to not parse all the valid formats in ISO 8601 and gives a work-around:

1 Like