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()
}
Related
Right now I have this:
let path : String = NSBundle.mainBundle().pathForResource("jsonFile", ofType: "json") as String!
let jsonData = NSData(contentsOfFile: path) as NSData!
let readableJSON = JSON(data :jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil)
But I want to receive the file from a url, and not have it in my app.
You need to read through some of apple docs: NSURLConnection and NSURLSession
Here is a quick solution for how to achieve this though.
func fetchDetailsFromServer() {
let urlPath = "Server URL to Fetch JSON"
let url = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
println("Task completed")
if(error != nil) {
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
}
var err: NSError?
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary {
if(err != nil) {
// If there is an error parsing JSON, print it to the console
println("JSON Error \(err!.localizedDescription)")
}
// Use your jsonResult appropriately here
}
})
// The task is just an object with all these properties set
// In order to actually make the web request, we need to "resume"
task.resume()
}
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.
I have a page that loops JSON data and show in a tableview.
I get the correct correctly but when it shows on the page, it is very slow.
I tried to println the json to see how fast it retrieves the json data and it was very fast.
One more thing which is very odd is when it is loading, if I drag the page, everything will show instantly.
Below is the code I put in viewdidload function
self.PoTable.separatorStyle = UITableViewCellSeparatorStyle(rawValue: 0)!
self.navigationController?.navigationBar.topItem?.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
PoTable.delegate = self
PoTable.dataSource = self
self.PoTable.addSubview(self.activityIndicatorView)
UIApplication.sharedApplication().networkActivityIndicatorVisible = true
activityIndicatorView.startAnimating()
let url = NSURL(string: SharedClass().clsLink + "/json/POList.cfm")
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?
let res = response as! NSHTTPURLResponse!
if(res != nil){
if (res.statusCode >= 200 && res.statusCode < 300){
self.jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as! NSDictionary
if(err != nil) {
// If there is an error parsing JSON, print it to the console
println("JSON Error \(err!.localizedDescription)")
}
var resultsArr: NSArray = self.jsonResult["results"] as! NSArray
// println(resultsArr)
self.PoList = PoInfo.poInfoWithJSON(resultsArr)
self.PoTable!.reloadData()
self.activityIndicatorView.stopAnimating()
self.activityIndicatorView.hidden = true
}
else{
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
SharedClass().serverAlert(self)
}
}else{
UIApplication.sharedApplication().networkActivityIndicatorVisible = false
self.activityIndicatorView.stopAnimating()
SharedClass().serverAlert(self)
}
})
task.resume()
please help
try this async scope dispatch_async(dispatch_get_main_queue()) in your if closure like
dispatch_async(dispatch_get_main_queue()) {
if (res.statusCode >= 200 && res.statusCode < 300){
self.jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as! NSDictionary
if(err != nil) {
// If there is an error parsing JSON, print it to the console
println("JSON Error \(err!.localizedDescription)")
}
var resultsArr: NSArray = self.jsonResult["results"] as! NSArray
self.PoList = PoInfo.poInfoWithJSON(resultsArr)
self.PoTable!.reloadData()
self.activityIndicatorView.stopAnimating()
self.activityIndicatorView.hidden = true
}
}
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)
}
#IBOutlet weak var weather: UILabel!
#IBOutlet weak var city: UITextField!
#IBAction func button(sender: AnyObject) {
let urlpath = "api.worldweatheronline.com/free/v2/weather.ashx?q=\(city.text)&format=json&num_of_days=5&key=c7fc4c9444ae2ddcee02a0893d5f0"
let url = NSURL(string: urlpath)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
self.weather.text="error"
}else{
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary
println(jsonResult)
}
})
task.resume()
}
I receive errors in the console when running the application. I am correctly serializing the data I believe but, I can not get the JSON serialized data to show up in an NSDictionary. I have tried to print the Dictionary to the console and it still just shows up as error. Please help me understand whats wrong here.
Try some thing like this.
func fetchWeatherData(latLong: String, completion: WeatherDataCompletionBlock) {
let baseUrl = NSURL(string: "http://api.worldweatheronline.com/free/v2/weather.ashx?q=\(city.text)&format=json&num_of_days=5&key=c7fc4c9444ae2ddcee02a0893d5f0")
let request = NSURLRequest(URL: baseUrl!)
println(request)
let task = session.dataTaskWithRequest(request) {[unowned self] data, response, error in
if error == nil {
var jsonError: NSError?
if (jsonError == nil) {
let weatherDictionary = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.AllowFragments, error: &jsonError) as NSDictionary
let data = WeatherData(weatherDictionary: weatherDictionary)
completion(data: data, error: nil)
} else {
completion(data: nil, error: jsonError)
}
} else {
completion(data: nil, error: error)
}
}
task.resume()
}