Home > OS >  Swift JSON Nested out put for ListView
Swift JSON Nested out put for ListView

Time:12-22

Iam trying to display the contents of the result. The Data is returned as JSON Array. I created a view model "Stocks" and want to access the "results". Currently it compiles but the data does not show up.

Help would be highly appreciated

import SwiftUI
struct Stocks: Hashable, Codable{
    var results: [Results]
    var status: String
    
    struct Results: Hashable, Codable{
        var ticker: String
        var name: String
        var market: String
        var locale: String
        var primary_exchange: String
        var type: String
        var active: Bool
        var currency_name: String
        var cik: String
        var composite_figi: String
        var share_class_figi: String
        var last_update_utc: String
    }
}


class ViewModel: ObservableObject{
    @Published var stocks: [Stocks] = []
    func fetch(){
        guard let url = URL(string: "https://api.polygon.io/v3/reference/tickers?market=stocks&active=true&apiKey=<apikey>") else{return}
        
        let task = URLSession.shared.dataTask(with: url) {[weak self]data, _, error in
            guard let data = data, error == nil else{
                return
            }
            // Convert JSON
            do{
                let stocks = try JSONDecoder().decode([Stocks].self, from: data)
                DispatchQueue.main.async{
                    self?.stocks = stocks
                }
               
            }
            catch{
                print(error)
            }
        }
        task.resume()
    }
}

struct ContentView: View {
    @StateObject var viewModel = ViewModel()

    
    var body: some View {
        NavigationView{
            List{
                ForEach(viewModel.stocks, id: \.self){resu in
                    ForEach(resu.results, id: \.self){st in
                        Text(st.currency_name)
                    }
                }
                
            }
        }.navigationTitle("Stocks")
        .onAppear{
            viewModel.fetch()
        }
    }
}

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

Here is the Response object:

{
    "results": [
        {
            "ticker": "A",
            "name": "Agilent Technologies Inc.",
            "market": "stocks",
            "locale": "us",
            "primary_exchange": "XNYS",
            "type": "CS",
            "active": true,
            "currency_name": "usd",
            "cik": "0001090872",
            "composite_figi": "BBG000C2V3D6",
            "share_class_figi": "BBG001SCTQY4",
            "last_updated_utc": "2022-12-20T00:00:00Z"
        },
        {
            "ticker": "AA",
            "name": "Alcoa Corporation",
            "market": "stocks",
            "locale": "us",
            "primary_exchange": "XNYS",
            "type": "CS",
            "active": true,
            "currency_name": "usd",
            "cik": "0001675149",
            "composite_figi": "BBG00B3T3HD3",
            "share_class_figi": "BBG00B3T3HF1",
            "last_updated_utc": "2022-12-20T00:00:00Z"
        },

I created a view model "Stocks" and want to access the "results". Currently it compiles but the data does not show up.

CodePudding user response:

The naming of your structs is largely confusing.

According to the JSON you are going to receive one root object containing an array of Stock (supposed to be named in singular form) objects for key results.

And there is a struct member last_update_utc which does not match the key last_updated_utc.

Name your structs this way, I renamed the struct members as camelCase and as constants (let) and you can decode lastUpdatedUtc as Date with the .iso8601 strategy

struct Response: Hashable, Decodable {
    let results: [Stock]
    let status: String
    
    struct Stock: Hashable, Decodable {
        let ticker: String
        let name: String
        let market: String
        let locale: String
        let primaryExchange: String
        let type: String
        let active: Bool
        let currencyName: String
        let cik: String
        let compositeFigi: String
        let shareClassFigi: String
        let lastUpdatedUtc: Date
    }
}

and decode the JSON

class ViewModel: ObservableObject{
    @Published var stocks: [Response.Stock] = []
    func fetch() {
        guard let url = URL(string: "https://api.polygon.io/v3/reference/tickers?market=stocks&active=true&apiKey=<apikey>") else{return}
        
        let task = URLSession.shared.dataTask(with: url) {[weak self] data, _, error in
            if let error { print(error); return }
            // Convert JSON
            do{
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                decoder.dateDecodingStrategy = .iso8601
                let response = try decoder.decode(Response.self, from: data!)
                DispatchQueue.main.async {
                    self?.stocks = response.results
                }
               
            }
            catch{
                print(error)
            }
        }
        task.resume()
    }
}

I even recommend to use async/await

@MainActor
class ViewModel: ObservableObject{
    @Published var stocks: [Response.Stock] = []
    
    func fetch() {
        guard let url = URL(string: "https://api.polygon.io/v3/reference/tickers?market=stocks&active=true&apiKey=<apikey>") else{return}
        Task {
            do {
                let (data, _) = try await URLSession.shared.data(from: url)
                let decoder = JSONDecoder()
                decoder.keyDecodingStrategy = .convertFromSnakeCase
                decoder.dateDecodingStrategy = .iso8601
                let response = try decoder.decode(Response.self, from: data)
                self.stocks = response.results
            } catch {
                print(error)
            }
        }
    }
}
  • Related