I have an enum for decoding points, polygons, and multipolygons from GeoJSON files using several nested Codable types. I can decode GeoJSON files without issue, but when I encode this data to store as binary data in Core Data, it can't be decoded again.
I'm getting typeMismatch when trying to decode.
Here's my enum:
enum GeoJSONFeatureGeometryCoordinates: Codable {
case point([Double])
case polygon([[[Double]]])
case multipolygon([[[[Double]]]])
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
let polygonVal = try container.decode([[[Double]]].self)
self = .polygon(polygonVal)
} catch DecodingError.typeMismatch {
do {
let multipolygonVal = try container.decode([[[[Double]]]].self)
self = .multipolygon(multipolygonVal)
} catch DecodingError.typeMismatch {
let pointVal = try container.decode([Double].self)
self = .point(pointVal)
}
}
}
}
And here's where I successfully encode it but throw an error when testing the decoding
do {
let encoder = JSONEncoder()
let encodedCoordinates = try encoder.encode(centroid.geometry.coordinates)
let decoder = JSONDecoder()
let decodedCoordinates = try decoder.decode(GeoJSONFeatureGeometryCoordinates.self, from: encodedCoordinates)
} catch {
fatalError("\n\n Error archiving feature.geometry.coordinates as Data")
}
The decoding fails because you are encoding it in one format and decoding it in another format.
Since you did not provide an encode(to:)
implementation, an implementation is automatically generated by the Swift compiler. This implementation encodes your enum to a format like this:
{
"point": {
"_0": [] // the Double array goes here
}
}
Notice how the name of the enum case, as well as the names of the associated values are also recorded. This is different from the format that your init(from:)
implementation expects. Therefore decoding fails.
You can add an explicit implementation of encode(to:)
, so that it produces the format that init(from:)
expects.
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .point(let arr):
try container.encode(arr)
case .polygon(let arr):
try container.encode(arr)
case .multipolygon(let arr):
try container.encode(arr)
}
}