Passing data from API Call to UI

Hi,
API call request runs fine as print command within API call shows correct data has been downloaded.
Data in API has been made Observable and @Published, and Observed in UI, but receive two errors attempting to show data in UI: “Cannot convert value of type ‘DataModel?’ to expected argument type ‘Range’” and “Initializer ‘init(_:)’ requires that ‘Binding’ conform to ‘StringProtocol’”.
Here is code within UI:

   
   @ObservedObject var apiCall = APICall()
   
   var body: some View {
       //        Text("Hello World")
       List(apiCall.dataPull) {
           data in
           Text($apiCall.byCountry[28].cntry)
       }
   }

And here is API Call:

class APICall : ObservableObject, Identifiable {
   
   @Published var dataPull: DataModel? = nil
   
   init() {
       let urlCall = URL(string: "https://api.covid19api.com/summary")
       guard urlCall != nil else {
           print("Error reaching API")
           return
       }
       let session = URLSession.shared
       let dataTask = session.dataTask(with: urlCall!) { (data, response, error) in
           if error == nil && data != nil {
               let decoder = JSONDecoder()
               do {
                   let dataPull = try decoder.decode(DataModel.self, from: data!)
                   print(dataPull.byCountry[37].cntry)
               }
               catch {
                   print("Server busy, try again in 5 min.")
               }
               DispatchQueue.main.async {
                   self.dataPull = self.dataPull
               }
           }
       }
       dataTask.resume()
   }
}

Any suggestions?

what is your DataModel?

it seems you are trying to save self.dataPull to itself. try changing your variable names to avoid confusion and conflict

Hi Francis,
Thanks for response. I’m sure you’re on the right track, as I’m a beginner and somewhat clueless & confused as to how to declare the model, vs. a variable and when to us a colon “:” to assign a data model to variable, vs an equal sign “=”. Why sometimes one and sometimes the other??

In any case, here’s what the DataModel looks like:

import Foundation

class DataModel: Decodable, Identifiable {
    let global: Global
    let byCountry: [ByCountry]
    let date: String
    
    enum CodingKeys: String, CodingKey {
        case global = "Global"
        case byCountry = "Countries"
        case date = "Date"
    }
    
    struct Global: Decodable {
        let globalNC: Int
        let globalTC: Int
        let globalND: Int
        let globalTD: Int
        let globalNR: Int
        let globalTR: Int
        
        enum CodingKeys: String, CodingKey {
            case globalNC = "NewConfirmed"
            case globalTC = "TotalConfirmed"
            case globalND = "NewDeaths"
            case globalTD = "TotalDeaths"
            case globalNR = "NewRecovered"
            case globalTR = "TotalRecovered"
        }
    }
    
    struct ByCountry: Decodable {
        let cntry: String
        let ccode: String
        let slug: String
        let cntryNC: Int
        let cntryTC: Int
        let cntryND: Int
        let cntryTD: Int
        let cntryNR: Int
        let cntryTR: Int
        let date: String
        
        enum CodingKeys: String, CodingKey {
            case cntry = "Country"
            case ccode = "CountryCode"
            case slug = "Slug"
            case cntryNC = "NewConfirmed"
            case cntryTC = "TotalConfirmed"
            case cntryND = "NewDeaths"
            case cntryTD = "TotalDeaths"
            case cntryNR = "NewRecovered"
            case cntryTR = "TotalRecovered"
            case date = "Date"
        }
    }
}

And I will take your advice and rename some of the objects to avoid confusion. Thanks.

FYI, I have been playing with code non-stop to try and fix things, basically simplifying code down to where I could hopefully de-bug it. Here is latest I have:

struct ContentView: View {
    
    @State @ObservedObject var apiCall = APICall()
    
    var body: some View {
        
        NavigationView {
            List {
                VStack {
 //                   Text("Hello Dummy")
 //                   Text("Hello Dummy2")
                    Text(String(apilCall.byCountry[40].cntryNC))
                }
            }
            .navigationBarTitle("COVID Dashboard")
        }
        .onAppear {
            self.apiCall.pullData()
        }

For the above, if I just use the 2 Text statements that are commented out, things are fine.

When I uncomment the Text statement trying to display the apiCall data, I get this error behind it: Use of unresolved identifier 'apilCall'.

Also, I know that declaring the single item from byCountry will only show that one item. I understand that, but have again tried to simply everything to just be able to display a data point, before jumping into a ForEach statement or range, etc…

I know these are stupid questions but again I’m just learning and am totally confused by the model, vs. use of : , vs. use of =, etc…

Really appreciate your help.

– Doxie

well you made a spelling mistake, it should be apiCall not apilCall

i also think you are just making it hard for yourself to make use of the API call. it seems it returns a JSON file. so i suggest using SwiftyJSON instead of the “DataModel” that you created

check out our article for it

Thanks, Francis… will check it out.