Im working on project using the football-data.org api and have received an API key for it that instructed me to modify my client to use a HTTP header named “X-Auth-Token” with the underneath personal token as value. I asked a question about retrieving data from a json api recently but that one did not have an API Key needed.
check past question here
I updated the fetch function to incorporate my API key but no data is being retrieved. I have attached the updated fetch func and where it gets called as well as the object structs incase it is an issue with how I created my array of teams.
ContentView:
struct ContentView: View {
@State var teams: [Team] = []
let apiKey = "API_KEY"
// for procesing fetch request from json URL
@State private var processing: Bool = false
var body: some View {
VStack {
if processing {
ProgressView()
}
else {
ForEach(teams, id: .id) { team in
Text(team.name ?? "Team Not Found")
}
}
}
.task {
processing = true
let response: TeamApiResponse? = await fetchTeam(urlString: "https://api.football-data.org/v4/competitions/PL/teams")
if let teamResponse = response?.team {
teams = teamResponse
}
processing = false
}
.padding()
}
// curl -X GET "https://api.football-data.org/v4/competitions/PL/teams/66" -H "X-Auth-Token: API_KEY"
// Man United = 66
func fetchTeam<T: Decodable>(urlString: String) async -> T? {
// Define your API key
let apiKey = "your_api_key_here"
// Create the URL from the provided urlString
guard let url = URL(string: urlString) else {
print("Invalid URL string")
return nil
}
// Create a URLRequest object
var request = URLRequest(url: url)
// Add the API key to the request header
request.setValue(apiKey, forHTTPHeaderField: "X-Auth-Token")
do {
// Perform the network request
let (data, response) = try await URLSession.shared.data(for: request)
// Check if the response status code is 200 (OK)
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
print(URLError(.badServerResponse))
return nil
}
// Decode the data into the specified type
return try JSONDecoder().decode(T.self, from: data)
} catch {
// Handle errors during the request or decoding
print("----> error: (error)")
return nil
}
}
}
structs:
import Foundation
import UIKit
struct TeamApiResponse: Decodable {
var team: [Team]?
}
struct Team: Decodable, Identifiable {
var id: String?
var name: String?
var shortName: String?
var abbreviation: String?
var crest: UIImage?
var address: String?
var website: URL?
var founded: String?
var clubColors: String? // Idk do something with this later in relation to UI
var venue: String?
var marketValue: Int? // add formatting to this
var lastUpdated: String?
var runningCompetitions: [Competition]
var coach: Coach
var squad: [Player]
enum CodingKeys: String, CodingKey {
// omitting area for now
case id
case name
case shortName
case abbreviation = "tla"
case crestURL = "crest"
case address
case website
case founded
case clubColors
case venue
case marketValue
// omitting staff for now
case lastUpdated
case runningCompetitions
case coach
case squad
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.id = try values.decode(String.self, forKey: .id)
self.name = try values.decodeIfPresent(String.self, forKey: .name)
self.shortName = try values.decodeIfPresent(String.self, forKey: .shortName)
self.abbreviation = try values.decodeIfPresent(String.self, forKey: .abbreviation)
// convert crestURL to UIImage
let crestURL = try values.decodeIfPresent(URL.self, forKey: .crestURL)
let imageData = try Data(contentsOf: crestURL!) // may need to provide default value in instance this doesn't work
self.crest = UIImage(data: imageData)
self.address = try values.decodeIfPresent(String.self, forKey: .address)
self.website = try values.decodeIfPresent(URL.self, forKey: .website)
self.founded = try values.decodeIfPresent(String.self, forKey: .founded)
self.clubColors = try values.decodeIfPresent(String.self, forKey: .clubColors)
self.venue = try values.decodeIfPresent(String.self, forKey: .venue)
self.marketValue = try values.decodeIfPresent(Int.self, forKey: .marketValue)
self.lastUpdated = try values.decodeIfPresent(String.self, forKey: .lastUpdated)
// fetch from competitions block
self.runningCompetitions = try values.decodeIfPresent([Competition].self, forKey: .runningCompetitions) ?? []
// fetch from coach block
self.coach = try values.decodeIfPresent(Coach.self, forKey: .coach) ?? Coach()
// fetch from squad block
self.squad = try values.decodeIfPresent([Player].self, forKey: .squad) ?? []
}
}
/**
{
"id": 11656,
"firstName": "Agustín",
"lastName": "Rossi",
"name": "Agustín Rossi",
"position": "Goalkeeper",
"dateOfBirth": "1995-08-21",
"nationality": "Argentina",
"shirtNumber": 1,
"marketValue": 3200000,
"contract": {
"start": "2017-02",
"until": "2023-12"
}
}
*/
struct Player: Identifiable, Hashable, Decodable {
var id: Int
var firstName: String
var lastName: String
var name: String
var position: String
var dateOfBirth: String
var nationality: String
var shirtNumber: Int
var marketValue: Int // add formatting to this
//omitt contract dates for now
enum CodingKeys: String, CodingKey {
case id
case firstName
case lastName
case name
case position
case dateOfBirth
case nationality
case shirtNumber
case marketValue
}
init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.firstName = try container.decode(String.self, forKey: .firstName)
self.lastName = try container.decode(String.self, forKey: .lastName)
self.name = try container.decode(String.self, forKey: .name)
self.position = try container.decode(String.self, forKey: .position)
self.dateOfBirth = try container.decode(String.self, forKey: .dateOfBirth)
self.nationality = try container.decode(String.self, forKey: .nationality)
self.shirtNumber = try container.decode(Int.self, forKey: .shirtNumber)
self.marketValue = try container.decode(Int.self, forKey: .marketValue)
}
}
/**
"id": 60123,
"firstName": "Sebastián",
"lastName": "Battaglia",
"name": "Sebastián Battaglia",
"dateOfBirth": "1980-11-08",
"nationality": "Argentina",
"contract": {
"start": "2021-08",
"until": "2022-12"
}
*/
struct Coach: Identifiable, Hashable, Decodable {
var id: Int
var firstName: String
var lastName: String
var name: String
var dateOfBirth: String
var nationality: String
//omitt contract dates for now
enum CodingKeys: String, CodingKey {
case id
case firstName
case lastName
case name
case dateOfBirth
case nationality
}
init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.firstName = try container.decode(String.self, forKey: .firstName)
self.lastName = try container.decode(String.self, forKey: .lastName)
self.name = try container.decode(String.self, forKey: .name)
self.dateOfBirth = try container.decode(String.self, forKey: .dateOfBirth)
self.nationality = try container.decode(String.self, forKey: .nationality)
}
init() {
self.id = 0
self.firstName = ""
self.lastName = ""
self.name = ""
self.dateOfBirth = ""
self.nationality = ""
}
}
/**
{
"id": 2152,
"name": "Copa Libertadores",
"code": "CLI",
"type": "CUP",
"emblem": "https://crests.football-data.org/CLI.svg"
}
*/
struct Competition: Identifiable, Hashable, Decodable {
var id: Int
var name: String
var code: String
var type: String
var emblem: UIImage
enum CodingKeys: String, CodingKey {
case id
case name
case code
case type
case emblemURL = "emblem"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.id = try values.decode(Int.self, forKey: .id)
self.name = try values.decode(String.self, forKey: .name)
self.code = try values.decode(String.self, forKey: .code)
self.type = try values.decode(String.self, forKey: .type)
// convert emblemURL to UIImage
let emblemURL = try values.decode(URL.self, forKey: .emblemURL)
let imageData = try Data(contentsOf: emblemURL)
self.emblem = UIImage(data: imageData)!
}
}
1