JSON Parsing error with Swift - ios

I'm new in iOS programming and I'm trying with the new language Swift.
I've a problem / I don't know how in parsing JSON with Swift for iOS use. I already used JSON in Android, so I know that the JSON and the link is right, but when I try the code below (seen in a tutorial) the app seems to crash and highlighted this line:
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
The console gives me back this error:
The operation couldn’t be completed. (NSURLErrorDomain error -1005.) fatal error: unexpectedly found nil while unwrapping an Optional value
Here is the entire code of the button's action:
let urlAsString = "http://xxxxxxxxxx.altervista.org/App/_DD_/downloadutenti.php?email="+campoEmail.text+"&password="+campoPassword.text
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
println(url)
println(urlSession)
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
println(error.localizedDescription)
}
var err: NSError?
if(data != nil){
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
if (err != nil) {
println("JSON Error \(err!.localizedDescription)")
}
var jsonEmail = ""
if var jsonEmail: String! = jsonResult["email"] as? NSString{
}
else{
println("PROBLEM 1")
}
var jsonPassword = ""
if var jsonPassword: String! = jsonResult["pass"] as? NSString{
}
else{
println("PROBLEM 2")
}
dispatch_async(dispatch_get_main_queue(), {
self.scritta2.text = "Email: " + jsonEmail + " - Password: " + jsonPassword
})
}
})
jsonQuery.resume()
N.B.
I use xcode6 simulator and I know for sure that the "variables" campoEmail.text and campoPassword.text are taken in a good way.
JSON it should give back:
[{"id":"1","email":"lincoln#gmail.com","password":"pass","permessi":"1","stato":"Italia","citta":"Palermo","via":"Via Lincoln, 29","cap":"90100","telefono":"091xxxxxx"}]
EDIT FOR #Neo HELP:
I edited all the action of the button like this, but the code goes to the data's null check..
let curURL: NSURL = NSURL(string: "http://fantacharleston.altervista.org/App/_DD_/downloadutenti.php?email=lincoln#gmail.com&password=pass")!
let curRequest: NSURLRequest = NSURLRequest(URL: curURL, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 60)
NSURLConnection.sendAsynchronousRequest(curRequest, queue: NSOperationQueue.mainQueue()) { (response: NSURLResponse!, data: NSData?, error: NSError!) -> Void in
if (data != nil) {
if let jsonArray: NSArray = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSArray? {
let jsonObject: NSDictionary? = jsonArray.objectAtIndex(0) as? NSDictionary
if (jsonObject != nil) {
NSLog("object: %#", jsonObject!)
let email: NSString? = jsonObject?.objectForKey("email") as? NSString
let password: NSString? = jsonObject?.objectForKey("password") as? NSString
if (email != nil && password != nil) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.scritta2.text = "Email: " + email! + " - Password: " + password!
})
}
}
}
}
else{
println("null")
}
}

Yes I think I know where your Problem is... Your are trying to put a JSONArray into a NSDictionary... That cant go... Its like trying to put it into a JSONObject in Java.
Just exchange the NSDictionary with NSArray and be sure to get properties like array[0]["key"], not like array["key"], because you are working with an array, not with a dictionary... (Its like ArrayList, and HashMap in Java)
Try this please...
if let jsonResult: NSArray = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSArray? {
let jsonEmail: NSString? = jsonResult.objectAtIndex(0).objectForKey("email")
}
Edit with Code you need
Here it is... I wrote it a bit like I would write it in Java for Android... I think you will easily get it... The problem you commented maybe was that there is no "pass" tag in your JSON... its "password"...
let curURL: NSURL = NSURL(string: "http://fantacharleston.altervista.org/App/_DD_/downloadutenti.php?email=lincoln#gmail.com&password=pass")!
let curRequest: NSURLRequest = NSURLRequest(URL: curURL, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 60)
NSURLConnection.sendAsynchronousRequest(curRequest, queue: NSOperationQueue.mainQueue()) { (response: NSURLResponse!, data: NSData?, error: NSError!) -> Void in
if (data != nil) {
if let jsonArray: NSArray = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSArray? {
let jsonObject: NSDictionary? = jsonArray.objectAtIndex(0) as? NSDictionary
if (jsonObject != nil) {
NSLog("object: %#", jsonObject!)
let email: NSString? = jsonObject?.objectForKey("email") as? NSString
let password: NSString? = jsonObject?.objectForKey("password") as? NSString
if (email != nil && password != nil) {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.scritta2.text = "Email: " + email + " - Password: " + password
})
}
}
}
}
}

Related

I keep getting a use unresolved identifier error swift

When I try to run this project I am greeted with a "Use of unresolved identifier error." Here is the code I get the error on the line with
var jsonDict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
as! NSDictionary
let task : NSURLSessionDataTask = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
if((error) != nil) {
print(error!.localizedDescription)
} else {
let err: NSError?
do {
var jsonDict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
} catch {
if(err != nil) {
print("JSON Error \(err!.localizedDescription)")
}
else {
//5: Extract the Quotes and Values and send them inside a NSNotification
let quotes:NSArray = ((jsonDict.objectForKey("query") as! NSDictionary).objectForKey("results") as! NSDictionary).objectForKey("quote") as! NSArray
dispatch_async(dispatch_get_main_queue(), {
NSNotificationCenter.defaultCenter().postNotificationName(kNotificationStocksUpdated, object: nil, userInfo: [kNotificationStocksUpdated:quotes])
})
}
}
}
})
can someone please help. Thank you.
You problem could be this line of code in the catch block.
let quotes:NSArray = ((jsonDict.objectForKey("query") as! NSDictionary).objectForKey("results") as! NSDictionary).objectForKey("quote") as! NSArray
In the above statement jsonDict is out of scope. You declared jsonDict in the do block but are trying to use it in the catch block.
Try Following:-
(Assuming JSON has a root node structure)
let urlRequest: NSMutableURLRequest = NSMutableURLRequest(URL: yourURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
let httpResponse = response as! NSHTTPURLResponse
let statusCode = httpResponse.statusCode
if (statusCode == 200) {
do{
let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments)
if let queries = json["query"] as? [[String: AnyObject]] {
for query in queries {
if let quote = query["quote"] as? String {
self.quoteArr.append(quote)
}
}//for loop
dispatch_async(dispatch_get_main_queue(),{
// your main queue code
NSNotificationCenter.defaultCenter().postNotificationName(kNotificationStocksUpdated, object: nil, userInfo: [kNotificationStocksUpdated:quotes])
})//dispatch
}// if loop
}
catch
{
print("Error with Json: \(error)")
}
}
else
{
// No internet connection
// Alert view controller
// Alert action style default
}
this cobweb of code is exactly why SwiftyJSON library exists. I recommend it highly, it can be imported into your project using cocoapods.
using this library the resultant code would be
jsonQuery["query"]["results"]["quote"]
which is more readable and as you implement more APIs, much faster.

how can i fix this error when i try to Using External Database and API's with swift

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

Twitter Search Api 1.1 ERROR: Could not cast value of type '__NSCFDictionary' to 'NSArray'

This code is to search a hashtag on twitter but it gives error saying
Could not cast value of type '__NSCFDictionary' to 'NSArray'
and the same code works fine when I access my home timeline with following URL
https://api.twitter.com/1.1/statuses/home_timeline.json
func getTimeLine() {
let accountType = account.accountTypeWithAccountTypeIdentifier(
ACAccountTypeIdentifierTwitter)
self.account.requestAccessToAccountsWithType(accountType, options: nil,
completion: {(success: Bool, error: NSError!) -> Void in
if success {
let arrayOfAccounts =
self.account.accountsWithAccountType(accountType)
if arrayOfAccounts.count > 0 {
let twitterAccount = arrayOfAccounts.last as! ACAccount
let requestURL = NSURL(string:
"https://api.twitter.com/1.1/search/tweets.json?q=%23baseball&result_type=recent%3F")
let parameters = ["count" : "10"]
let postRequest = SLRequest(forServiceType:
SLServiceTypeTwitter,
requestMethod: SLRequestMethod.GET,
URL: requestURL,
parameters: nil)
postRequest.account = twitterAccount
postRequest.performRequestWithHandler(
{(responseData: NSData!, urlResponse: NSHTTPURLResponse!, error: NSError!) -> Void in
var err: NSError?
self.array = NSJSONSerialization.JSONObjectWithData(responseData, options: NSJSONReadingOptions.MutableLeaves, error: &err) as! [AnyObject**strong text**]
if self.array.count != 0 {
dispatch_async(dispatch_get_main_queue()) {
self.mycollectionView.reloadData()
}
}
})
}
}
else {
println("Failed to access account")
}
})
}
Try making a cast to NSDictionary. It should solve your problem.
self.array = NSJSONSerialization.JSONObjectWithData(
responseData,
options: NSJSONReadingOptions.MutableLeaves, error: &err
) as! NSDictionary
Maybe this is the correct return from twitter.

Can't get value from Google-Books JSON API

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

cannot invoke 'jsonObjectWithData'

I cannot figure out how to solve this issue.
This comes from a youtube tutorial to build a simple Weather App.
The tutorial was uploaded in March 2015 and therefor written in a previous version of Swift, there it worked, with the current Swift 2 it doesn't.
The error I get is: "cannot invoke 'jsonObjectWithData' with an argument list of type '(NSData, options: nil, error: NSError)'"
func getWeatherData(urlString:String) {
let url = NSURL(string: urlString)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!){ (data, response, error) in
dispatch_async(dispatch_get_main_queue(), {
self.setLabels(data)
})
}
task.resume()
}
func setLabels(weatherData: NSData) {
var jsonError: NSError
let json = NSJSONSerialization.JSONObjectWithData(weatherData, options: nil, error: jsonError)
if let name = json["name"] as? String {
self.ResultLabel.text = name
}
}
if you want to get this code ready for Swift 2, you have to run the JSONParser with try and catch possible errors.
private func httpGetRequest(request: NSURLRequest!, callback: (NSData?, String?) -> Void) {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if error != nil {
callback(nil, error!.localizedDescription)
} else {
callback(data, nil)
}
}
task!.resume()
}
func setLabels(weatherData: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(weatherData, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
if let name = json["name"] as? String {
self.resultLabel.text = name
}
} catch {
print(error)
self.resultLabel.text = "Lorem Ipsum"
}
}
func loadWeatherData() {
let weatherRequest = NSMutableURLRequest(URL: NSURL(string: "Your URL String goes here")!)
httpGetRequest(weatherRequest){
(data, error) -> Void in
if error != nil {
print("Error: \(error)")
} else {
self.setLabels(data!)
}
}
}
Hope that will help to solve your Problems.
Try this:
var jsonError: NSError?
let json = NSJSONSerialization.JSONObjectWithData(weatherData, options: nil, error: &jsonError)
in swift 3.0 and Swift 4.2
try this ...
do {
let jsonData = try JSONSerialization.data(withJSONObject: your array or dictionary, options: JSONSerialization.WritingOptions()) as Data
let json = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions(rawValue: UInt(0)))
}
catch
{
}
You need to pass the error pointer into NSJSONSerialization.JSONObjectWithData(...) with &.
let json = NSJSONSerialization.JSONObjectWithData(weatherData, options: nil, error: &jsonError) // &jsonError points to the NSErrorPointer of the NSError object
In swift 3 you can try this:
func setLabels(weatherData: NSData) {
do {
var jsonError: NSError
let json = try JSONSerialization.jsonObject(with: weatherData as Data, options: []) as! NSDictionary
if let name = json["name"] as? String {
self.ResultLabel.text = name
}
} catch {
}
}
In Swift 3, you can fix the same error with the code below:
do{
let jsonData = try JSONSerialization.jsonObject(with: (data)!, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: AnyObject]
}catch{
print("Error while parsing JSON: Handle it")
}

Resources