I am attempting to parse a JSON document and enter its information into a UICollectionView. I had tested the parsing before working on the UICollectionViewDelegate/Flowlayout/DataSource etc. It was returning the correct information, however now I am getting this error. Any idea what I am doing wrong here?
class ViewModel {
let urlString = "https://s3.amazonaws.com/nxtapnxtap/clubsinfo.json"
var clubNames = [String]()
var smImg = [UIImage]()
var lgImg = [String]()
func fetchItems(success: () -> ()) {
let url = NSURL(string: urlString)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithURL(url!) { (data, response, error) in
var jsonError: NSError?
let json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) as NSDictionary // Error here --> fatal error: unexpectedly found nil while unwrapping an Optional value
if let unwrappedError = jsonError {
println("jsonError: \(unwrappedError)")
} else {
self.clubNames = json.valueForKeyPath("entry.name.label") as [String]
self.smImg = json.valueForKeyPath("entry.smimg.label") as [UIImage]
self.lgImg = json.valueForKeyPath("entry.lgimg.label") as [String]
success()
}
}
task.resume()
}
The object coming back from JSONObjectWithData is nil. You are trying to force cast it as an NSDictionary. You need to check if it can be cast to an NSDictionary before you act on it:
func fetchItems(success: () -> ()) {
let url = NSURL(string: urlString)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithURL(url!) { (data, response, error) in
var jsonError: NSError?
if let json = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) as? NSDictionary {
self.clubNames = json.valueForKeyPath("entry.name.label") as [String]
self.smImg = json.valueForKeyPath("entry.smimg.label") as [UIImage]
self.lgImg = json.valueForKeyPath("entry.lgimg.label") as [String]
success()
} else if let unwrappedError = jsonError {
println("jsonError: \(unwrappedError)")
}
}
task.resume()
}
Related
This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 6 years ago.
I am working on weather API. getting error in following code:
fileprivate let openWeatherMapBaseURL = "http://api.openweathermap.org/data/2.5/weather"
fileprivate let openWeatherMapAPIKey = "b7ac98fd9b59acbe6078468d865bd908"
func getWeather(_ city: String) {
// This is a pretty simple networking task, so the shared session will do.
let session = URLSession.shared
let weatherRequestURL = URL(string:"http://api.openweathermap.org/data/2.5/weather?q=\(city)&APPID=\(openWeatherMapAPIKey)")!
let dataTask = session.dataTask(with: weatherRequestURL, completionHandler: {
(data: Data?, response: URLResponse?, error: NSError?) in
if let error = error{
print("Error:\n\(error)")
}
else{
print("Raw data:\n\(data!)\n")
let dataString = String(data: data!, encoding: String.Encoding.utf8)
print("Human-readable data:\n\(dataString!)")
}
} as! (Data?, URLResponse?, Error?) -> Void)
dataTask.resume()
}}
Getting Error in this line:
let dataTask = session.dataTask(with: weatherRequestURL, completionHandler: {
error:
unexpectedly found nil while unwrapping an Optional value
do anyone know what is the solution for this?
User this
let requestURL: NSURL = NSURL(string: "http://api.openweathermap.org/data/2.5/weather?q=\(city)&APPID=\(openWeatherMapAPIKey)")!
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(url: requestURL as URL)
let session = URLSession.shared
let task = session.dataTask(with: urlRequest as URLRequest) {
(data, response, error) -> Void in
let httpResponse = response as! HTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
do{
let jsonResponse = try JSONSerialization.jsonObject(with: data! as Data, options: .allowFragments) as? NSDictionary
print(jsonResponse)
}catch {
print("Error with Json: \(error)")
}
}
}
Your forcibly unwrapping it
let weatherRequestURL = URL(string:"http://api.openweathermap.org/data/2.5/weather?q=\(city)&APPID=\(openWeatherMapAPIKey)")!
instead do like this
if let weatherRequestURL = URL(string:"http://api.openweathermap.org/data/2.5/weather?q=\(city)&APPID=\(openWeatherMapAPIKey)") {
// do your stuff
}
i try to using external database and api's. so i follow from this video in youtube
https://www.youtube.com/watch?v=Ixk93yx-v28
and i see this error
"Value of optional type 'NSURL?' not unwrapped; " on that line
func request(url:String,callback:(NSDictionary)->()) {
var nsURL = NSURL(string: url)
///////////////////////////on this line/////////////////////////////////
let task = NSURLSession.sharedSession().dataTaskWithURL(nsURL) {
/////////////////////////////////////////////////////////////////
(data,response,error) in
var error:NSError?
var response = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as NSDictionary
callback(response)
}
task.resume()
}
and when i try to fix by put ! in nsURL like this
xCode return this error "Extra argument 'error' in call "
func request(url:String,callback:(NSDictionary)->()) {
var nsURL = NSURL(string: url)
let task = NSURLSession.sharedSession().dataTaskWithURL(nsURL!) {
(data,response,error) in
var error:NSError?
////////////////////////////Error Here/////////////////////////////////////
var response = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: error) as NSDictionary
/////////////////////////////////////////////////////////////////////////////////
callback(response)
}
task.resume()
}
have any ideal ? sorry for my english
Update your function as shown below for swift 2.0:
func request(url:String,callback:(NSDictionary)->()) {
guard let nsURL = NSURL(string: url) else { return }
///////////////////////////on this line/////////////////////////////////
let task = NSURLSession.sharedSession().dataTaskWithURL(nsURL) {
/////////////////////////////////////////////////////////////////
(data, response, error) in
guard let data = data where error == nil else { return }
do {
if let response = try NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers) as? NSDictionary {
callback(response)
}
} catch let error as NSError {
print("json error: \(error.localizedDescription)")
}
}
task.resume()
}
I'm using a bar code scanner to get data on a scanned book using the google books api. I successfully call the API and get a JSON object back.
I'm trying to get the book title which follows the path items.volumeInfo.title.
When I call valueForPath on the JSON object returned by the API and attempt to print it (the title), I end up printing:
Optional((
"A Dance with Dragons"
))
I can't seem to figure out how to actually get the string out of the printed optional. I tried as! String and jsonResult.valueForKeyPath("items.volumeInfo.title")!, but the first simply complained to me and the second only removed the optional and outside set of parentheses.
func getBookInfo(isbn: String) {
var url: String = "https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn;
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.valueForKeyPath("items.volumeInfo.title"))
//self.json.setValue(jsonResult.valueForKeyPath("items.volumeInfo.title")!, forKey: "title")
} else {
GlobalConstants.AlertMessage.displayAlertMessage("Error fetching data from barcode, please try again.", view: self)
}
})
}
The response you get from the API is an array of titles.
I suggest using if let to unwrap the Optional value you get from KVC, and typecasting the result as a Swift array of Strings.
Swift 1
func getBookInfo(isbn: String) {
var url: String = "https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn
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
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: error) as? NSDictionary {
if let arrayOfTitles = jsonResult.valueForKeyPath("items.volumeInfo.title") as? [String] {
let titles = ", ".join(arrayOfTitles)
println(titles)
} else {
// error: no title found
}
} else {
GlobalConstants.AlertMessage.displayAlertMessage("Error fetching data from barcode, please try again.", view: self)
}
})
}
getBookInfo("0553283685") // prints "Hyperion"
Swift 2
For this version we're using NSURLSession because NSURLConnection is now deprecated.
func getBookInfo(isbn: String) {
let urlString = "https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn
if let url = NSURL(string: urlString) {
NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: {data, _, error -> Void in
if let error = error {
print(error.localizedDescription)
} else {
if let data = data,
jsonResult = try? NSJSONSerialization.JSONObjectWithData(data, options: []),
arrayOfTitles = jsonResult.valueForKeyPath("items.volumeInfo.title") as? [String] {
let titles = arrayOfTitles.joinWithSeparator(", ")
print(titles)
} else {
GlobalConstants.AlertMessage.displayAlertMessage("Error fetching data from barcode, please try again.", view: self)
}
}
}).resume()
}
}
getBookInfo("0553283685") // prints "Hyperion"
Swift 3
Same as Swift 2 with some syntax changes. I've also added the "authors" example, and I'm now using guard. Just for the sake of showing something different from the previous example.
func getBookInfo(isbn: String) {
guard let url = URL(string: "https://www.googleapis.com/books/v1/volumes?q=isbn:\(isbn)") else {
print("the url is not valid")
return
}
URLSession.shared().dataTask(with: url, completionHandler: {data, response, error -> Void in
guard error == nil else {
print(response)
print(error!.localizedDescription)
return
}
guard let data = data else {
print("no error but no data")
print(response)
return
}
guard let jsonResult = try? JSONSerialization.jsonObject(with: data, options: []) else {
print("the JSON is not valid")
return
}
if let arrayOfTitles = jsonResult.value(forKeyPath: "items.volumeInfo.title") as? [String] {
print(arrayOfTitles)
}
if let arrayOfAuthors = jsonResult.value(forKeyPath: "items.volumeInfo.authors") as? [[String]] {
print(arrayOfAuthors)
}
}).resume()
}
getBookInfo(isbn: "0553283685")
I'm attempting to test a server response using an API key taken from a .plist file. When I print out the key from the from the request header, it has an optional wrapper: Optional([App-Token: apikey1234567])). I'm not getting a response, which I think is because the key is being passed in this form. A little new to Swift. How can I pass this so it isn't optional? Relevant code below:
var key: String!
let baseURL: String = ("https://apiurl.com/").stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
override init() {
path = NSBundle.mainBundle().pathForResource("APIkey", ofType: "plist")
dict = NSDictionary(contentsOfFile: path!)
key = dict!.objectForKey("APIkey") as! String
super.init()
}
func updateJSON() {
var session = NSURLSession.sharedSession()
var request = NSMutableURLRequest(URL: NSURL(string: baseURL)!)
request.HTTPMethod = "GET"
request.setValue(key, forHTTPHeaderField:"App-Token")
// This prints out the 'Optional([App-Token: apikey1234567]))'
println("\(request.allHTTPHeaderFields))")
var dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
var error: NSError?
if (error != nil) {
println("\(error)")
} else {
self.jsonObject = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error) as? NSDictionary
println("\(self.jsonObject)")
}
})
dataTask.resume()
}
UPDATE: I was getting nil because jsonObject was being cast as an NSDictionary. When I changed its type to AnyObject the response came back.
The functions valueForHTTPHeaderField and allHTTPHeaderFields - return optionals, it doesn't mean the key you put in is optional. from Swift NSURLRequest file:
func valueForHTTPHeaderField(field: String) -> String?
var allHTTPHeaderFields: [String : String]?
Check value of key by adding print after setting it in init:
var key: String!
let baseURL: String = ("https://apiurl.com/").stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
override init() {
path = NSBundle.mainBundle().pathForResource("APIkey", ofType: "plist")
dict = NSDictionary(contentsOfFile: path!)
key = dict!.objectForKey("APIkey") as! String
println("On init Key = \(key)") // Check key value here is it optional?
super.init()
}
Also: dataTaskWithRequest returns optional so at least optional chain:
dataTask?.resume()
You also have an new error:NSError created in completion of task when it already returns an error, it appears you mean to create this error for JSON parse? best to name it different to avoid any ambiguity. Some var can be let because never mutated -session, request, dataTask.
func updateJSON() {
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: baseURL)!)
request.HTTPMethod = "GET"
request.setValue(key, forHTTPHeaderField:"App-Token")
// This prints out the 'Optional([App-Token: apikey1234567]))'
println("\(request.allHTTPHeaderFields))")
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
println("\(error)")
} else {
var jsonError: NSError?
self.jsonObject = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: & jsonError) as? NSDictionary
println("\(self.jsonObject)")
}
})
dataTask?.resume()
}
(NB: I only have machine with xcode 7 and swift 2 installed at this moment, but i don't think any of these things have changed since Swift 1.2)
I am trying to return data from JSON object as a string but whenever I try it returns nil if someone could help please find the below code. I need to return currentWeather as a String with its value currently I am not able to return the data as a string only as an optional.
let urlPath = "http://api.openweathermap.org/data/2.5/weather?q=London,uk&units=metric"
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) {
// 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
let jsonDictionary = jsonResult as NSDictionary
let mainDictionary = jsonResult.valueForKey("main") as NSDictionary
let currentWeather = mainDictionary.valueForKey("humidity") as? NSString
println(currentWeather)
})
task.resume()
I tried the code. The 'humidity' is an Int, not String. So you should use
let currentWeather = mainDictionary.valueForKey("humidity") as Int