Handling JSON in Swift - json

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)
}

Related

-- function-call with completion URLRequest - JSON

i did read a lot about functions with completion-handler, but now i have a problem how to call this function (downloadJSON) in the correct way. Which parameters do i have to give in the function and handle the result-data (json) in my own class, where the function was called.
This is the code from David Tran. Hi makes wonderful tutorials, but in the code there is no call of this function.
let request: URLRequest
lazy var configuration: URLSessionConfiguration = URLSessionConfiguration.default
lazy var session: URLSession = URLSession(configuration: self.configuration)
typealias JSONHandler = (JSON?, HTTPURLResponse?, Error?) -> Void
func downloadJSON(completion: #escaping JSONHandler)
{
let dataTask = session.dataTask(with: self.request) { (data, response, error) in
// OFF THE MAIN THREAD
// Error: missing http response
guard let httpResponse = response as? HTTPURLResponse else {
let userInfo = [NSLocalizedDescriptionKey : NSLocalizedString("Missing HTTP Response", comment: "")]
let error = NSError(domain: DANetworkingErrorDomain, code: MissingHTTPResponseError, userInfo: userInfo)
completion(nil, nil, error as Error)
return
}
if data == nil {
if let error = error {
completion(nil, httpResponse, error)
}
} else {
switch httpResponse.statusCode {
case 200:
// OK parse JSON into Foundation objects (array, dictionary..)
do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String : Any]
completion(json, httpResponse, nil)
} catch let error as NSError {
completion(nil, httpResponse, error)
}
default:
print("Received HTTP response code: \(httpResponse.statusCode) - was not handled in NetworkProcessing.swift")
}
}
}
dataTask.resume()
}
Let Xcode help you. Type downlo and press return. Xcode completes the function
Press return again and you get the parameters
You have to replace the placeholders with parameter names for example
downloadJSON { (json, response, error) in
if let error = error {
print(error)
} else if let json = json {
print(json)
}
}
Note:
There is a fatal type mismatch error in your code: The result of the JSONSerialization line is [String:Any] but the first parameter of the completion handler is JSON

json data not getting loaded into UITextView in swift

class ViewController:ViewController,UITextViewDelegate{
#IBOutlet weak var newTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
newTextView.delegate = self
dataFun()
}
func dataFun()
{
let url : String = "http:xyz/abc"
let request : NSMutableURLRequest = NSMutableURLRequest()
request.URL = NSURL(string: url)
request.HTTPMethod = "GET"
print("Start")
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
do {
let jsonResult: NSDictionary! = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers) as? NSDictionary
print("In method\(jsonResult)")
// let data = jsonResult["description"]
// print(data!)
if (jsonResult != nil)
{
// process jsonResult
print("Data added")
let test:String = jsonResult["description"] as! String
print(test)
self.newTextView.text = test
} else {
print("No Data")
// couldn't load JSON, look at error
}
}
catch {
print("Error Occured")
}
}
.resume()
}
In my app I am going to call services from API
I can see my json data in console.
that data is not show in textviewController
it shows fatal error:
unexpectedly found nil while unwrapping an Optional value
and then crash the app
Make sure #IBOutlet weak var newTextView: UITextView! is set up correctly.
Make sure let test:String = jsonResult["description"] as! String doesn't crash. JSON has field description and it's a string.

Error in JSON downloaded data as NSDictionary in Swift

#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()
}

Get JSON data function - Address noch reachable - NSURLErrorDomain error -1002

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()
}

Unable to parse JSON from the HTTP response

Why am I unable to parse JSON from the HTTP response via the following code?
if let url = NSURL(string: "https://2ch.hk/b/threads.json") {
let task = NSURLSession.sharedSession().dataTaskWithURL(url) {
(data, response, error) in
var jsonError: NSError?
let jsonDict = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) as [String: AnyObject]
if jsonError != nil {
return
}
// ...
}
task.resume()
}
Output
fatal error: unexpectedly found nil while unwrapping an Optional value
What am I doing wrong? How can I fix it?
Thanks in advance.
This is a bit late.... but I think you are trying to parse the error as well so add an else part and and the dictionary to be serialized will only be parsed if there is the data ... your code can be modified as follows
if let url = NSURL(string: "https://2ch.hk/b/threads.json") {
let task = NSURLSession.sharedSession().dataTaskWithURL(url) {
(data, response, error) in
if (jsonError != nil) {
return
} else {
var jsonError: NSError?
let jsonDict = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) as [String: AnyObject]}
// ...
}
task.resume()
}