SwiftUI JSON decoder throws empty data - json

I'm learning SwiftUI in XCode and I'm stuck with this problem with some strange conditions, I'm using two iPhones for debug, one of them is an iPhone XS where I can't find a solution to fix it, the only way to solve it is modify some code on my project and then rebuild it, but one day after the error comes again.
This is the code I'm using:
func loadJSON<T: Decodable>(_ filename: String) -> T {
let data: Data
guard let file = URL(string: filename)
else {
fatalError("Couldn't find \(filename).")
}
do {
data = try Data(contentsOf: file)
}
catch {
//fatalError("Couldn't load \(filename):\n\(error)")
data = Data.init() //if I remove this, the exception comes from this line ▲
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
}
catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)") //<- error comes here
}
}
And this is the debug output:
Thread 2: Fatal error: Couldn't parse http://**myURL** as Array<ItemClass>:
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Unable to parse empty data." UserInfo={NSDebugDescription=Unable to parse empty data.})))
And in the other iPhone (is an iPhone 8) this problem does not happen, both of them have the same version of iOS and the same Development profile installed.

Related

The Data Couldn't Be Read Because It Isn't in The Correct Format?

I'm pretty sure my model is correct based on my data, I cannot figure out why I am getting the format error?
JSON:
{
"1596193200":{
"clientref":1,
"type":"breakfast"
},
"1596200400":{
"clientref":0,
"type":"lunch"
},
"1596218400":{
"clientref":2,
"type":"dinner"
}
}
model:
struct Call: Decodable {
let clientref: Int?
let type: String?
}
edit updated question with the code for decoding the json data from the URL:
class CallService {
static let shared = CallService()
let CALLS_URL = "url.com/Calls.json"
func fetchCalls(completion: #escaping ([Call]) -> ()) {
guard let url = URL(string: CALLS_URL) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
// handle error
if let error = error {
print("Failed to fetch data with error: ", error.localizedDescription)
return
}
guard let data = data else {return}
do {
let call = try JSONDecoder().decode([Call].self, from: data)
completion(call)
} catch let error {
print("Failed to create JSON with error: ", error.localizedDescription)
}
}.resume()
}
}
I strongly suggest to learn how to debug: it includes where to look, what info to get, where to get them, etc, and at the end, fix it.
That's a good thing that you print the error, most beginner don't.
print("Failed to create JSON with error: ", error.localizedDescription)
=>
print("Failed to create JSON with error: ", error)
You'll get a better idea.
Second, if it failed, print the data stringified. You're supposed to have JSON, that's right. But how often do I see question about that issue, when it fact, the answer wasn't JSON at all (the API never stated it will return JSON), the author were facing an error (custom 404, etc.) and did get a XML/HTML message error etc.
So, when the parsing fails, I suggest to do:
print("Failed with data: \(String(data: data, encoding: .utf8))")
Check that the output is a valid JSON (plenty of online validators or apps that do that).
Now:
I'm pretty sure my model is correct based on my data,
Well, yes and no.
Little tip with Codable when debuting (and not using nested stuff): Do the reverse.
Make your struct Codable if it's not the case yet (I used Playgrounds)
struct Call: Codable {
let clientref: Int?
let type: String?
}
do {
let calls: [Call] = [Call(clientref: 1, type: "breakfast"),
Call(clientref: 0, type: "lunch"),
Call(clientref: 2, type: "dinner")]
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted]
let jsonData = try encoder.encode(calls)
let jsonStringified = String(data: jsonData, encoding: .utf8)
if let string = jsonStringified {
print(string)
}
} catch {
print("Got error: \(error)")
}
Output:
[
{
"clientref" : 1,
"type" : "breakfast"
},
{
"clientref" : 0,
"type" : "lunch"
},
{
"clientref" : 2,
"type" : "dinner"
}
]
It doesn't look like. I could only used an array to put various calls inside a single variable, and that's what you meant for decoding, because you wrote [Call].self, so you were expecting an array of Call. We are missing the "1596218400" parts. Wait, could it be a dictionary at top level? Yes. You can see the {} and the fact it uses "keys", not listing one after the others...
Wait, but now that we printed the full error, does it make more sense now?
typeMismatch(Swift.Array<Any>,
Swift.DecodingError.Context(codingPath: [],
debugDescription: "Expected to decode Array<Any> but found a dictionary instead.",
underlyingError: nil))
Fix:
let dictionary = try JSONDecoder().decode([String: Call].self, from: data)
completion(dictionary.values) //since I guess you only want the Call objects, not the keys with the numbers.
From the code you provided it looks like you are trying to decode an Array<Call>, but in the JSON the data is formatted as a Dictionary<String: Call>.
You should try:
let call = try JsonDecoder().decode(Dictionary<String: Call>.self, from: data)

Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed

I'm trying to decode json from google drive attachment but it shows the following error:
responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(error: Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.}))
The JSON:
{
"price" : 5000,
"item" : "Some item"
}
Models:
struct Things: Codable {
let price: Int,
let item: String
}
Request:
Alamofire.request("https://drive.google.com/linkhere").responseJSON { (response) in
switch response.result {
case .failure(let error):
print("error : \(error)")
case .success:
print("success!")
let decoder = JSONDecoder()
let things = try! decoder.decode(Things.self, from: response.data!)
print("things.price : \(things.price)")
print("things.item : \(things.item)")
}
}
This only occurs when i fetch json from google drive attachment (the json is valid).
Are there any difference when fetching json from google drive attachment?
If your response goes in failure case that means api call is having an issue, if your response goes in success state that means it may chances your decoding have some issue.
Failure case:
Check api in postman
Check api method(i.e GET or POST)
Check encoding or content type "application/json"

Unwrapping JSON from Itunes API - IOS App

Having an issue with my program. I would appreciate it if someone could help out. I have tried for weeks to parse the JSON files fetched from the iTunes API
(itunes.apple.com/search?term=song+you+want+to+search&entity=songTrack).
However, my answers are never displayed on my tableview and an error always shows up in the terminal:
"2017-11-14 17:25:28.809190+0100 Itunes Learning[32409:6240818] [MC] Lazy loading NSBundle MobileCoreServices.framework
2017-11-14 17:25:28.810264+0100 Itunes Learning[32409:6240818] [MC] Loaded MobileCoreServices.framework
2017-11-14 17:25:28.823734+0100 Itunes Learning[32409:6240818] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /Users/cyprianzander/Library/Developer/CoreSimulator/Devices/D52FD9D5-B6E4-4CE0-99E4-6E0EE15A680D/data/Containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
Could not cast value of type '__NSDictionaryI' (0x103b911d8) to 'NSArray' (0x103b90d28).
2017-11-14 17:25:29.875534+0100 Itunes Learning[32409:6240900] Could not cast value of type '__NSDictionaryI' (0x103b911d8) to 'NSArray' (0x103b90d28).
(lldb) "
This is approximately how the JSON file is set up:
{“resultCount” : 50, “results”: [ {“trackName”:”name”, ”artistName”:”name2”}, {“trackName”:”name3”, “artistName”:”name4”} ] }
(An array of objects inside an array - meaning the first object is on the far outside).
I have tried my function with another API, which did work. I have the feeling that the main reason as to why this happens, is because the iTunes API JSON file is very complex. It is an assortment of very long objects inside an array, which is inside a smaller list of objects. However, the other one was only and array of objects.
Here is my code: (I have noticed that the problem occurs while parsing the data I need. The only thing I need to know is how to properly unwrap my JSON file)
func parseData(searchTerm: String) {
fetchedSong = []
let itunesSearchTerm = searchTerm.replacingOccurrences(of: " ", with: "+", options: .caseInsensitive, range: nil)
let escapedSearchTerm = itunesSearchTerm.addingPercentEncoding(withAllowedCharacters: [])!
let urlString = "https://itunes.apple.com/search?term=\(escapedSearchTerm)&entity=song"
let url = URL(string: urlString)!
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
// If there is an error in the web request, print it to the console
print(error)
return
}
else {
do {
let fetchedData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSArray
print(fetchedData)
for eachFetchedSong in fetchedData {
let eachSong = eachFetchedSong as! [String: Any]
let song = eachSong["trackName"] as! String
let artist = eachSong["artistName"] as! String
self.fetchedSong.append(songs(song: song, artist : artist))
}
self.SongTableView.reloadData()
}
catch {
print("An error occured while decoding the JSON object")
}
}
}.resume()
}
If anyone could help me, I would be extremely happy, especially because I have been stuck with this for three weeks, continuously trying different techniques (this one seemed the most successful).
Your JSON data is not an array. It is a dictionary with two key/value pairs. The first is the key "resultCount" with a value of 50, and the second is the key "results" with an array as its value.
Never use as! when parsing JSON, since this will crash your app if you get an unexpected result. Don't use .mutableLeaves unless you can explain to us what it does and why you need it. Don't use NSArray in your Swift code.
Handling one error and crashing on others is pointless. I'd write
if let fetchedDict = try? JSONSerialization(...) as? [String:Any],
let fetchedArray = fetchedDict ["results"] as? [[String:Any]] {
for dict in fetchedArray {
if let song = dict ["trackName"] as? String,
let artist = dict ["artistName"] as? String {
...
}
}
}

Errors in do catch statement in swift

I'm trying to explore this new (for me) language, and I'm making an app that like many others retrieve some json data from a server.
in this function (from a tutorial I found) I get 4 errors, and I'm unable to fix it:
func json_parseData(data: NSData) -> NSDictionary? {
do {
let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)
print("[JSON] OK!")
return (json as? NSDictionary)
}catch _ {
print("[ERROR] An error has happened with parsing of json data")
return nil
}
}
the first one is at the "try", xcode suggest me to fix it using "try;"
the others at the "catch" and are this others:
Braced block of statement is an unused closure
Type of expression is ambiguous without more context
Expected while in do-while-loop
please help me to understand
It seems that you are using Xcode 6.x and Swift 1.x where the do-try-catch syntax is not available (only Xcode 7 and Swift 2)
This code has equivalent behavior:
func json_parseData(data: NSData) -> NSDictionary? {
var error: NSError?
// passing the error as inout parameter
// so it can be mutated inside the function (like a pointer reference)
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainerserror: &error)
if error == nil {
print("[JSON] OK!")
return (json as? NSDictionary)
}
print("[ERROR] An error has happened with parsing of json data")
return nil
}
Note: as of Xcode 7 beta 6 you can also use try? which returns nil if an error occurs.

Parsing JSON data (from URL) in Swift

I'm trying to retrieve some data from an URL thanks to JSON. Here's my swift code:
// get symbol asked
let symbol = symbolField.text!
// define URL
let url = NSURL(string: "http://yahoojson.gobu.fr/symbol.php?symbol=\(symbol)")!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers)
print(jsonResult)
} catch {
print("Error JSON")
}
}
}
task.resume()
Everything seems to work fine, but the "do-try-catch" always prints "Error JSON". My code seems unable to convert my URL content into actual JSON. Any idea what I am doing wrong?
The URL returns html/javascript not pure json.
Paste the URL into your browser and look at the source code.
A side note: replace
print("Error JSON")
with
print(error)
to get more specific error information