Thanks for your help in advance! I am relatively new to SwiftUI and have been struggling with encoding an enum with Arrays of custom objects. Here is the code:
struct ChartData: Codable {
var data: DataType
...
private enum CodingKeys : String, CodingKey { case id, title, type, info, label_x, label_y, last_update, data }
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
...
try container.encode(self.data, forKey: .data)
}
}
enum DataType: Codable{
case plot([ChartPlotData])
case slice([ChartSliceData])
case number([ChartNumberData])
}
struct ChartPlotData: Codable {
var chart_id: String
var order_plot: Int
var label_plot: String?
var points_x: [String]
var points_y: [Double]
private enum CodingKeys : String, CodingKey { case chart_id, order_plot, label_plot, points_x, points_y }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.chart_id = try container.decode(String.self, forKey: .chart_id)
self.order_plot = try container.decode(Int.self, forKey: .order_plot)
self.label_plot = try? container.decode(String.self, forKey: .label_plot)
do{
self.points_x = try container.decode([String].self, forKey:.points_x)
}catch{
let xs = try container.decode([Double].self, forKey:.points_x)
self.points_x = xs.map { String($0) }
}
self.points_y = try container.decode([Double].self, forKey:.points_y)
}
}
struct ChartSliceData: Codable {
var chart_id: String
var order_slice: Int
var label_slice: String
var value_slice: Double
}
struct ChartNumberData: Codable {
var chart_id: String
var number: Double
var unit: String?
}
I am attempting to cache JSON in UserDefaults so as to avoid having to make extraneous API calls. Using JSONEncoder, I am left with the following JSON snippet (excerpted from a much longer string):
"data" : {
"plot" : {
"_0" : [
{
"label_plot" : "China",
"points_y" : [
0,
0,
0,
...
However, I am looking to get an encoding like this:
"data" : [
{
"label_plot" : "China",
"points_y" : [
0,
0,
0,
...
Any help would be greatly appreciated! Thanks so much!
This can be solved by adding an extra item in the CodingKeys enum for encoding and decoding the type used for the DataType
enum and then using a switch
to encode and decode the right type of array.
Here is the full ChartData struct although with some properties and code removed for brevity
struct ChartData: Codable {
var id: Int
var title: String
var data: DataType
enum CodingKeys: String, CodingKey {
case id, title, data, type
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(title, forKey: .title)
switch data {
case .number(let values):
try container.encode("number", forKey: .type)
try container.encode(values, forKey: .data)
case .plot(let values):
try container.encode("plot", forKey: .type)
try container.encode(values, forKey: .data)
case .slice(let values):
try container.encode("slice", forKey: .type)
try container.encode(values, forKey: .data)
}
}
init(from decoder: Decoder) throws {
var container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
title = try container.decode(String.self, forKey: .title)
let type = try container.decode(String.self, forKey: .type)
switch type {
case "number":
let values = try container.decode([ChartNumberData].self, forKey: .data)
data = .number(values)
// case ...
default:
fatalError("Unsupported type for DataType: \(type)")
}
}
}