Search code examples
iosswiftjsondecoderdateformatter

Unable to convert date string to Date format received through json response via JSONDecoder


I am receiving the date in 'M/d/yy' format (eg. 8/13/21), and I am using JSONDecoder to decode the data to expected model.

struct Model: Codable {
    let submodel: SubModel
}

struct SubModel: Codable {
    let item1, item2: [Date: Int] // I used [String: Int] and it works but I don't want to write an
}                                 // additional method to do something that if possible can be
                                  // done easily

I am able to get it to work if I use [String: Int] format but its not working if I use [Date: Int]. I used the dateEncodingStrategy of the decoder to set the custom strategy.

makeGetRequest(url: url) { data in
    do {
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .custom({ decoder in
            let formatter = DateFormatter()
            let container = try decoder.singleValueContainer()
            let dateString = try container.decode(String.self)
            formatter.dateFormat = "M/d/yy"
            if let date = formatter.date(from: dateString) {
                return date
            }
            throw DecodingError.dataCorruptedError(in: container,
                                                   debugDescription: "Cannot decode date string \(dateString)")
        })
        let responseData = try decoder.decode(MainModel.self, from: data)
        success(responseData)
    } catch (let error) {
        debugPrint("Error decoding data: \(error.localizedDescription)")
        failure(error.localizedDescription)
    }
}

The sample response I get from the server in json format is

{
    "submodel": {
        "item1": {
            "8/7/21": 1,
            "8/8/21": 2,
            "8/9/21": 3
        },
        "item2": {
            "8/7/21": 11,
            "8/8/21": 12,
            "8/9/21": 13
        }
    }
}

I am getting the error "The data couldn’t be read because it isn’t in the correct format."

Any help is appreciated


Solution

  • I don't think you have any alternative other than creating a custom decoder for your SubModel structure:

    extension SubModel {
        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            let dictionary1 = try container.decode([String:Int].self, forKey: .item1)
            let dictionary2 = try container.decode([String:Int].self, forKey: .item2)
            let formatter = DateFormatter()
            formatter.locale = .init(identifier: "en_US_POSIX")
            formatter.dateFormat = "M/d/yy"
            item1 = dictionary1.reduce(into: [:], { result, kv in
                guard let date = formatter.date(from: kv.key) else { return }
                result[date] = kv.value
            })
            item2 = dictionary2.reduce(into: [:], { result, kv in
                guard let date = formatter.date(from: kv.key) else { return }
                result[date] = kv.value
            })
        }
    }