pointfreeco/swift-tagged

Decodable issue when using Tagged type as key in Dictionary

marcusficner opened this issue · 1 comments

Hey @stephencelis and @mbrandonw!

I'm using Tagged in my latest project and it works great but I found the following issue and I'm not sure where the error is.

I'm trying to decode JSON into a Dictionary where the key is a tagged type like this:

let json = """
{
    "data": {
        "key": "value"
    }
}
""".data(using: .utf8)!

struct SomeStruct: Decodable {
    typealias Key = Tagged<SomeStruct, String>
    let data: [Key: String]
}

Strangely I receive this error message on decoding:

typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "data", intValue: nil)], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))

When I change the type of the dictionary to [String : String] it works as expected.

Any ideas?

Thank you in advance and keep up the great work with Pointfree.co!

As Ole Begemann pointed out here: https://oleb.net/blog/2017/12/dictionary-codable-array/
this seems to be a bug in the Codable implementation. His findings are true for Decodable as well: https://github.com/apple/swift/blob/swift-4.1-branch/stdlib/public/core/Codable.swift.gyb#L2035-L2104

EDIT: Link posts to Swift 4.1. but it's true Swift 4.2 and Swift 5.0 also

Indeed, when I change to example above to:

struct SomeStruct: Decodable {
    typealias Key = Tagged<SomeStruct, String>
    let data: [Key.RawValue: String]
}

decoding works fine. But so I could've used String directly :(

As a workaround I'll decode into [String : String] and then reduce(into: [:], { result, x in result[Key(rawValue: x.key)] = x.value })