0

Good morning,

I have an issue with my SwiftUI list. After receiving the data from my JSON correctly and having it as a string afterwards, the informations doesn't seem to appear in my list view.

struct Lists: View{

@State private var Countries = [Country]()

var body: some View {
        List(Countries, id: \.id) { item in
            VStack(alignment: .leading) {
                Text(item.Country)
                    .font(.headline)
                Text(item.Country)
            }
        }.onAppear(perform: loadData)
    
}

func loadData() {
    guard let url = URL(string: "https://api.covid19api.com/summary") else {
        print("Invalid URL")
        return
    }
    let request = URLRequest(url: url)
    let jsonData = (try! String(contentsOf: url))
    /*print(jsonData)*/
    
    URLSession.shared.dataTask(with: request) { data, response, error in
        
        let decoder = JSONDecoder()
        decoder.dateDecodingStrategy = .iso8601
        
        if let jsonData = data {
            do {
                let decodedResponse = try decoder.decode(AllCountries.self, from: jsonData)
                print(decodedResponse.Countries)
                
            } catch let error as NSError {
            /*print("json error: \(error.localizedDescription)")*/
            print("json error: \(error)")
            
        }
    }
    }.resume()
}

Here are my structs for the object:

struct AllCountries: Decodable {
    var Countries: [Country]
}

struct AllCountries: Decodable {
    var Countries: [Country] }
    struct Country: Decodable, Identifiable {
    let id = UUID()
    var Country, CountryCode, Slug: String
    var NewConfirmed, TotalConfirmed, NewDeaths, TotalDeaths: Int
    var NewRecovered, TotalRecovered: Int
    var Date: Date
    }

    enum CodingKeys: String, CodingKey {
        case Country = "Country"
        case CountryCode = "CountryCode"
        case Slug = "Slug"
        case NewConfirmed = "NewConfirmed"
        case TotalConfirmed = "TotalConfirmed"
        case NewDeaths = "NewDeaths"
        case TotalDeaths = "TotalDeaths"
        case NewRecovered = "NewRecovered"
        case TotalRecovered = "TotalRecovered"
        case Date = "Date"       
    }
}

Here is the beginning of the result of the "data" when I print it:

[_IOS_Project.Country(id: EB629D42-8278-444C-878E-A6EAC46BD5D6, Country: "Afghanistan", CountryCode: "AF", Slug: "afghanistan", NewConfirmed: 546, TotalConfirmed: 28424, NewDeaths: 21, TotalDeaths: 569, NewRecovered: 330, TotalRecovered: 8292, Date: 2020-06-21 19:37:01 +0000), _IOS_Project.Country(id: 8DDDCA84-CE99-4374-A487-096BFDF8A467, Country: "Albania", CountryCode: "AL", Slug: "albania", NewConfirmed: 53, TotalConfirmed: 1891, NewDeaths: 1, TotalDeaths: 43, NewRecovered: 12, TotalRecovered: 1126, Date: 2020-06-21 19:37:01 +0000),

Could somebody point me to the right direction on this issue?

Thanks in advance :)

2
  • Where do you add the countries to the countries array? You seem to download it but you don't add it. Commented Jun 21, 2020 at 19:48
  • @Andrew Hi, you mean that I don't seem to assign the values that I get to the struct? (sorry If my coding vocabulary seems all over the place, I'm new to Swift :\ ) Edit: It's updated, it seems I forgot a piece of code Commented Jun 21, 2020 at 19:52

1 Answer 1

4

There seems to be a few problems with your code.

Firstly the naming of your variables. Variable names in Swift begin with a lowercase, structs and classes begin with uppercase.

Secondly you aren't assigning the response from the your URLRequest to the countries state variable, this is the main problem.

Thirdly, your pasted code doesn't seem to be formatted correctly.

I put your code into a fresh project and with a few tweaks I got it to work.

struct ContentView: View {

    @State private var countries = [AllCountries.Country]()

    var body: some View {
        List(countries, id: \.id) { item in
            VStack(alignment: .leading) {
                Text(item.country)
                    .font(.headline)
                Text(item.country)
            }
        }.onAppear(perform: loadData)

    }

    func loadData() {
        guard let url = URL(string: "https://api.covid19api.com/summary") else {
            print("Invalid URL")
            return
        }
        let request = URLRequest(url: url)

        URLSession.shared.dataTask(with: request) { data, response, error in

            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .iso8601

            if let jsonData = data {
                do {
                    let decodedResponse = try decoder.decode(AllCountries.self, from: jsonData)
                    print(decodedResponse.countries)
                    // You should update this on the main thread
                    DispatchQueue.main.async {
                        // this assigns the values you received to the state variable countries
                        self.countries = decodedResponse.countries
                    }

                } catch let error as NSError {
                    print("json error: \(error)")
                }
            }
        }.resume()
    }
}

Notice in the loadData function that I assign the response from the URLRequest to the countries variable. Because this is a @State variable it causes the screen to reload when it changes. You weren't doing this so your UI had no idea that it needed to update.

I also updated your variable names, so that they are lowercased.

struct AllCountries: Decodable {
    var countries: [Country]

    enum CodingKeys: String, CodingKey {
        case countries = "Countries"
    }

    struct Country: Decodable, Identifiable {
        let id = UUID()
        var country, countryCode, slug: String
        var newConfirmed, totalConfirmed, newDeaths, totalDeaths: Int
        var newRecovered, totalRecovered: Int
        var date: Date

        enum CodingKeys: String, CodingKey {
            case country = "Country"
            case countryCode = "CountryCode"
            case slug = "Slug"
            case newConfirmed = "NewConfirmed"
            case totalConfirmed = "TotalConfirmed"
            case newDeaths = "NewDeaths"
            case totalDeaths = "TotalDeaths"
            case newRecovered = "NewRecovered"
            case totalRecovered = "TotalRecovered"
            case date = "Date"
        }
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

What can I say except thank you! :) Honestly, thanks for taking your time to explain that to me. Have a good day

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.