Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't decode date in Swift 4

I'm currently trying to learn Swift and haven't gotten very far yet, so forgive me if this is an easy problem; I've been working on it for hours now and haven't been able to figure it out.

I have a Codable class called Person. On this class I have a Date property called birthdate. So it looks like this:

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"
    }
}

And I'm trying to decode my JSON:

[
    {
        "address": "302 N. 5th St.",
        "base_64_image": null,
        "birthdate": "2009-05-06T18:56:38.367",
        "created": "2017-11-21T16:21:13",
        "emergency_contact": "",
        "emergency_contact_number": null,
        "father_cell_number": null,
        "father_home_number": null,
        "father_name": null,
        "first_name": "John",
        "gender": 1,
        "id": "d92fac59-66b9-49a5-9446-005babed617a",
        "image_uri": null,
        "is_inactive": false,
        "last_name": "Smith",
        "mother_cell_number": "1234567890",
        "mother_home_number": "",
        "mother_name": "His Mother",
        "nickname": null,
        "tenant_id": "9518352f-4855-4699-b0da-ecdc06470342",
        "updated": "2018-01-20T02:11:45.9025023"
    }
]

like this:

// Fetch the data from the URL.
let headers: HTTPHeaders = [
    "Accept": "application/json"
]

Alamofire.request(url, headers: headers).responseJSON { response in
    if let data = response.data {
        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: data)
    }
}

However, I always get the same error:

Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.dataCorrupted(Swift.DecodingError.Context(codingPath: [Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 47", intValue: Optional(47)), App.Person.CodingKeys.birthdate], debugDescription: "Date string does not match format expected by formatter.", underlyingError: nil))

(The "Index 47" obviously isn't accurate, since that's for my live [and private] data).

If I take the birthdate property off the Person class everything works as expected.

I've been Googling and trying new things for several hours, and still can't get it to work no matter what I try. Can anyone here help me out?

like image 260
Shawn Beachy Avatar asked Mar 18 '18 00:03

Shawn Beachy


2 Answers

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.

like image 117
Zundo Avatar answered Sep 19 '22 12:09

Zundo


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"

like image 21
arturdev Avatar answered Sep 18 '22 12:09

arturdev