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.
Related
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.
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 {
...
}
}
}
func setlLabels(weatherData: NSData) {
var jsonError: NSError?
let json = NSJSONSerialization.JSONObjectWithData(weatherData, options: nil, error: &jsonError) as! NSDictionary
// ...
}
after building in line "let json..." there is an error like "thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)"
I think that's a problem related with NSDicitionary, but I don't know how to solve this.
Screenshot with the error
This error is caused when an unexpected thing happens in your app and the app crashes. To fix this, there is some variable that is not supposed to be nil and you force unwrapped it. (!) Here is what a fixed version would look like:
func setlLabels(weatherData: NSData) {
var jsonError: NSError?
let json = NSJSONSerialization.JSONObjectWithData(weatherData, options: nil, error: &jsonError) as? NSDictionary
// ...
}
As you see, when you remove the ! after as and replace it with ? it will not crash.
I'm running a NSJSONSerialization command with try catches but it is still failing when the command returns a nil. What am I doing incorrect with my try catches?
fatal error: unexpectedly found nil while unwrapping an Optional value happens at the line where z is set. Why doesn't the catch handle this?
func reachForWebsite(){
let url = NSURL(string: "https://myURL")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
do {
let z = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) as! [NSObject: AnyObject]
} catch let myJSONError {
print(myJSONError)
}
}
task!.resume()
}
The do-try-catch process catches errors that are thrown, but is not a generalized exception handling process. As before, as a developer, you are still responsible for preventing exceptions from arising (e.g. the forced unwrapping of the optional NSData).
So, check to make sure data is not nil before proceeding. Likewise, don't use as! in the cast unless you are assured that the cast cannot fail. It is safer to guard against data being nil and perform an optional binding of the JSON to a dictionary:
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { data, response, error in
guard data != nil else {
print(error)
return
}
do {
if let z = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) as? [String: AnyObject] {
// do something with z
}
} catch let parseError {
print(parseError)
}
}
task.resume()
You need to check the values of error and data before trying to give them to NSJSONSerialization to parse – data is probably nil, which is what triggers your crash.
I am new to Xcode and Swift, and I am trying to serialize JSON data from a url using the code below and get the error
'NSJSONReadingOptions' is not convertible to 'NSJSONWritingOptions'
on the line
var jsonResult = NSJSONSerialization.dataWithJSONObject(data, options: NSJSONReadingOptions.MutableContainers, error: nil)!
I cannot see my error, from online sources others use the same code and its works?
let task = session.dataTaskWithURL(loginUrl!, completionHandler: { (data, response, error) -> Void in
if error != nil {
}else {
var jsonResult = NSJSONSerialization.dataWithJSONObject(data, options: NSJSONReadingOptions.MutableContainers, error: nil)!
println(jsonResult)
}
})
task.resume();
Read the docs for dataWithJSONObject. The options parameter needs to be a value from the NSJSONWritingOptions enum, not the NSJSONReadingOptions enum.
But if your goal here is to convert an NSData object to an NSArray or NSDictionary, then the problem is that you are calling the wrong method. You want to use JSONObjectWithData, not dataWithJSONObject.