Nathan McKaskle Nathan McKaskle - 11 months ago 60
JSON Question

Why do I get an error Code=3840 Parsing JSON?

I'm new to Swift and iOS development in general. I'm testing and learning how to use remote server api's. I'm getting JSON from a Strongloop (Loopback) api while attempting authentication and the parsing I'm trying to use gets an error:


Error Domain=NSCocoaErrorDomain Code=3840 "The operation couldn’t be completed. (Cocoa error 3840.)" (No value.) UserInfo=0x7fd623e38870 {NSDebugDescription=No value.}


Here is the return value in a string, I'm obviously getting a proper JSON response from Loopback, an authentication token, ttl, date and userId:


{"id":"BHzKnjrbUPn9KSC1GegQTNHJFNwfuifNXPfZuKkYxC5IwRDEHuerurvSdBMzzrVi","ttl":1209600,"created":"2015-07-03T13:04:39.791Z","userId":2}


I think the actual problem is NOT occurring in the parseJSON method but rather the performLoginRequestWithURL method. It's returning an empty string. Something to do with the asynchronous request. I noticed that the json string variable does not get set until after it's returned from the method, so it gets returned blank. If I put two println(json) methods, one inside the asynchronous request and one after it, the one after prints first. Which makes sense in a way but I don't know how to solve this. I need to get the json returned from the post but I don't know how to capture that. Something tells me I need to use a synchronous request instead but I don't know how to do that in this context of getting json from a POST request.

Here is my code:

//Login button pressed
@IBAction func login() {
//Gets url string
let url = getLogin()
//Posts url with UITextField data.
if let jsonString = performLoginRequestWithURL(url) {
//Error occurs in the parseJSON method
if let dictionary = parseJSON(jsonString) {
/*
if let at = dictionary["id"] as? String {
accesstoken = at
}
if let id = dictionary["userId"] as? Int {
println(id)
}
*/
}
}

}

func getLogin() -> NSURL {
let toEscape = "http://localhost:3000/api/Users/login"
let urlString = toEscape.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let url = NSURL(string: urlString)
return url!
}

func performLoginRequestWithURL(url: NSURL) -> String? {
let bodyData = "username=\(textEmail.text)&password=\(textPW.text)"
var request: NSMutableURLRequest = NSMutableURLRequest(URL: url)
var json = ""
request.HTTPMethod = "POST"
request.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()){
response, data, error in

if data != nil {
json = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
}
println(json)
}
return json
}

func parseJSON(jsonString: String) -> [String: AnyObject]? {
if let data = jsonString.dataUsingEncoding(NSUTF8StringEncoding) {
var error: NSError?
if let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(0), error: &error) as? [String: AnyObject] {
return json
} else if let error = error {
//Here's where the error comes back.
println("JSON Error: \(error)")
} else {
println("Unknown JSON Error")
}
}
return nil
}

Answer Source

The parseJSON method must be called from the NSURLConnection.sendAsynchronousRequest completion block, not just after the asynchronous request is made, there generally is no response by that point.

if data != nil {
    json = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
    println("json: \(json)")

    if let dictionary = parseJSON(jsonString) {
        println("dictionary: \(dictionary)")
   }
}