Thanks for the good advice and for the motivation!
Here's what I did:
- Used the app.quicktype.io to get the structs
- Played a bit arrond in Playgrounds
- Two let with type DATE did not worke? I changed them to a STRING.
- Made some easy print() functions to get some output on the console...
And it worked!
I understand all ot the struct stuff, the CodingKeys and the JSONDecoder but I have little idea what all the classes and functions (below the JSON data) are doing? Any insight for me?
See my code
import Foundation
import SwiftUI
// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
// let welcome = try? JSONDecoder().decode(Welcome.self, from: jsonData)
let jsonData = """
**(( Had to delete the JSON data here because it was to much text for the forum - please copy data into here))**
""".data(using: .utf8)!
// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
// let welcome = try? JSONDecoder().decode(Welcome.self, from: jsonData)
import Foundation
// MARK: - Welcome
struct Welcome: Codable {
let code: Int
let status: String
let data: DataClass
}
// MARK: - DataClass
struct DataClass: Codable {
let progID, eventID: Int
let progName: String
let isRace: Bool
let progDate, progDateUTC: String
let progTime, progTimeUTC: JSONNull?
let progTimezoneName, progTimezoneOffset, progGender: String
let progMinAge, progMaxAge: Int
let progDistanceCategory: String
let progDistances: [JSONAny]
let progNotes: JSONNull?
let results: [Result]
let team, liveTimingEnabled: Bool
let event: Event
let meta: Meta
let headers: [Header]
let headersCount: Int
enum CodingKeys: String, CodingKey {
case progID = "prog_id"
case eventID = "event_id"
case progName = "prog_name"
case isRace = "is_race"
case progDate = "prog_date"
case progDateUTC = "prog_date_utc"
case progTime = "prog_time"
case progTimeUTC = "prog_time_utc"
case progTimezoneName = "prog_timezone_name"
case progTimezoneOffset = "prog_timezone_offset"
case progGender = "prog_gender"
case progMinAge = "prog_min_age"
case progMaxAge = "prog_max_age"
case progDistanceCategory = "prog_distance_category"
case progDistances = "prog_distances"
case progNotes = "prog_notes"
case results, team
case liveTimingEnabled = "live_timing_enabled"
case event, meta, headers
case headersCount = "headers_count"
}
}
// MARK: - Event
struct Event: Codable {
let eventID: Int
let eventTitle, eventSlug: String
//let eventEditDate: Date
let eventEditDate: String
let eventVenue, eventCountry: String
let eventLatitude, eventLongitude: Double
let eventDate, eventFinishDate, eventCountryIsoa2, eventCountryNOC: String
let eventRegionID, eventCountryID: Int
let eventRegionName, eventStatus: String
let triathlonlive: Bool
let eventCategories, eventSpecifications: [EventCategoryElement]
let eventFlag: String
let eventListing, eventAPIListing: String
enum CodingKeys: String, CodingKey {
case eventID = "event_id"
case eventTitle = "event_title"
case eventSlug = "event_slug"
case eventEditDate = "event_edit_date"
case eventVenue = "event_venue"
case eventCountry = "event_country"
case eventLatitude = "event_latitude"
case eventLongitude = "event_longitude"
case eventDate = "event_date"
case eventFinishDate = "event_finish_date"
case eventCountryIsoa2 = "event_country_isoa2"
case eventCountryNOC = "event_country_noc"
case eventRegionID = "event_region_id"
case eventCountryID = "event_country_id"
case eventRegionName = "event_region_name"
case eventStatus = "event_status"
case triathlonlive
case eventCategories = "event_categories"
case eventSpecifications = "event_specifications"
case eventFlag = "event_flag"
case eventListing = "event_listing"
case eventAPIListing = "event_api_listing"
}
}
// MARK: - EventCategoryElement
struct EventCategoryElement: Codable {
let catName: String
let catID: Int
let catParentID: Int?
enum CodingKeys: String, CodingKey {
case catName = "cat_name"
case catID = "cat_id"
case catParentID = "cat_parent_id"
}
}
// MARK: - Header
struct Header: Codable {
let segment, name: String
let units: String?
}
// MARK: - Meta
struct Meta: Codable {
let temperatureWater, temperatureAir, humidity, wbgt: JSONNull?
let wind, weather, wetsuit, headReferee: JSONNull?
let competitionJury: String
enum CodingKeys: String, CodingKey {
case temperatureWater = "temperature_water"
case temperatureAir = "temperature_air"
case humidity, wbgt, wind, weather, wetsuit
case headReferee = "head_referee"
case competitionJury = "competition_jury"
}
}
// MARK: - Result
struct Result: Codable {
let athleteID: Int
let athleteTitle, athleteFirst, athleteLast, athleteGender: String
let validated: Bool
let neutralStatus: Int
let updatedAt: String
let athleteProfileImage: JSONNull?
let dob: String
let athleteCountryID: Int
//let athleteEditDate: Date
let athleteEditDate: String
let athleteYob: Int
let athleteSlug, athleteNOC, athleteCountryName, athleteCountryIsoa2: String
let athleteAge: Int
let athleteListing: String
let athleteFlag: String
let athleteAPIListing: String
let athleteCategories: [Int]
let splits: [String]
let resultID: Int
let position: Position
let totalTime: String
let startNum: Int
enum CodingKeys: String, CodingKey {
case athleteID = "athlete_id"
case athleteTitle = "athlete_title"
case athleteFirst = "athlete_first"
case athleteLast = "athlete_last"
case athleteGender = "athlete_gender"
case validated
case neutralStatus = "neutral_status"
case updatedAt = "updated_at"
case athleteProfileImage = "athlete_profile_image"
case dob
case athleteCountryID = "athlete_country_id"
case athleteEditDate = "athlete_edit_date"
case athleteYob = "athlete_yob"
case athleteSlug = "athlete_slug"
case athleteNOC = "athlete_noc"
case athleteCountryName = "athlete_country_name"
case athleteCountryIsoa2 = "athlete_country_isoa2"
case athleteAge = "athlete_age"
case athleteListing = "athlete_listing"
case athleteFlag = "athlete_flag"
case athleteAPIListing = "athlete_api_listing"
case athleteCategories = "athlete_categories"
case splits
case resultID = "result_id"
case position
case totalTime = "total_time"
case startNum = "start_num"
}
}
enum Position: Codable {
case integer(Int)
case string(String)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(Int.self) {
self = .integer(x)
return
}
if let x = try? container.decode(String.self) {
self = .string(x)
return
}
throw DecodingError.typeMismatch(Position.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Position"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .integer(let x):
try container.encode(x)
case .string(let x):
try container.encode(x)
}
}
}
// MARK: - Encode/decode helpers
class JSONNull: Codable, Hashable {
public static func == (lhs: JSONNull, rhs: JSONNull) -> Bool {
return true
}
public var hashValue: Int {
return 0
}
public func hash(into hasher: inout Hasher) {
// No-op
}
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(JSONNull.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for JSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
class JSONCodingKey: CodingKey {
let key: String
required init?(intValue: Int) {
return nil
}
required init?(stringValue: String) {
key = stringValue
}
var intValue: Int? {
return nil
}
var stringValue: String {
return key
}
}
class JSONAny: Codable {
let value: Any
static func decodingError(forCodingPath codingPath: [CodingKey]) -> DecodingError {
let context = DecodingError.Context(codingPath: codingPath, debugDescription: "Cannot decode JSONAny")
return DecodingError.typeMismatch(JSONAny.self, context)
}
static func encodingError(forValue value: Any, codingPath: [CodingKey]) -> EncodingError {
let context = EncodingError.Context(codingPath: codingPath, debugDescription: "Cannot encode JSONAny")
return EncodingError.invalidValue(value, context)
}
static func decode(from container: SingleValueDecodingContainer) throws -> Any {
if let value = try? container.decode(Bool.self) {
return value
}
if let value = try? container.decode(Int64.self) {
return value
}
if let value = try? container.decode(Double.self) {
return value
}
if let value = try? container.decode(String.self) {
return value
}
if container.decodeNil() {
return JSONNull()
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decode(from container: inout UnkeyedDecodingContainer) throws -> Any {
if let value = try? container.decode(Bool.self) {
return value
}
if let value = try? container.decode(Int64.self) {
return value
}
if let value = try? container.decode(Double.self) {
return value
}
if let value = try? container.decode(String.self) {
return value
}
if let value = try? container.decodeNil() {
if value {
return JSONNull()
}
}
if var container = try? container.nestedUnkeyedContainer() {
return try decodeArray(from: &container)
}
if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self) {
return try decodeDictionary(from: &container)
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decode(from container: inout KeyedDecodingContainer<JSONCodingKey>, forKey key: JSONCodingKey) throws -> Any {
if let value = try? container.decode(Bool.self, forKey: key) {
return value
}
if let value = try? container.decode(Int64.self, forKey: key) {
return value
}
if let value = try? container.decode(Double.self, forKey: key) {
return value
}
if let value = try? container.decode(String.self, forKey: key) {
return value
}
if let value = try? container.decodeNil(forKey: key) {
if value {
return JSONNull()
}
}
if var container = try? container.nestedUnkeyedContainer(forKey: key) {
return try decodeArray(from: &container)
}
if var container = try? container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key) {
return try decodeDictionary(from: &container)
}
throw decodingError(forCodingPath: container.codingPath)
}
static func decodeArray(from container: inout UnkeyedDecodingContainer) throws -> [Any] {
var arr: [Any] = []
while !container.isAtEnd {
let value = try decode(from: &container)
arr.append(value)
}
return arr
}
static func decodeDictionary(from container: inout KeyedDecodingContainer<JSONCodingKey>) throws -> [String: Any] {
var dict = [String: Any]()
for key in container.allKeys {
let value = try decode(from: &container, forKey: key)
dict[key.stringValue] = value
}
return dict
}
static func encode(to container: inout UnkeyedEncodingContainer, array: [Any]) throws {
for value in array {
if let value = value as? Bool {
try container.encode(value)
} else if let value = value as? Int64 {
try container.encode(value)
} else if let value = value as? Double {
try container.encode(value)
} else if let value = value as? String {
try container.encode(value)
} else if value is JSONNull {
try container.encodeNil()
} else if let value = value as? [Any] {
var container = container.nestedUnkeyedContainer()
try encode(to: &container, array: value)
} else if let value = value as? [String: Any] {
var container = container.nestedContainer(keyedBy: JSONCodingKey.self)
try encode(to: &container, dictionary: value)
} else {
throw encodingError(forValue: value, codingPath: container.codingPath)
}
}
}
static func encode(to container: inout KeyedEncodingContainer<JSONCodingKey>, dictionary: [String: Any]) throws {
for (key, value) in dictionary {
let key = JSONCodingKey(stringValue: key)!
if let value = value as? Bool {
try container.encode(value, forKey: key)
} else if let value = value as? Int64 {
try container.encode(value, forKey: key)
} else if let value = value as? Double {
try container.encode(value, forKey: key)
} else if let value = value as? String {
try container.encode(value, forKey: key)
} else if value is JSONNull {
try container.encodeNil(forKey: key)
} else if let value = value as? [Any] {
var container = container.nestedUnkeyedContainer(forKey: key)
try encode(to: &container, array: value)
} else if let value = value as? [String: Any] {
var container = container.nestedContainer(keyedBy: JSONCodingKey.self, forKey: key)
try encode(to: &container, dictionary: value)
} else {
throw encodingError(forValue: value, codingPath: container.codingPath)
}
}
}
static func encode(to container: inout SingleValueEncodingContainer, value: Any) throws {
if let value = value as? Bool {
try container.encode(value)
} else if let value = value as? Int64 {
try container.encode(value)
} else if let value = value as? Double {
try container.encode(value)
} else if let value = value as? String {
try container.encode(value)
} else if value is JSONNull {
try container.encodeNil()
} else {
throw encodingError(forValue: value, codingPath: container.codingPath)
}
}
public required init(from decoder: Decoder) throws {
if var arrayContainer = try? decoder.unkeyedContainer() {
self.value = try JSONAny.decodeArray(from: &arrayContainer)
} else if var container = try? decoder.container(keyedBy: JSONCodingKey.self) {
self.value = try JSONAny.decodeDictionary(from: &container)
} else {
let container = try decoder.singleValueContainer()
self.value = try JSONAny.decode(from: container)
}
}
public func encode(to encoder: Encoder) throws {
if let arr = self.value as? [Any] {
var container = encoder.unkeyedContainer()
try JSONAny.encode(to: &container, array: arr)
} else if let dict = self.value as? [String: Any] {
var container = encoder.container(keyedBy: JSONCodingKey.self)
try JSONAny.encode(to: &container, dictionary: dict)
} else {
var container = encoder.singleValueContainer()
try JSONAny.encode(to: &container, value: self.value)
}
}
}
let welcome = try JSONDecoder().decode(Welcome.self, from: jsonData)
print("Code: \(welcome.code)")
print("Prog ID: \(welcome.data.progID)")
print("Event Title: \(welcome.data.event.eventTitle)")
print("Athlet Title: \(welcome.data.results[0].athleteTitle)")
print("Split 0: \(welcome.data.results[0].splits[0])")
//Dump all of it
print("\nDump all:")
dump(welcome)
One question - I dont need all the encodeing stuff - right? Since I am only decoding the JSON data?
So now I will try to get a working prototype in xCode with some usefull output in a list or table view...
Thx