Full error: Fatal error: Unexpectedly found nil while unwrapping an Optional value.
I am trying to get data from Web API service and am not sure where in the program it is getting nil value from.
Program crashes and getting error at line when declaring jsonResult
let urlAsString = "http://api.geonames.org/earthquakesJSON?north="+northString+"&south="+southString+"&east="+eastString+"&west="+westString+"&username=test"
let url = URL(string: urlAsString)!
let urlSession = URLSession.shared
let jsonQuery = urlSession.dataTask(with: url, completionHandler: { data, response, error -> Void in
if (error != nil) {
print(error!.localizedDescription)
}
var err: NSError?
let jsonResult = (try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSDictionary //program crashes and gets error here
if (err != nil) {
print("JSON Error \(err!.localizedDescription)")
}
print(jsonResult)
let setOne:NSArray? = jsonResult["earthquakes"] as? NSArray
print(setOne?[0]);
let y = setOne?[0] as? [String: AnyObject]
let dateTime: String = (y!["datetime"] as? NSString)! as String
DispatchQueue.main.async{
self.date.text = String(dateTime)
}
})
jsonQuery.resume()
Seems like data is nil. You forcibly tried to set nil value.
if let data = data {
if let jsonResult = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as NSDictionary {
if let setOne = jsonResult["earthquakes"] as? [NSDictionary] {
let y = setOne[0] as? [String: AnyObject]
let dateTime: String = (y!["datetime"] as? String)! as String
DispatchQueue.main.async{
self.date.text = String(dateTime)
}
}
}
}
It crashes because you force unwrap a nil value. So try with optional instead
replace this line let jsonResult = (try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSDictionary with:
do {
if let jsonResult = try? JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)) as? NSDictionary {
// Rest of your code here
}
} catch let error {
}
Related
I openned an old ios project in Xcode with warnings about swift 4 updates.
During some fixing an error I could not find solution.
The error occours while looping jsonArray, passing values to variables...
let url=NSURL(string:"http://webserver.com/json")
let data = NSData(contentsOf: url! as URL)
do {
let jsonResult = try JSONSerialization.jsonObject(with: data! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
let jsonArray = jsonResult.value(forKey: "person") as! NSArray
for json in jsonArray {
let id = json["id"] as? Int?
let name = json["name"] as? String
let age = json["age"] as? String
tableID.append(String(id!))
tableName.append(name!)
tableAge.append(age!)
tableView.reloadData()
}
} catch {
}
There are many don'ts in the code.
NSURL, NSData
NSArray, NSDictionary (which causes the error)
(NS)Data(contentsOf)
value(forKey:)
.mutableContainers
ignoring the error in the catch block
The actual native Swift syntax is
let url = URL(string:"http://webserver.com/json")!
let task = URLSession.shared.dataTask(with: url) { [unowned self] (data, response, error) in
if let error = error { print(error); return }
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!) as? [String:Any],
let jsonArray = jsonResult["person"] as? [[String:Any]] {
for json in jsonArray {
let id = json["id"] as! Int
let name = json["name"] as! String
let age = json["age"] as! String
self.tableID.append(String(id))
self.tableName.append(name)
self.tableAge.append(age)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
} catch {
print(error)
}
}
task.resume()
There is one don't left: Don't use multiple arrays as data source.
When using this code to get JSON from a server with Basic auth:
let config = URLSessionConfiguration.default
let userPasswordData = "\(username!):\(password!)".data(using: .utf8)
let base64EncodedCredential = userPasswordData!.base64EncodedString(options: Data.Base64EncodingOptions.init(rawValue: 0))
let authString = "Basic \(base64EncodedCredential)"
config.httpAdditionalHeaders = ["Authorization" : authString]
let session = URLSession(configuration: config)
let url = URL(string: "https://theforest.academy/api/v1/auth")
let task = session.dataTask(with: url!) {
(data, response, error) in
if (response as? HTTPURLResponse) != nil {
do {
if let data = data {
// let json1 = try JSONSerialization.jsonObject(with: data) as? [String: Any]
let json = try JSONSerialization.jsonObject(with: data, options: [], error: []) as? [String: Any]
if(json["valid"]) {
print(json["token"])
} else {
print("Invalid login")
}
}
} catch {
print("Error deserializing JSON: \(error)")
}
I am getting the following error
Cannot invoke 'jsonObject' with an argument list of type '(with:
Data, options: [Any], error: [Any])'
Looking at the documentation, it seems that you only have two choices:
jsonObject(with: Data, options: JSONSerialization.ReadingOptions = [])
And
jsonObject(with: InputStream, options: JSONSerialization.ReadingOptions = [])
I think you are looking for the first method. Perhaps you are confusing it with
writeJSONObject(Any, to: OutputStream, options: JSONSerialization.WritingOptions = [], error: NSErrorPointer)
So, in short, such a method does not exist. That is why you are getting the error.
This is because we are not handling error while getting Data. So we need to safely type caste data and then pass it to JsonSerializer. Here is the code.
do {
let data = try NSData.init(contentsOfFile: path) as Data
let jsonArray = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
print(jsonArray)
} catch {
print("Error getting data")
}
i tried to get The (Name) value to a label
i used resultLabel.text = !(jsonResult["name"]) but it returns an error
Cannot subscript a value of type 'AnyObject' with an index of type 'String'
See my JSON
and does anybody know's how to get the data..
My code ....
if let url = URL(string: "http://www.omdbapi.com/?t=The+Raid&y=&plot=short&r=json") {
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in // URLSession.shared().dataTask(with: url) { (data, response, error) is now URLSession.shared.dataTask(with: url) { (data, response, error)
if error != nil {
print(error)
} else {
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject // Added "as anyObject" to fix syntax error in Xcode 8 Beta 6
print(jsonResult)
print(jsonResult["Title"])
resultLabel.text = (jsonResult["name"])
if let description = ((jsonResult["weather"] as? NSArray)?[0] as? NSDictionary)?["description"] as? String {
DispatchQueue.main.sync(execute: {
self.resultLabel.text = description
})
}
} catch {
print("JSON Processing Failed")
}
}
}
}
task.resume()
} else {
resultLabel.text = "Couldn't find weather for that city - please try another."
}
}
Casting the result of JSON deserialization to AnyObject is the worst you can do.
First of all the unspecified JSON type is Any and since the type is supposed to be a dictionary, cast it to [String:Any].
Further in Swift 3 the compiler must know all types which are subscripted
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: []) as! [String:Any]
let name = jsonResult["name"] as? String
print(name ?? "n/a")
if let weather = jsonResult["weather"] as? [[String:Any]], !weather.isEmpty {
if let description = weather[0]["description"] as? String {
DispatchQueue.main.async { // not sync !!
self.resultLabel.text = description
}
}
...
PS: As always, mutableContainers is meaningless in Swift and meaningless anyway if the values are only read.
I've updated Xcode from 7 to 8 and Swift from 2.3 to 3.
I'm getting this error at let names = candidate["CandidateName"]!:
type nsfastenumerationiterator.element aka any has no subscript members
let url = URL(string: "https://website.com")
let data = try? Data(contentsOf: url!)
var tmpValues = try! JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSArray
tmpValues = tmpValues.reversed() as NSArray
reloadInputViews()
for candidate in tmpValues {
if ((candidate as? NSDictionary) != nil) {
let names = candidate["CandidateName"]!
//self.values.append(candidate["CandidateName"])
self.values.append(name!)
print(name)
}
}
I think your for in loop should like this. This is work for me. But be sure var tmpValues.
for candidate in (tmpValues as? [[String:Any]])! {
if ((candidate as? NSDictionary) != nil) {
let names = candidate["CandidateName"]! as? String
//self.values.append(candidate["CandidateName"])
self.values.append(name!)
print(name)
}
}
I want to cast jsonResult to NSDicitonary to be able to do a callback of jsonResult. Is this possible?
func request(url:String,callback:(NSDictionary)->()) {
let nsURL = NSURL(string: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(nsURL!, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error)
} else {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
print(jsonResult[0])
} catch {
print("my error")
}
}
})
If I understand your question, you can use optional binding to cast your object:
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSArray {
if let dict = jsonResult[0] as? NSDictionary {
callback(dict)
}
}