Google Books API failing to decode JSON

167 views Asked by At

I'm trying to display data from the Google Books API. The code behaves correctly with certain search queries but not with others. Below the search query is "weather," the console prints the correct JSON along with the "JSON failed to decode" message.

However, when I replace q=weather with q=hello I get the correct results displayed and the error message is not printed. My Response struct seems to be correct.

import SwiftUI

struct Response: Codable {
    let kind: String
    let totalItems: Int
    let items: [Result]
}

struct Result: Codable, Identifiable { 
    var id: String  
    var etag: String
    var selfLink: String
    var volumeInfo: VolumeInfo
}

struct VolumeInfo: Codable {
    let title: String
    let authors: [String]
    let publisher, publishedDate, description: String
    let pageCount: Int
    let imageLinks: ImageLinks
}

struct ImageLinks: Codable {
    let thumbnail: String
}

struct IndustryIdentifier: Codable {
    let type: TypeEnum
    let identifier: String
}

enum TypeEnum: String, Codable {
    case isbn10 = "ISBN_10"
    case isbn13 = "ISBN_13"
}

struct BookSearchView: View {
    
    @State private var results = [Result]()
    
    var body: some View {
        VStack {

            List(results) { item in 
                
                HStack {
                    
                    let baseURL = "https://books.google.com/books/content?id="
                    let bookID = item.id
                    let urlString = "\(baseURL)\.  (bookID)&printsec=frontcover&img=1&zoom=1&edge=curl&source=gbs_api"
                     
                    // put the url strings together with the bookID and convert to a URL object
                    let ImageURL = URL(string: urlString)
                                        
                    AsyncImage(url: ImageURL)
                    
                    VStack(alignment: .leading) {
                        Text(item.volumeInfo.title)
                            .font(.headline)
                        Text("Authors: \(item.volumeInfo.authors.joined(separator: ", "))")
                            .font(.subheadline)

                        Divider()
                        Text("\(item.id)")
                    }
                }
            }
            .task {
                await loadData()
            }
        }
    }
    
    func loadData() async {
        guard let url = URL(string: "https://www.googleapis.com/books/v1/volumes?q=weather") else {
            print("Invalid URL")
            return
        }
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            print("Received Data: \(String(data: data, encoding: .utf8) ?? "Data is empty")")
            if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
                results = decodedResponse.items
                print("Results Count: \(results.count)")
            } else {
                print("JSON failed to decode")
            }
        } catch {
            print("Error: \(error)")
        }
    }
    
}```

1

There are 1 answers

0
workingdog support Ukraine On

Read the API docs, and ensure that each optional property has a ? after it. For example try:

struct VolumeInfo: Codable {
    let title: String
    let authors: [String]?  // <-- here
    let publisher, publishedDate, description: String?  // <-- here
    let pageCount: Int?  // <-- here
    let imageLinks: ImageLinks?  // <-- here
}

and

func loadData() async {
    guard let url = URL(string: "https://www.googleapis.com/books/v1/volumes?q=weather") else {
        print("Invalid URL")
        return
    }
    do {
        let (data, _) = try await URLSession.shared.data(from: url)
        let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
        results = decodedResponse.items
    } catch {
        print("Error: \(error)")  // <--- here important, NO try?
    }
}

Note, you will have to adjust Text("Authors: ....