2

I'm trying to decode the following JSON string using Codables in Swift.

let json = """
{
    "todaysProvision": 235.50,
    "referenceDate": "2019-01-30",
    "lastSevenDays": {
        "2019-02-12": 235.20,
        "2019-02-11": 235.20,
        "2019-02-10": 235.20,
        "2019-02-09": 235.20,
        "2019-02-08": 235.20,
        "2019-02-07": 235.20,
        "2019-02-06": 235.20,
    }
}
"""

My current struct for decoding looks like this.

struct ProvisionInfo: Codable {

    let todaysProvision: Double
    let referenceDate: Date

}

Using JSONDecoder with dateDecodingStrategy works like a charm for the key referenceDate:

let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(df)

let decoded = try decoder.decode(ProvisionInfo.self, from: json.data(using: .utf8)!)

// decoded.todaysProvision -> 235.5
// decoded.referenceDate -> "Jan 30, 2019 at 12:00 AM"

For the JSON dictionary lastSevenDays I thought, I could simply change my struct to

struct ProvisionInfo: Codable {

    let todaysProvision: Double
    let referenceDate: Date
    let lastSevenDays: [Date: Double]

}

But with that i got decoding errors

Playground execution terminated: An error was thrown and was not caught:
▿ DecodingError
  ▿ typeMismatch : 2 elements
    - .0 : Swift.Array<Any>
    ▿ .1 : Context
      ▿ codingPath : 1 element
        - 0 : CodingKeys(stringValue: "lastSevenDays", intValue: nil)
      - debugDescription : "Expected to decode Array<Any> but found a dictionary instead."
      - underlyingError : nil

The only way to solve this problem was to add a custom initializer for decoding my struct, which parses the dictionary as [String: Double] and converts that dictionary into [Date: Double], using the same DateFormatter

public init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    todaysProvision = try container.decode(Double.self, forKey: .todaysProvision)
    referenceDate = try container.decode(Date.self, forKey: .referenceDate)
    let foo = try container.decode([String: Double].self, forKey: .lastSevenDays)
    lastSevenDays = Dictionary(uniqueKeysWithValues:foo.compactMap { (key, value) in
        guard let date = df.date(from: key) else { return nil }
        return (date, value)
    })
}

Is this the only possible way to parse [Date: Double] using Codables or am I doing something wrong?

Thanks!

Christoph P.
  • 670
  • 9
  • 17
  • may be i skipped part of your question but i don't think there is a way – Shehata Gamal Feb 20 '19 at 10:46
  • I regard this as a bug. The error message `Expected to decode Array` is definitely wrong. – vadian Feb 20 '19 at 11:37
  • 2
    Possible duplicate of [Swift 4 Decodable - Dictionary with enum as key](https://stackoverflow.com/questions/44725202/swift-4-decodable-dictionary-with-enum-as-key). The `enum` in the title of that post can be replaced with any type that is not a `String` or an `Int` – pckill Feb 20 '19 at 12:01

0 Answers0