This is a continuation of my previous question Parse decodable struct property as any data type
I have a funciton that goes and gets data from an API and decodes this JSON to an NbaJsonData
type
func getTeamGameLog(teamId: Int) async throws -> NbaJsonData {
// This will need to be retrieved from somewhere else, as this will change over time
let season = "2022-23"
let urlStr = "https://stats.nba.com/stats/teamgamelog?DateFrom=&DateTo=&LeagueID=00&Season=\(season)&SeasonType=Regular%20Season&TeamID=\(teamId)"
let url = URL(string: urlStr)
var request = URLRequest(url: url!)
request.httpMethod = "GET"
request.setValue("stats.nba.com", forHTTPHeaderField: "host")
request.setValue("https://www.nba.com/", forHTTPHeaderField: "Referer")
request.setValue("https://www.nba.com", forHTTPHeaderField: "Origin")
request.setValue("application/json", forHTTPHeaderField: "Accept")
let (data, _) = try await URLSession.shared.data(for: request)
return try (JSONDecoder().decode(NbaJsonData.self, from: data))
}
NbaJsonData
looks like this, where ResultSet
is an enum
struct NbaJsonData: Decodable {
var resultSets: [ResultSet]
}
and
enum ResultSet: Decodable {
case lastMeeting([LastMeeting])
case lineScore([LineScore])
case teamGameLog([TeamGameLog])
case string([String]) // A catch-all for arrays we don't need.
enum CodingKeys: CodingKey {
case name
case headers
case rowSet
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
// Decoding must be done in the order the data arrives
// 1, "two" => Int.self, String.self... etc.
let name = try container.decode(String.self, forKey: .name)
_ = try container.decode([String].self, forKey: .headers)
// rowSet can be any type, but the type is inferred from the resouce "name".
// We don't want it to throw, so for now, set an empty array for everything else
switch name {
case "GameHeader",
"AvailableVideo":
self = .string([String]()) // Set an empty array, we don't need them yet
case "TeamGameLog":
self = .teamGameLog(try container.decode([TeamGameLog].self, forKey: .rowSet))
case "LineScore":
self = .lineScore(try container.decode([LineScore].self, forKey: .rowSet))
case "LastMeeting":
// Decode it as a LastMeeting object
self = .lastMeeting(try container.decode([LastMeeting].self, forKey: .rowSet))
default:
// Nothing to parse
throw DecodingError.dataCorrupted(.init(codingPath: container.codingPath, debugDescription: "Unknown resource name \(name)"))
}
}
}
Now, this works okay, and calling the getTeamGameLog
function works, and I can see the output in the debugger, but the problem I now have is accessing that data, so I want to do something like this
let data = try await getTeamGameLog(teamId: team.id)
let resultSet = data.resultSets[0] // This is an enum result, and is an array
// foreach teamGameLog in resultSet
resultSet
does show the data in the debugger, but I don't know how to parse what's in it as a type
The highlighted gameId
property is what I'm trying to access.