Can't read 'data' object returned from URLRequest

Hello,

I am learning to use Swift and am trying to request data from the USDA Food Data Central REST API. The problem I am having is that when I try to parse the response, the 'data' object can't be read. It's not nil (tested for that) and using .count, it seems like there is data there (27531 bytes). I just can't get it to pass through the "let data = data" command.

I format the request and send it off to the API without problems, and I can read the 'response' object (there is not an 'error' object). And if I print the URLRequest.description and copy and paste it into Safari, I get a page of JSON content back (so I know the request is formatted correctly and works with the API.) I also know form the response code (200) that the API says it is successfully sending.

I also found that the response says the data is encoded in gzip. I set the "Accept-Encoding" to "identity", but that didn't solve the problem.

Any idea why this might be happening? I have been working on this for 3 days now and am hoping someone can help me figure it out.

The code is shown below (sorry for the ugly listing, I have never posted here before and can't figure out how to paste in a well-formatted code sample from Xcode...):


        let API_Key: String = "48oWHEcKXeCZuuh9SGqHfhn8ttL3HcWjTDZn4gmo"

        let fdcFoodItemSearchKeywords: String = "apple%20fuji"

        

        let fdcResourceString = "https://api.nal.usda.gov/fdc/v1/foods/search?api_key=\(API_Key)&query=\(fdcFoodItemSearchKeywords)&dataType=Foundation"

    

        guard let fdcResourceURL = URL(string: fdcResourceString) else {

            print("Invalid URL")

            return

        }

        

        var request = URLRequest(url: fdcResourceURL)

        request.addValue("application/json", forHTTPHeaderField: "Accept")

//        request.addValue("identity", forHTTPHeaderField: "Accept-Encoding")

        request.httpMethod = "GET"

        

        print("The request variable = \(request.description)")

        print("The request variable http Header is: \(String(describing: request.allHTTPHeaderFields))")

        

        URLSession.shared.dataTask(with: request) { data, response, error in

            

            print("The data has \(String(describing: data?.count)) bytes and is: \n")

            debugPrint(data ?? "Can't print data")

            

            print("The response is: \n")

            debugPrint(response ?? "Can't print response")

    

            if let data = data {

                if let decodedResponse = try? JSONDecoder().decode(FDCResponse.self, from: data) {

                    DispatchQueue.main.async {

                        self.FDCResults = decodedResponse.FDCResults

                    }

                    return

                }

            }

            

            print("FDC Data Fetch Failed: \(error?.localizedDescription ?? "Unknown error")")

            print(fdcResourceURL.description)

        }.resume()
if let decodedResponse = try? JSONDecoder().decode(FDCResponse.self, from: data) {

decodedResponse might be nil if an JSON decoding failure occurs. Try replacing this line with let decodedResponse = try! JSONDecoder().decode(FDCResponse.self, from: data) and see if your app crash. If it crash, then you need to change the code of FDCResponse.

You are receiving data, and the "let data = data" command is okay.

So presumably the failure occurs on:

if let decodedResponse = try? JSONDecoder().decode(FDCResponse.self, from: data) {

You don't include a definition of FDCResponse, so I can't test that.

However, you can try what I did:

if let data = data {
    if let dataString = String(data: data, encoding: .utf8) {
        print("got dataString: \n\(dataString)")
    }
    // ...

Which prints the incoming json, which looks like this:

{"totalHits":5,"currentPage":1,"totalPages":1,"pageList":[1],"foodSearchCriteria":{"dataType":["Foundation"],"query":"apple fuji","generalSearchInput":"apple fuji","pageNumber":1,"numberOfResultsPerPage":50,
...

So you can compare that json with your FDCResponse, to see where an error might arise.

Your code goes through to print("FDC Data Fetch Failed:...) even when if let data = data is working as expected, when try? ends with nil. Generally I do not recommend to use try? where you do not understand all the possibilities when error is thrown.

Use do-try-catch and never use try?.

Please try something like this and consider what is causing the problem:

        let API_Key: String = "..." // You should not make API_Key public.
        let fdcFoodItemSearchKeywords: String = "apple%20fuji"

        let fdcResourceString = "https://api.nal.usda.gov/fdc/v1/foods/search?api_key=\(API_Key)&query=\(fdcFoodItemSearchKeywords)&dataType=Foundation"

        guard let fdcResourceURL = URL(string: fdcResourceString) else {
            print("Invalid URL")
            return
        }

        var request = URLRequest(url: fdcResourceURL)
        request.addValue("application/json", forHTTPHeaderField: "Accept")
        request.httpMethod = "GET"

        print("The request variable = \(request.description)")
        print("The request variable http Header is: \(String(describing: request.allHTTPHeaderFields))")

        URLSession.shared.dataTask(with: request) { data, response, error in
            if let error = error {
                print("FDC Data Fetch Failed: \(error)")
                return
            }
            guard let data = data else {
                print("data is nil")
                return
            }

            print("The data has \(data.count) bytes and is: \n")
            let responseText = String(data: data, encoding: .utf8) ?? "*unknow encoding*"
            print("The response is: \(responseText)")

            do {
                let decodedResponse = try JSONDecoder().decode(FDCResponse.self, from: data)
                DispatchQueue.main.async {
                    self.FDCResults = decodedResponse.FDCResults
                }
                return
            } catch {
                print("FDC Data Decoding Failed: \(error)")
            }
        }.resume()

Following on from my original answer...

The json response looks quite complicated, so I made a very simple test for the decoding:

struct Food: Decodable {
    let description: String
}

struct FDCResponse: Decodable {
    let foods: [Food]
}

You have:

self.FDCResults = decodedResponse.FDCResults

...and I don't know the structure of your FDCResults, so I just:

print("decodedResponse: \(decodedResponse)") // FDCResponse

...and I got:

decodedResponse: FDCResponse(foods: [DataTest.Food(description: "Apples, fuji, with skin, raw"), DataTest.Food(description: "Apples, gala, with skin, raw"), DataTest.Food(description: "Apples, honeycrisp, with skin, raw"), DataTest.Food(description: "Apples, granny smith, with skin, raw"), DataTest.Food(description: "Apples, red delicious, with skin, raw")])
Can't read 'data' object returned from URLRequest
 
 
Q