I'm working with an API that provides 2 JSON URLS. Each URL contains a nested container with different attributes that belong to the same class and object.
JSON URL 1
{
"last_updated": 1535936629,
"xyz": 5,
"data": {
"dataList": [
{
"id": "42",
"a1": "a1value",
"a2": "a2value",
},
// ,,,
]
}
}
JSON URL 2
{
"last_updated": 1536639996,
"xyz": 5,
"data": {
"dataList": [
{
"id": "42",
"a3": "a3value",
"a4": "a4value",
},
// ,,,
]
}
}
I want to use these JSON URLS to create a single Codable CustomClass object using the items in the nested dataList list, so I created a Feed struct to handle these 2 JSON files.
Feed.swift
import Foundation
Struct Feed: Decodable {
var lastUpdated: Int
var xyz: Int
var data: KeyedDecodingContainer<Feed.dataCodingKey>
var dataList: [CustomClass]
enum CodingKeys: String, CodingKey {
case lastUpdated = "last_updated"
case xyz
case data
}
enum dataCodingKey: String, CodingKey {
case dataList
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.lastUpdated = try decoder.decode(Int.self, forKey: .lastUpdated)
self.xyz = try container.decode(Int.self, forKey: .xyz)
self.data = try container.nestedContainer(keyedBy: dataCodingKey.self, forKey: .data)
self.dataList = try data.decode([CustomClass].self, forKey: .dataList)
}
}
CustomClass.swift
class CustomClass: Decodable {
var id: String
var a1: String
var a2: Double
var a3: String
var a4: String
enum CodingKeys: String, CodingKey {
case id
case a1
case a2
case a3
case a4
}
required init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.id = try values.decode(String.self, forKey: .id)
self.a1 = try values.decode(String.self, forKey: .a1)
self.a2 = try values.decode(String.self, forKey: .a2)
self.a3 = try values.decode(String.self, forKey: .a3)
self.a4 = try values.decode(String.self, forKey: .a4)
}
}
In my ViewController, I do two separate asynchronous calls to obtain the data:
ViewController.swift
var listOfCustomClass: [CustomClass]
var listOfCustomClass2: [CustomClass]
func getFeed(urlString: String, completionHandler: @escaping (_ result: Feed?) -> Void) {
// parses JSON to return a Feed object
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
}
guard let data = data else { return }
//Implement JSON decoding and parsing
do {
let feed = try JSONDecoder().decode(Feed.self, from: data)
DispatchQueue.main.async {
completionHandler(feed)
}
} catch {
print(error)
}
}.resume()
}
getFeed(urlString: url1String) { result in
// Obtain the contents of dataList from URL1
if let feed = result {
self.listOfCustomClass = feed.dataList
self.getFeed(urlString: url2String) { result in
//Upon completion, obtain the dataList info and populate the "a3" and "a4" attributes from CustomClass
if let feed = result {
let dataList2: [CustomClass] = feed.dataList
// code to merge self.listOfCustomClass 1 and self.listOfCustomClass2 into a single [CustomClass] list with all attributes and store it as self.listOfCustomClass
// Upon completion, return the finalized station array for use
DispatchQueue.main.async {
completionHandler(self.listOfCustomClass)
}
}
}
}
}
The problem I'm running into is that the dataList CodingKey has different keys a1 or a2 if coming from URL1 or a3, a4 if coming from URL2. Therefore the Codable init method is complaining whenever it can't find 2 of 4 keys in the dataList container.
How can I approach creating one CustomClass object with a1, a2, a3, and a4 instantiated using a single Decoder?
MKAnnotation, show that in your question - but don't include all the code from your view controller - how the data is downloaded doesn't effect how the data is decoded.