Home > Back-end >  Decodeable SWIFT parsing Rest API data error
Decodeable SWIFT parsing Rest API data error

Time:09-19

I am trying to query the marvel API. I believe my decodable is wrong. I have the following code:

struct ReturnedData : Decodable {
    
    var id : Int?
    var name : String?
    var description : String?
}

var savedData : [ReturnedData] = []
    
    let urlString = "https://gateway.marvel.com/v1/public/characters?ts=1&apikey=\(myAPIKey)"
    let url = URL(string: urlString)
    let session = URLSession.shared
    
    let dataTask = session.dataTask(with: url!) { (data, response, error) in
        guard let data = data else {return}
        do {
            let recievedData = try JSONDecoder().decode([ReturnedData].self, from: data)
            self.savedData = recievedData
        } catch {
            print(error)
        }
    }
    dataTask.resume()
}

I am getting the following error message: typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))

according to the documentation, I should get all of the below: Character

id (int, optional): The unique ID of the character resource., name (string, optional): The name of the character. description (string, optional): A short bio or description of the character. modified (Date, optional): The date the resource was most recently modified. resourceURI (string, optional): The canonical URL identifier for this resource. urls (Array[Url], optional): A set of public web site URLs for the resource. thumbnail (Image, optional): The representative image for this character. comics (ComicList, optional): A resource list containing comics which feature this character. stories (StoryList, optional): A resource list of stories in which this character appears events (EventList, optional): A resource list of events in which this character appears. series (SeriesList, optional): A resource list of series in which this character appears.

Also, any tips on how to get the thumbnail image is appreciated.

CodePudding user response:

The error you get is because the model you have does not match the json data. So, try this approach ...to query the marvel API.... In future, copy and paste your json data into https://app.quicktype.io/ this will generate the models for you. Adjust the resulting code to your needs. Alternatively read the docs at: https://developer.marvel.com/docs#!/public/getCreatorCollection_get_0 and create the models by hand from the info given.

class MarvelModel: ObservableObject {
    @Published var results = [MarvelResult]()
    
    let myAPIKey = "xxxx"
    let myhash = "xxxx"
    
    func getMarvels() async {
        guard let url = URL(string: "https://gateway.marvel.com/v1/public/characters?ts=1&apikey=\(myAPIKey)&hash=\(myhash)") else { return }
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            let response = try JSONDecoder().decode(MarvelResponse.self, from: data)
            Task{@MainActor in
                results = response.data.results
            }
        } catch {
            print("---> error: \(error)")
        }
    }
}

struct ContentView: View {
    @StateObject var vm = MarvelModel()

    var body: some View {
        VStack {
            if vm.results.isEmpty { ProgressView() }
            List(vm.results) { item in
                Text(item.name)
            }
        }
        .task {
            await vm.getMarvels()
        }
    }
}

struct MarvelResponse: Decodable {
    let code: Int
    let status, copyright, attributionText, attributionHTML: String
    let etag: String
    let data: MarvelData
}

struct MarvelData: Decodable {
    let offset, limit, total, count: Int
    let results: [MarvelResult]
}

struct MarvelResult: Identifiable, Decodable {
    let id: Int
    let name, resultDescription: String
    let modified: String
    let thumbnail: Thumbnail
    let resourceURI: String
    let comics, series: Comics
    let stories: Stories
    let events: Comics
    let urls: [URLElement]
    
    enum CodingKeys: String, CodingKey {
        case id, name
        case resultDescription = "description"
        case modified, thumbnail, resourceURI, comics, series, stories, events, urls
    }
}

struct Comics: Decodable {
    let available: Int
    let collectionURI: String
    let items: [ComicsItem]
    let returned: Int
}

struct ComicsItem: Identifiable, Decodable {
    let id = UUID()
    let resourceURI: String
    let name: String
}

struct Stories: Decodable {
    let available: Int
    let collectionURI: String
    let items: [StoriesItem]
    let returned: Int
}

struct StoriesItem: Identifiable, Decodable {
    let id = UUID()
    let resourceURI: String
    let name: String
    let type: String
}

struct Thumbnail: Decodable {
    let path: String
    let thumbnailExtension: String
    
    enum CodingKeys: String, CodingKey {
        case path
        case thumbnailExtension = "extension"
    }
}

struct URLElement: Identifiable, Decodable {
    let id = UUID()
    let type: String
    let url: String
}

EDIT-1: if you want something very basic, then try this:

struct ContentView: View {
    @State var results = [MarvelResult]()
    
    var body: some View {
        List(results) { item in
            Text(item.name)
        }
        .onAppear {
            getMarvels()
        }
    }
    
    func getMarvels() {
        let myAPIKey = "xxxx"
        let myhash = "xxxx"
        guard let url = URL(string: "https://gateway.marvel.com/v1/public/characters?ts=1&apikey=\(myAPIKey)&hash=\(myhash)") else { return }
        URLSession.shared.dataTask(with: url) { (data, _, _) in
            if let data = data {
                do {
                    let response = try JSONDecoder().decode(MarvelResponse.self, from: data)
                    DispatchQueue.main.async {
                        self.results = response.data.results
                    }
                }
                catch {
                    print(error)
                }
            }
        }.resume()
    }
}
  • Related