Can't decode date in Swift 4

It looks like one of your birthdates:

"birthdate": "2009-05-06T18:56:38.367",

contains milliseconds. Your date format string:

dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"

Isn't able to handle this. You can either change the birthdate field in the incoming JSON, or change your dateFormat string to this:

dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"

Note that adding .SSS appears to break the formatter for non-millisecond dates. I'd recommend cutting out the milliseconds server-side.


Original answer below:

I've just tried this in a Playground, and it appears to work as expected:

class Person : Codable {
    var birthdate: Date = Date()
    var firstName: String = ""
    var lastName: String = ""

    enum CodingKeys : String, CodingKey {
        case birthdate
        case firstName = "first_name"
        case lastName = "last_name"
    }
}

var json: String = """
[
{
    "birthdate": "2009-05-06T18:56:38",
    "first_name": "John",
    "last_name": "Smith"
}
]
"""

let decoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
decoder.dateDecodingStrategy = .formatted(dateFormatter)

let people = try! decoder.decode(Array<Person>.self, from: json.data(using: .utf8, allowLossyConversion: false)!)

Where people is now this:

{birthdate "May 6, 2009 at 6:56 PM", firstName "John", lastName "Smith"}

Either there's something subtly different between my code and yours, or there may be a different set of example data needed.


You just forgot to add milliseconds to your date format.

Change this line:
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss".
With this:
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"


If you decode JSON with dates in multiple parts of your code I recommend doing a custom class to adapt the decoding to what you need in this case: decoding Date.


The implementation will be something like this:

/**Custom decoder for dates*/
class DecoderDates: JSONDecoder {

    override func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable {
        let decoder = JSONDecoder()
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
        decoder.dateDecodingStrategy = .formatted(dateFormatter)
        return try decoder.decode(T.self, from: data)
    }

}

Example use:

DecoderDates().decode(Codable.self, from: data)

Hope this helps someone.