zeem
zeem

Reputation: 105

Trouble with JSON Parsing in Swift

Having a slight trouble in saving my decoded JSON data into the struct, not sure what I'm doing wrong here.

What I'm trying to do is call the sneaker database and retrieve the results and display them in a view, i can see the JSON call is working with the print but now stuck on how to get this in a way I can display it into a view.

JSON

{
  "count": 1,
  "results": [
    {
      "id": "029ddc1d-a64b-469b-b9df-bd21c84a608e",
      "brand": "Jordan",
      "colorway": "Deep Ocean/Sail-Cement Grey-Fire Red",
      "gender": "men",
      "name": "SE Sashiko",
      "releaseDate": "2020-12-05",
      "retailPrice": 190,
      "shoe": "Jordan 4 Retro",
      "styleId": "CW0898-400",
      "title": "Jordan 4 Retro SE Sashiko",
      "year": 2020,
      "media": {
        "imageUrl": "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=700&h=500&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370",
        "smallImageUrl": "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=300&h=214&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370",
        "thumbUrl": "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=140&h=100&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370"
      }
    }
  ]
}

Model:

struct APIResponse: Decodable {
    enum CodingKeys: String, CodingKey {
        case shoeResults = "results"
    }

    let shoeResults: [Shoe]
}


struct Shoe: Decodable {
    public var id: String
    public var brand: String
    public var colorway: String
    public var gender: String
    public var name: String
    public var releaseDate: String
    public var retailPrice: Int
    public var shoe: String
    public var styleId: String
    public var title: String
    public var year: Int
    public var media: mediaData
    
    enum CodingKeys: String, CodingKey {
            case id = "id"
            case brand = "brand"
            case colorway = "colorway"
            case gender = "gender"
            case name = "name"
            case releaseDate = "releaseDate"
            case retailPrice = "retailPrice"
            case shoe = "shoe"
            case styleId = "styleId"
            case title = "title"
            case year = "year"
            case media = "media"

        }
}

struct mediaData: Decodable {
    var imageUrl: String
    var smallImageUrl: String
    var thumbUrl: String
    

}

Shoefetcher class:

public class ShoeFetcher: ObservableObject {

    
    init(){
        load()
    }
    
    func load() {
        let url = URL(string: "https://api.thesneakerdatabase.com/v1/sneakers?limit=10&styleId=cw0898-400")!
    
        URLSession.shared.dataTask(with: url) {(data,response,error) in
            do {
                if let d = data {
                    let decodedLists = try JSONDecoder().decode(APIResponse.self, from: d)
                    DispatchQueue.main.async {
                       
                        print(decodedLists)
                    }
                }else {
                    print("No Data")
                }
            }catch DecodingError.keyNotFound(let key, let context) {
                Swift.print("could not find key \(key) in JSON: \(context.debugDescription)")
            } catch DecodingError.valueNotFound(let type, let context) {
                Swift.print("could not find type \(type) in JSON: \(context.debugDescription)")
            } catch DecodingError.typeMismatch(let type, let context) {
                Swift.print("type mismatch for type \(type) in JSON: \(context.debugDescription)")
            } catch DecodingError.dataCorrupted(let context) {
                Swift.print("data found to be corrupted in JSON: \(context.debugDescription)")
            } catch let error as NSError {
                NSLog("Error in read(from:ofType:) domain= \(error.domain), description= \(error.localizedDescription)")
            }
        
            
        }.resume()
         
    }
}

output of decodedlists:

APIResponse(shoeResults: [Sneakers.Shoe(id: "029ddc1d-a64b-469b-b9df-bd21c84a608e", brand: "Jordan", colorway: "Deep Ocean/Sail-Cement Grey-Fire Red", gender: "men", name: "SE Sashiko", releaseDate: "2020-12-05", retailPrice: 190, shoe: "Jordan 4 Retro", styleId: "CW0898-400", title: "Jordan 4 Retro SE Sashiko", year: 2020, media: SneakerNews.mediaData(imageUrl: "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=700&h=500&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370", smallImageUrl: "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=300&h=214&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370", thumbUrl: "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=140&h=100&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370"))])

Edit:

updated the shoefetcher class:

public class ShoeFetcher: ObservableObject {
    
         @Published var shoeResults: [Shoe] = [] //added

        init(){
            load()
        }
        
        func load() {
            let url = URL(string: "https://api.thesneakerdatabase.com/v1/sneakers?limit=10&styleId=cw0898-400")!
        
            URLSession.shared.dataTask(with: url) {(data,response,error) in
                do {
                    if let d = data {
                        let decodedLists = try JSONDecoder().decode(APIResponse.self, from: d)
                        DispatchQueue.main.async {
                           
                     self.shoeResults = decodedLists.shoeResults //added
                   
                    print(self.shoeResults)
                        }
                    }else {
                        print("No Data")
                    }
                }catch DecodingError.keyNotFound(let key, let context) {
                    Swift.print("could not find key \(key) in JSON: \(context.debugDescription)")
                } catch DecodingError.valueNotFound(let type, let context) {
                    Swift.print("could not find type \(type) in JSON: \(context.debugDescription)")
                } catch DecodingError.typeMismatch(let type, let context) {
                    Swift.print("type mismatch for type \(type) in JSON: \(context.debugDescription)")
                } catch DecodingError.dataCorrupted(let context) {
                    Swift.print("data found to be corrupted in JSON: \(context.debugDescription)")
                } catch let error as NSError {
                    NSLog("Error in read(from:ofType:) domain= \(error.domain), description= \(error.localizedDescription)")
                }
            
                
            }.resume()
             
        }
    }

added a ContentView to just see if I can display something off the JSON file.

struct ContentView: View {
   @ObservedObject var fetcher = ShoeFetcher()
    
    @State var Shoes: Shoe
    
    var body: some View {
        
        
                VStack {
                    Text("Shoes")
                    Text(Shoes.brand)
        }
        
    }
}

errors I'm getting is Missing argument for a parameter 'Shoes' in call in my SneakersApp.swift file

import SwiftUI

@main
struct SneakersApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView() //error here
        }
    }
}

I have a feeling I need to initialise the JSON variables but cant seem to workout where/how I do that

Upvotes: 0

Views: 76

Answers (1)

jnpdx
jnpdx

Reputation: 52387

You've done all of the hard work of getting the data from the API and decoding it into structs.

To display it in a view, you could do something like this:

struct ContentView : View {
    @StateObject var fetcher = ShoeFetcher()
    
    var body: some View {
        VStack {
            if let response = fetcher.apiResponse {
                ForEach(response.shoeResults, id: \.id) { shoe in
                    Text(shoe.name)
                    Text(shoe.brand)
                    Text(shoe.title)
                    Text("\(shoe.year)")
                    Text("\(shoe.retailPrice)")
                }
            }
        }
    }
}

Your current API call is only returning one Shoe in the array, so the ForEach call isn't doing much, but it would certainly be important if you had more results.

Upvotes: 2

Related Questions