0

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

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you so much for you help, seems I got close but not all the way there, I tried your solution kept getting errors, not sure where I'm going wrong. I've edited the code slightly, not sure if thats going in the right direction or not, added changed to main post
Your updated code doesn't match what is in this answer - you don't need separate @State inside the view for your shoes, that is owned by the fetcher, which should be a @StateObject and not an observed object, since it is owned by the content view. Looking at your updated code above, you probably want ForEach(fetcher.shoeResults, id: \.id) instead of what's written above.
Thank you so much, found out where I was going wrong, I have it displaying correctly now! really appreciate it

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.