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.
Related
This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed 4 years ago.
I am developing an application that json parse. I'm using the AlertView for json messages. But I can not access the jsonmessage variable in the AlertView. if I put the AlertView in DO I get this error: "libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)"
Sorry for my bad English. This is my code:
request.httpBody = postParameters.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with:request as URLRequest){
data, response, error in
if error != nil{
print("error is \(String(describing: error))")
return;
}
do {
let myJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = myJSON {
var jsonmessage : String!
jsonmessage = parseJSON["message"] as! String?
print(jsonmessage)
}
} catch {
}
}
task.resume()
let alert = UIAlertController(title: "Alert", message: jsonmessage /*not accessible*/ , preferredStyle: .alert)
alert.addAction(UIAlertAction(title:"Ok", style:UIAlertActionStyle.default, handler:{ (UIAlertAction) in
_ = self.navigationController?.popToRootViewController(animated: true)
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "load"), object: nil)
}))
self.present(alert, animated: true, completion: nil)
As you have discovered jsonMessage is not accessible from where you are trying to access it.
This is because of a few reasons:
The request is an asynchronous task that runs in the background and takes some time to complete. So the alert view code actually runs before the jsonMessage is returned
The variable jsonMessage is also out of scope where you are trying to call it.
To help explain:
let task = URLSession.shared.dataTask(with:request as URLRequest){
data, response, error in
let fakeMessage = "hi"
// data, response, error and fakeMessage only exist here upto the closing bracket.
}
task.resume()
// fakeMessage doesn't exist here at all.
To resolve your issue you can either present your alert from within the closure (where I have put fakeMessage) or you se a completionHandler to return jsonMessage when it is ready and then show the alert.
Method 1
let task = URLSession.shared.dataTask(with:request as URLRequest){
data, response, error in
if error != nil{
print("error is \(String(describing: error))")
return;
}
do {
let myJSON = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = myJSON {
var jsonmessage : String!
jsonmessage = parseJSON["message"] as! String?
DispatchQueue.main.async {
// some helper function to show a basic alert
self.presentAlert(title: "Response", message: jsonMessage)
}
}
} catch {
}
}
task.resume()
Method 2
func fetchSomething(completion: #escaping (String -> Void)?) {
// setup request
let task = URLSession.shared.dataTask(with:request as URLRequest){
data, response, error in
let fakeMessage = "hi"
completion(fakeMessage)
}
task.resume()
}
then you can use it this way
self.fetchSomething { response in
DispatchQueue.main.async {
// some helper function to show a basic alert
self.presentAlert(title: "Response", message: jsonMessage)
}
}
First of all, you are doing a request that's asynchronous. The jsonmessage variable is set once you get the response. But you create the UIAlertController before this happens. I'm guessing you wish to display the alert once you get a response?
Also, you can't access the jsonmessage variable outside of its scope. To fix this, move var jsonmessage : String! so that it belongs to the same scope as the UIAlertController.
You should be able to move your alert into the do catch statement, but you have to make sure that the alert is displayed on the main thread.
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.
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 trying to use an API to get json data every 0.5 seconds. The API allows thousands of requests per second however sometimes when running the application, I get this crash:
fatal error: unexpectedly found nil while unwrapping an Optional value
This is the code I am using to get the json data.
var url : String = "URL.COM"
var request : NSMutableURLRequest = NSMutableURLRequest
request.URL = NSURL(string: url)
request.HTTPMethod = "GET"
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse?, data: NSData?, error: NSError?) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers, error: error) as NSDictionary
Can anyone advise me how to prevent this? I understand why the data might be nil occasionally but I want the program to keep checking regardless of if it is nil or not, as it will soon be something other than nil.
You should add this, before the sterilization:
if data == nil
{
println("data is nil")
return
}
The problem lays in data. It can be nil, but you're unwrapping it using !, which means: "I know there's some data inside here, unwrap it". The problem is when there's no data.
To solve it, use this pattern in Swift:
if let myData = data {
// do something using myData. If data is nil, doen't even enter here
}
Complete code:
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse?, data: NSData?, error: NSError?) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
if let myData = data {
let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(myData, options:NSJSONReadingOptions.MutableContainers, error: error) as NSDictionary
}
})
I'm trying to learn Swift and am struggling with understanding a piece about handling JSON and get "fatal error: unexpectedly found nil while unwrapping an Optional value" from using this code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
let urlPath = "http://api.worldweatheronline.com/free/v2/marine.ashx?key=45e8e583134b4371a2fa57a9e5776&format=xml&q=45,-2"
let url: NSURL = NSURL(string: urlPath)!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
println("Task completed")
if ((error) != nil) {
println(error)
} else {
var err: NSError?
println("URL: \(url)")
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
println(jsonResult)
}
})
task.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The API key I'm using is free so don't worry about using it. The JSON results should look something like this but I am instead greeted with:
Task completed
URL: http://api.worldweatheronline.com/free/v2/marine.ashx?key=45e8e583134b4371a2fa57a9e5776&format=xml&q=45,-2
fatal error: unexpectedly found nil while unwrapping an Optional value
I'm assuming the jsonResult variable is what's causing the problem, but even if I try to force unwrap it (I think that's what I'm doing?) with something like
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary
it still fails with "expected type after 'as'."
Everything is fail-able here, a website can even return 200 but return nothing, the data can come corrupted, and, in your case, I tried calling the URL you are using and found out, it's actually XML, so... there's your problem?
A more robust approach (assuming that you still want to parse JSON, maybe from another site? The ISS has an open API) is to use the if let to unwrap optionals and then on the else case deal with the errors, rather than ! your way to success.
Here's a very extensive check of everything
task = session.dataTaskWithRequest(NSURLRequest(URL: url), completionHandler: { [unowned self] (data, response :NSURLResponse!, errorRequest) -> Void in
// Check the HTTP Header
if let responseHTTP = response as? NSHTTPURLResponse
{
if responseHTTP.statusCode != 200
{
println("Received status code \(responseHTTP.statusCode)")
return
}
}
else
{
println("Non-HTTP Response received")
return
}
var errorParsing : NSError?
if let JSON = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &errorParsing) as? NSDictionary
{
// Do the parsing
}
else
{
println("Error parsing: \(errorParsing)")
}
})
task!.resume()
NSJSONSerialization.JSONObjectWithData... returns an optional NSData?. This means that you can't cast it directly to NSDictionary. To fix the crash you will need to use ?as which means your jsonResult will either be a NSDictionary or nil
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) ?as NSDictionary
In your case, you can safely unwrap the optional by doing this.
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil)
if let jsonResult = jsonResult ?as NSDictionary {
println(jsonResult)
}