Issue #854

If you use enum case as key in Dictionary, JSONEncoder will encode it as Array. For example

enum Vehicle: String, Codable {
    case car
    case truck
}
struct Container: Codable {
    var map: [Vehicle: String]
}
struct Container2: Codable {
    var map: [String: String]
}
let container = Container(map: [
    .car: "Car 1"
])
let container2 = Container2(map: [
    "car": "Car 1"
])

let data = try! JSONEncoder().encode(container)
print(String(data: data, encoding: .utf8)) //  {"map":["car","Car 1"]}
let data2 = try! JSONEncoder().encode(container2)
print(String(data: data2, encoding: .utf8)) //  {"map":{"car":"Car 1"}}

The reason is how JSONEncoder checks for type https://github.com/apple/swift/blob/d2085d8b0e/stdlib/public/core/Codable.swift.gyb#L1967

If the dictionary uses String or Int keys, the contents are encoded in a keyed container. Otherwise, the contents are encoded as alternating key-value pairs in an unkeyed container.

extension Dictionary : Encodable where Key : Encodable, Value : Encodable {
    /// Encodes the contents of this dictionary into the given encoder.
    ///
    /// If the dictionary uses `String` or `Int` keys, the contents are encoded
    /// in a keyed container. Otherwise, the contents are encoded as alternating
    /// key-value pairs in an unkeyed container.
    ///
    /// This function throws an error if any values are invalid for the given
    /// encoder's format.
    ///
    /// - Parameter encoder: The encoder to write data to.
    @inlinable // FIXME(sil-serialize-all)
    public func encode(to encoder: Encoder) throws {
        if Key.self == String.self {
            // Since the keys are already Strings, we can use them as keys directly.
            var container = encoder.container(keyedBy: _DictionaryCodingKey.self)
            for (key, value) in self {
                let codingKey = _DictionaryCodingKey(stringValue: key as! String)!
                try container.encode(value, forKey: codingKey)
            }
        } else if Key.self == Int.self {
            // Since the keys are already Ints, we can use them as keys directly.
            var container = encoder.container(keyedBy: _DictionaryCodingKey.self)
            for (key, value) in self {
                let codingKey = _DictionaryCodingKey(intValue: key as! Int)!
                try container.encode(value, forKey: codingKey)
            }
        } else {
            // Keys are Encodable but not Strings or Ints, so we cannot arbitrarily
            // convert to keys. We can encode as an array of alternating key-value
            // pairs, though.
            var container = encoder.unkeyedContainer()
            for (key, value) in self {
                try container.encode(key)
                try container.encode(value)
            }
        }
    }
}

Read more