Considering the following gist: https://gist.github.com/anonymous/0703a591f97fa9f6ad35b29234805fbd - no efficient way to display all of the relevant information as the files are of considerable length. Apologies
I have some nested JSON objects which I'm trying to decode.
{
"establishments":[
{ ... }
],
"meta":{ ... },
"links":[
]
}
^ The establishments array contains Establishment objects
My code runs successfully up until line 7 of ViewController.swift where I'm actually utilising Swift 4's JSONDecode() functionality.
ViewController.swift
let jsonURLString = "http://api.ratings.food.gov.uk/Establishments?address=\(self.getPostalCode(place: place))&latitude=\(place.coordinate.latitude.description)&longitude=\(place.coordinate.longitude.description)&maxDistanceLimit=0&name=\(truncatedEstablishmentName[0].replacingOccurrences(of: "'", with: ""))"
guard let url = URL(string: jsonURLString) else { print("URL is invalid"); return }
URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
guard let data = data, error == nil, response != nil else {
print("Something went wrong")
return
}
//print("test")
do {
let establishments = try JSONDecoder().decode(Establishments.self, from: data)
print(establishments)
} catch {
print("summots wrong")
}
}).resume()
The output I get for the mapped JSON object seen in Establishment.swift is: summots wrong - on line 11 of ViewController.swift.
I believe the reason it is not working as expected is because the JSON format is like so:
{
"establishments":[ ... ],
"meta":{ ... },
"links":[ ]
}
but I don't know if my class structure/ what I'm doing in my view controller satisfies this layout criteria.
Can anyone spot where I'm going wrong? I think it's something very basic but I can't seem to get my head around it
I also understand that I'm not supposed to name attributes of classes with a capital letter - apologies for breaking any conventions
EDIT: I printed the error instead of my own statement and it is as follows:
instead of: print("summots wrong")
I wrote: print(error)
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.})))
As Hamish correctly pointed out, I'm not receiving correct JSON data from the GET request. In fact, I'm not receiving any JSON at all. The API I'm using defaults to XML format when the x-api-version is not set to 2.
Instead of //print("test") in my ViewController.swift file, I used the recommended: print(String(data: data, encoding: .utf8)) line
I used Postman with the following headers: x-api-version: 2, accept: application/json and content-type: application/json to retrieve my JSON output. If I use the same GET string (with all of the parameters filled out properly) in my browser window, I will receive an error stating: The API 'Establishments' doesn't exist in an XML format.
Is there any way for me to send a header with this URL?
EDIT 2: I have changed the request to use URLRequest instead of just URL.
Here is my updated ViewController.swift code:
let jsonURLString = "http://api.ratings.food.gov.uk/Establishments?address=\(self.getPostalCode(place: place))&latitude=\(place.coordinate.latitude.description)&longitude=\(place.coordinate.longitude.description)&maxDistanceLimit=0&name=\(truncatedEstablishmentName[0].replacingOccurrences(of: "'", with: ""))"
guard let url = URL(string: jsonURLString) else { print("URL is invalid"); return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("x-api-version", forHTTPHeaderField: "2")
request.addValue("accept", forHTTPHeaderField: "application/json")
request.addValue("content-type", forHTTPHeaderField: "application/json")
URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
guard let data = data, error == nil, response != nil else {
print("Something went wrong")
return
}
print(String(data: data, encoding: .utf8))
// do {
// let establishments = try JSONDecoder().decode(Establishments.self, from: data)
// print(establishments)
// } catch {
// print(error)
// }
}).resume()
I'm getting the error:
HTTP Error 400. The request has an invalid header name
I did some checks, it the request just ignores the x-api-version header value of 2.
Any ideas?
EDIT 3:
The correct format for addValue should have been:
request.addValue("2", forHTTPHeaderField: 'x-api-version and so on.
I am now faced with an issue with my class declaration, specifically in my 'meta' class.
Meta.(CodingKeys in _DF4B170746CD5543281B14E0B0E7F6FB).dataSource], debugDescription: "Expected String value but found null instead.", underlyingError: nil
It's complaining because my class declaration is not matching the data it's expecting from the JSON object.
In my JSON response (refer to the gist) there are two meta objects, one contains the value of null for dataSource and the other being Lucene.
Is there any way for me to accept 'null' values as well as strings in my class initialisers, all while keeping in compliance with the Decodable protocol?
That section of the JSON response is dead to me, do I still need to create objects for it or can I get away with just ignoring them? - without declaring anything. Or do I specifically need to note down everything in the JSON response to be able to use the data?
{or[– try printing outString(data: data, encoding: .utf8)and seeing what you get (it may not even be valid UTF8).URLRequestinstead ofURLto create yourdataTaskrequest.addValue