Search code examples
swiftxcodeswiftuirapidapi

Struggling with rapid API and swiftui


I am struggling with getting football data API from rapidapi to work in Swift ui. Here is the code below .

The error I got is:

"self.team = decodedTeams" Cannot find 'self' in scope"

and in my content view I get for $network.getTeams:

Value of type 'EnvironmentObject.Wrapper' has no dynamic member 'getTeams' using key path from root type 'Network'

I have set out what I have in two pages of my swiftui code below. Any help would be appreciated, I am really struggling with this one.

//  Network.swift
//  Football Scores
//

//

import Foundation

class Network: ObservableObject {
        @Published var teams: [Team] = []

}
func getTeams() {
let headers = [
    "X-RapidAPI-Key": "MY API KEY",
    "X-RapidAPI-Host": "api-football-v1.p.rapidapi.com"
]`

let request = NSMutableURLRequest(url: NSURL(string: "https://api-football-v1.p.rapidapi.com/v3/standings?season=2022&league=39")! as URL,
                                  cachePolicy: .useProtocolCachePolicy,
                                  timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
    if (error != nil) {
        print(error)
    } else {
        let httpResponse = response as? HTTPURLResponse
        print(httpResponse)
        DispatchQueue.main.async {
                          do {
                              let decodedTeams = try JSONDecoder().decode([Team].self, from: data)
                              self.team = decodedTeams
                          } catch let error {
                              print("Error decoding: ", error)
                          }
                      }
    }
})

dataTask.resume()
}

and

//
//  Team.swift
//  Football Scores
//

//

import Foundation

struct Team: Identifiable, Decodable {
var id: Int
var name: String
var logo: String
var points: String
var goaldif: String
}

and

//  Football_ScoresApp.swift
//  Football Scores
//


import SwiftUI
import Foundation


@main

struct Football_ScoresApp: App {

var network = Network()

var body: some Scene {
    WindowGroup {
    
        ContentView()
                                       .environmentObject(network)
        }
    }

}

and

import SwiftUI
import CoreData

struct ContentView: View {
    @EnvironmentObject var network: Network


var body: some View {
    ScrollView {
                    Text("All teams")
                    .font(.title).bold()
            }
            .onAppear {
                network.getTeams()
            }
    VStack(alignment: .leading) {
        ForEach(network.teams) { team in
            HStack(alignment:.top) {
                Text("\(team.id)")

                VStack(alignment: .leading) {
                    Text(team.name)
                        .bold()

                   
                }
            }
            .frame(width: 300, alignment: .leading)
            .padding()
            .background(Color(#colorLiteral(red: 0.6667672396, green: 0.7527905703, blue: 1, alpha: 0.2662717301)))
            .cornerRadius(20)
        }
    }
    
}



}



struct ContentView_Previews: PreviewProvider {
static var previews: some View {
    ContentView()
        .environmentObject(Network())
    
}
}

Solution

  • To display the teams from your api request, first you need to create a set of struct models that represent the json data that the server is sending. From the server response, you need to extract the teams information you want to display. Here is my code that shows how to do it, works well for me, pay attention to the details:

    struct ContentView: View {
        @StateObject var network = Network() // <-- for testing
    
        var body: some View {
            List {
                VStack(alignment: .leading) {
                    ForEach(network.teams) { team in
                        HStack(alignment:.top) {
                            Text("\(team.id)")
                            Text(team.name).bold()
                        }
                        .frame(width: 300, alignment: .leading)
                        .padding()
                        .background(Color(#colorLiteral(red: 0.6667672396, green: 0.7527905703, blue: 1, alpha: 0.2662717301)))
                        .cornerRadius(20)
                    }
                }
            }
            .onAppear {
                network.getTeams()
            }
        }
        
    }
    
    class Network: ObservableObject {
        @Published var teams: [Team] = []
        
        func getTeams() {
            let token = "your-key"  // <--- here your api key
    
            guard let url = URL(string: "https://api-football-v1.p.rapidapi.com/v3/standings?season=2022&league=39") else { return }
            
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            request.setValue("\(token)", forHTTPHeaderField: "X-RapidAPI-Key")
            request.setValue("api-football-v1.p.rapidapi.com", forHTTPHeaderField: "X-RapidAPI-Host")
            
            URLSession.shared.dataTask(with: request) { data, response, error in
                guard let data = data else { return }
                DispatchQueue.main.async {
                    do {
                        let results = try JSONDecoder().decode(FootyResponse.self, from: data)
                        // extract just the teams
                        for response in results.response {
                            for stand in response.league.standings {
                                for league in stand {
                                    self.teams.append(league.team)
                                }
                            }
                        }
                    } catch {
                        print(error) // <-- here important
                    }
                }
            }.resume()
        }
      
    }
    
    // MARK: - FootyResponse
    struct FootyResponse: Codable {
        let welcomeGet: String
        let parameters: Parameters
        let errors: [String]
        let results: Int
        let paging: Paging
        let response: [Response]
    
        enum CodingKeys: String, CodingKey {
            case welcomeGet = "get"
            case parameters, errors, results, paging, response
        }
    }
    
    // MARK: - Paging
    struct Paging: Codable {
        let current, total: Int
    }
    
    // MARK: - Parameters
    struct Parameters: Codable {
        let league, season: String
    }
    
    // MARK: - Response
    struct Response: Codable {
        let league: League
    }
    
    // MARK: - League
    struct League: Codable {
        let id: Int
        let name: String
        let country: String
        let logo: String
        let flag: String
        let season: Int
        let standings: [[Standing]]
    }
    
    // MARK: - Standing
    struct Standing: Codable {
        let rank: Int
        let team: Team
        let points, goalsDiff: Int
        let group: String
        let form: String
        let status: String
        let standingDescription: String?
        let all, home, away: All
        let update: String  // <-- Date
    
        enum CodingKeys: String, CodingKey {
            case rank, team, points, goalsDiff, group, form, status
            case standingDescription = "description"
            case all, home, away, update
        }
    }
    
    // MARK: - All
    struct All: Codable {
        let played, win, draw, lose: Int
        let goals: Goals
    }
    
    // MARK: - Goals
    struct Goals: Codable {
        let goalsFor, against: Int
    
        enum CodingKeys: String, CodingKey {
            case goalsFor = "for"
            case against
        }
    }
    
    // MARK: - Team
    struct Team: Identifiable, Codable {
        let id: Int
        let name: String
        let logo: String
    }
    

    EDIT-1:

    to get the points of each team, you need to use the Standing struct. Here is an example code to do that.

    struct ContentView: View {
        @StateObject var network = Network()
    
        var body: some View {
            List {
                VStack(alignment: .leading) {
                    ForEach(network.stand) { stand in  // <-- here
                        HStack(alignment:.top) {
                            Text(stand.team.name).bold()  // <-- here
                            Text("\(stand.points) points")  // <-- here
                        }
                        .frame(width: 300, alignment: .leading)
                        .padding()
                        .background(Color(#colorLiteral(red: 0.6667672396, green: 0.7527905703, blue: 1, alpha: 0.2662717301)))
                        .cornerRadius(20)
                    }
                }
            }
            .onAppear {
                network.getTeams()
            }
        }
        
    }
    
    class Network: ObservableObject {
        @Published var teams: [Team] = []
        
        @Published var stand: [Standing] = []  // <-- here
        
        func getTeams() {
            let token = "your-key"  // <--- here your api key
    
            guard let url = URL(string: "https://api-football-v1.p.rapidapi.com/v3/standings?season=2022&league=39") else { return }
            
            var request = URLRequest(url: url)
            request.httpMethod = "GET"
            request.setValue("\(token)", forHTTPHeaderField: "X-RapidAPI-Key")
            request.setValue("api-football-v1.p.rapidapi.com", forHTTPHeaderField: "X-RapidAPI-Host")
            
            URLSession.shared.dataTask(with: request) { data, response, error in
                guard let data = data else { return }
                DispatchQueue.main.async {
                    do {
                        let results = try JSONDecoder().decode(FootyResponse.self, from: data)
                        // extract the teams and the standings
                        results.response.forEach{ response in
                            response.league.standings.forEach{ stand in
                                self.stand = stand  // <--- here
                                stand.forEach{ league in
                                    self.teams.append(league.team)
                                }
                            }
                        }
                    } catch {
                        print(error) // <-- here important
                    }
                }
            }.resume()
        }
      
    }
    
    // MARK: - Standing
    struct Standing: Identifiable, Codable {
        let id = UUID()  // <-- here
        let rank: Int
        let team: Team
        let points, goalsDiff: Int
        let group: String
        let form: String
        let status: String
        let standingDescription: String?
        let all, home, away: All
        let update: String  // <-- Date
    
        enum CodingKeys: String, CodingKey {
            case rank, team, points, goalsDiff, group, form, status
            case standingDescription = "description"
            case all, home, away, update
        }
    }