My function getWeatherInfo() retrieves data from a weather web service, parses the json, and sorts certain information into my array, info. After I set up my function, I ran the program and got this error: The operation couldn’t be completed. (NSURLErrorDomain error -1005.)
fatal error: unexpectedly found nil while unwrapping an Optional value. It tells me this error when I declare my variable jsonResult
Here's the code:
var info = [AppModel]()
func getWeatherInfo(completion: (results : NSArray?) ->Void){
let urlPath = "http://api.openweathermap.org/data/2.5/weather?zip=92606,us&units=imperial"
let url: NSURL = NSURL(string: urlPath)!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
if error != nil {
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
}
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary // here is where I recieve my error
if err != nil {
// If there is an error parsing JSON, print it to the console
println("JSON Error \(err!.localizedDescription)")
}
let json = JSON(jsonResult)
var temp = json["main"]["temp"].stringValue
var humidity = json["main"]["humidity"].stringValue
var pressure = json["main"]["pressure"].stringValue
var information = AppModel(temperature: temp, pressure: pressure, humidity: humidity)
println(information)
self.info.append(information)
completion(results: self.info)
})
task.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
getWeatherInfo{ (info) in
println(info)
}
}
It's been working fine before in the past, and I was successfully get the data back from the web service and sort information into my array. Can someone point me in the right direction on how I can fix this?
Occasional network problems are normal. Wait a while and try again. The mistake in your code is here:
if error != nil {
println(error.localizedDescription)
}
Should be:
if error != nil {
println(error.localizedDescription)
return
}
If there is an error you must stop, or you'll also crash.
Related
i created a watchOS app that request a value from an API and show it on a label.
It is working perfectly in the simulator but when I execute it on my Apple Watch it crashes with the following error:
[ERROR] There is an unspecified error with the connection
fatal error: unexpectedly found nil while unwrapping an Optional value
The first error is generated by my code.
The code I wrote is:
func price_request() -> NSData? {
guard let url = NSURL(string: "https://api.xxxxx.com/xxx.php") else {
return nil
}
guard let data = NSData(contentsOfURL: url) else {
print("[ERROR] There is an unspecified error with the connection")
return nil
}
print("[CONNECTION] OK, data correctly downloaded")
return data
}
func json_parseData(data: NSData) -> NSDictionary? {
do {
let json: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers) as! Dictionary<String, AnyObject>
print("[JSON] OK!")
return (json as? NSDictionary)
} catch _ {
print("[ERROR] An error has happened with parsing of json data")
return nil
}
}
I tried also to add the App Transport Security bypass also if it is not needed because of a request to an HTTPS URL but it does not works.
Can you please help me?
Thank you
Try using NSURLSession to get data...
//declare data task
var task: URLSessionDataTask?
//setup the session
let url = URL(string:"https://url.here")!
let session = URLSession(configuration: URLSessionConfiguration.default)
task = session.dataTask(with: url){ (data, res, error) -> Void in
if let e = error {
print("dataTaskWithURL fail: \(e.localizedDescription)")
return
}
if let d = data {
//do something
}
}
task!.resume()
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)
}
I have a function to retrieve JSON data from an certain ip address.
if the ip is not reachable, xcode gives me following error at
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
The operation couldn’t be completed. (NSURLErrorDomain error -1002.) fatal error: unexpectedly found nil while unwrapping an Optional value
How can I check, if jsonResult is valid or if ip-address is valid?
func getJsonData() {
let urlAsString = NSUserDefaults.standardUserDefaults().objectForKey("ipAddress") as String
let url: NSURL = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
println(error.localizedDescription)
}
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
if (err != nil) {
println("JSON Error \(err!.localizedDescription)")
}
//Display Data
dispatch_async(dispatch_get_main_queue(), {
self.label.text = String(format: "%.3f", (jsonResult["pwr"] as Float!)/1000)
})
})
jsonQuery.resume()
}
Thanks!
if (error != nil) {
println(error.localizedDescription)
}
It's not enough to print an error to debugger console. The error must be handled. Ask yourself - what do you want to do if there is no internet connection? Do you want to display an error to the user? Try to send the request again?
The basic fact is that if there was an error, you cannot continue and parse the data. The data is not there, it was not loaded. If you try to parse the data (which is nil), your app will crash.
if (error != nil) {
//XXX: display error to the user, e.g. using an alert
return; //return immediately, don't proceed to parsing.
}
Create a method like below to check valid url.
func validateUrl (stringURL : NSString) -> Bool {
var urlRegEx = "((https|http)://)((\\w|-)+)(([.]|[/])((\\w|-)+))+"
let predicate = NSPredicate(format:"SELF MATCHES %#", argumentArray:[urlRegEx])
var urlTest = NSPredicate.predicateWithSubstitutionVariables(predicate)
return predicate.evaluateWithObject(stringURL)
}
And then update your method. Add a line to call validate url method.
func getJsonData() {
let urlAsString = NSUserDefaults.standardUserDefaults().objectForKey("ipAddress") as String
var isValidURL = validateUrl(urlAsString)
if (!isValidURL) {
// Return from here. URL is invalid
return
}
let url: NSURL = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
println(error.localizedDescription)
}
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
if (err != nil) {
println("JSON Error \(err!.localizedDescription)")
}
//Display Data
dispatch_async(dispatch_get_main_queue(), {
self.label.text = String(format: "%.3f", (jsonResult["pwr"] as Float!)/1000)
})
})
jsonQuery.resume()
}
I am trying to follow a tutorial - which failed and having found some code on stack over flow Make REST API call in Swift decided to try this approach instead. The following code is a copy and paste (I know!) with a change of URL yet I seem to be getting an error printed to the console rather than the JSON - the error being largely unhelpful - 0x0000000000000000
The JSON appears to print to the browser window so I am not fully sure what might be wrong with this approach? Please could someone provide some help as to why this might not be working?
Thanks
var url : String = "http://www.flickr.com/services/rest/?method=flickr.test.echo&format=json&api_key=d6e995dee02d313a28ed4b799a09b869"
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
if (jsonResult != nil) {
println(jsonResult);
} else {
// couldn't load JSON, look at error
println(error);
}
})
The problem is that the call returns data in JSONP not JSON
Your code would work ok if tested with a correct JSON like http://ip.jsontest.com
To make it work with Flickr you have to modify it as follows:
var json_str:NSString = NSString(data: data, encoding: NSUTF8StringEncoding)
json_str = json_str.substringFromIndex(14)
json_str = json_str.substringToIndex(json_str.length-1)
let new_data:NSData = json_str.dataUsingEncoding(NSUTF8StringEncoding)!
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(new_data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary
if (jsonResult != nil) {
println(jsonResult);
} else {
// couldn't load JSON, look at error
println(error);
}