I am trying to access an api using the following code.
I am getting the error "fatal error: unexpectedly found nil while unwrapping an Optional value" when I am trying to parse json.
I am not sure why is that error happening.
The data is not nil.
var urlFull = NSURL(string: url)!
var urlrequest = NSURLRequest(URL: urlFull)
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(urlrequest, queue: queue, completionHandler: {
(response, data, error) -> Void in
println(response)
println(data)
println(error)
if let anError = error {
println(error)
} else {
var jsonError: NSError? = nil
let post = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) as NSDictionary
if let aJSONError = jsonError {
println("Error parsing")
} else {
println("The post is: " + post.description)
}
}
})
The problem is your forced cast: as NSDictionary. Whatever's being returned can't be casted to NSDictionary.
You should always use optional casting (as?) and optional unwrapping (if let…) when parsing JSON:
let post = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &jsonError) as? NSDictionary
if let post = post {
// it worked! parse post
} else {
// it's not a dictionary.
println(post)
// see what you have and debug from there
}
Related
This question already has answers here:
Swift: Extra argument 'error' in call
(3 answers)
Closed 5 years ago.
I have been trying to login for a while using php but I have a final bug that stops me from finishing. It turns out to give a bug in NSJSONSerialization where the error tells me: error extra argument in call. Then I will provide a screenshot so that the error is clearer and the code so you can help me since I am in a blocking moment and I do not know how to solve it. Thanks in advance
Photo error: [
Code:
#IBAction func loginButtonTapped(sender: AnyObject) {
let userEmail = userEmailTextField.text;
let userPassword = userPasswordTextField.text;
if(userEmail!.isEmpty || userPassword!.isEmpty) { return; }
let myUrl = NSURL(string: "http://localhost/billapp/userSignIn.php");
let request = NSMutableURLRequest(URL:myUrl!);
request.HTTPMethod = "POST";
let postString = "userEmail=\(userEmail)&userPassword=\(userPassword)";
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding);
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
if error != nil{
print("error=\(error)")
return
}
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error:NSError) as? NSDictionary
if let parseJSON = json {
var resultValue:String = parseJSON["status"] as String!;
print("result: \(resultValue)")
if(resultValue=="Success")
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isUserLoggedIn");
NSUserDefaults.standardUserDefaults().synchronize();
self.dismissViewControllerAnimated(true, completion: nil);
}
}
}
task.resume()
}
last error
You can follow the quick fix sugestions. Doing so will let you end up like this:
do {
var json = try JSONSerialization.jsonObject(with: Data(), options: .mutableContainers)
if let parseJSON = json {
//your code
}
} catch {
//handle error
}
Unlike Objective C, as per the new methodolgy in Swift, errors are handled using try/catch block instead of passing the error object in the method.
Use the following code for Swift 3.
do {
var json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
if let parseJSON = json {
var resultValue:String = parseJSON["status"] as String!;
print("result: \(resultValue)")
if(resultValue=="Success")
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isUserLoggedIn");
NSUserDefaults.standardUserDefaults().synchronize();
self.dismissViewControllerAnimated(true, completion: nil);
}
}
} catch let error {
//Perform the error handling here
}
Following for Swift 2
do {
var json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers)
if let parseJSON = json {
var resultValue:String = parseJSON["status"] as String!;
print("result: \(resultValue)")
if(resultValue=="Success")
{
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isUserLoggedIn");
NSUserDefaults.standardUserDefaults().synchronize();
self.dismissViewControllerAnimated(true, completion: nil);
}
}
} catch let error {
//Perform the error handling here
}
I am currently trying to download, parse and print JSON from an URL.
So far I got to this point:
1) A class (JSONImport.swift), which handles my import:
var data = NSMutableData();
let url = NSURL(string:"http://headers.jsontest.com");
var session = NSURLSession.sharedSession();
var jsonError:NSError?;
var response : NSURLResponse?;
func startConnection(){
let task:NSURLSessionDataTask = session.dataTaskWithURL(url!, completionHandler:apiHandler)
task.resume();
self.apiHandler(data,response: response,error: jsonError);
}
func apiHandler(data:NSData?, response:NSURLResponse?, error:NSError?)
{
do{
let jsonData : NSDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary;
print(jsonData);
}
catch{
print("API error: \(error)");
}
}
My problem is, that the data in
do{
let jsonData : NSDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary;
print(jsonData);
}
remains empty.
When I debug,the connection starts successfully, with the given url as a parameter. But my jsonData variable doesn't get printed. Instead the catch block throws the error, stating that there is no data in my variable:
API error: Error Domain=NSCocoaErrorDomain Code=3840 "No value."
Can someone please help me with this?
What am I missing?
Thank you all very much in advance!
[Edited after switching from NSURL Connection to NSURLSession]
Here's an example on how to use NSURLSession with a very convenient "completion handler".
This function contains the network call and has the "completion handler" (a callback for when the data will be available):
func getDataFrom(urlString: String, completion: (data: NSData)->()) {
if let url = NSURL(string: urlString) {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { (data, response, error) in
// print(response)
if let data = data {
completion(data: data)
} else {
print(error?.localizedDescription)
}
}
task.resume()
} else {
// URL is invalid
}
}
You can use it like this, inside a new function, with a "trailing closure":
func apiManager() {
getDataFrom("http://headers.jsontest.com") { (data) in
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
if let jsonDict = json as? NSDictionary {
print(jsonDict)
} else {
// JSON data wasn't a dictionary
}
}
catch let error as NSError {
print("API error: \(error.debugDescription)")
}
}
}
I'm currently developing my first iOS app using Swift 2.0 and Xcode Beta 2. It reads an external JSON and generates a list in a table view with the data. However, I'm getting a strange little error that I can't seem to fix:
Extra argument 'error' in call
Here is a snippet of my code:
let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in
print("Task completed")
if(error != nil){
print(error!.localizedDescription)
}
var err: NSError?
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary{
if(err != nil){
print("JSON Error \(err!.localizedDescription)")
}
if let results: NSArray = jsonResult["results"] as? NSArray{
dispatch_async(dispatch_get_main_queue(), {
self.tableData = results
self.appsTableView!.reloadData()
})
}
}
})
The error is thrown at this line:
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary{
Can someone please tell me what I'm doing wrong here?
With Swift 2, the signature for NSJSONSerialization has changed, to conform to the new error handling system.
Here's an example of how to use it:
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary {
print(jsonResult)
}
} catch let error as NSError {
print(error.localizedDescription)
}
With Swift 3, the name of NSJSONSerialization and its methods have changed, according to the Swift API Design Guidelines.
Here's the same example:
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: []) as? [String:AnyObject] {
print(jsonResult)
}
} catch let error as NSError {
print(error.localizedDescription)
}
Things have changed in Swift 2, methods that accepted an error parameter were transformed into methods that throw that error instead of returning it via an inout parameter. By looking at the Apple documentation:
HANDLING ERRORS IN SWIFT:
In Swift, this method returns a nonoptional result and is marked with the throws keyword to indicate that it throws an error in cases of failure.
You call this method in a try expression and handle any errors in the catch clauses of a do statement, as described in Error Handling in The Swift Programming Language (Swift 2.1) and Error Handling in Using Swift with Cocoa and Objective-C (Swift 2.1).
The shortest solution would be to use try? which returns nil if an error occurs:
let message = try? NSJSONSerialization.JSONObjectWithData(receivedData, options:.AllowFragments)
if let dict = message as? NSDictionary {
// ... process the data
}
If you're also interested into the error, you can use a do/catch:
do {
let message = try NSJSONSerialization.JSONObjectWithData(receivedData, options:.AllowFragments)
if let dict = message as? NSDictionary {
// ... process the data
}
} catch let error as NSError {
print("An error occurred: \(error)")
}
This has been changed in Swift 3.0.
do{
if let responseObj = try JSONSerialization.jsonObject(with: results, options: .allowFragments) as? NSDictionary{
if JSONSerialization.isValidJSONObject(responseObj){
//Do your stuff here
}
else{
//Handle error
}
}
else{
//Do your stuff here
}
}
catch let error as NSError {
print("An error occurred: \(error)") }
I'm using the SODA Client for swift (Created by Socrata), I just updated to XCode 7 and swift 2 and found some troubles. The one I haven't been able to solve is the completion handler case when it finds an error, it's not accepting the line "syncCompletion(.Error (reqError))" that supposedly should get the error and return to main thread.
I've seen many errors with the same description here "Type of expression is ambiguous without more context", but not in completion handlers, I saw one using do - catch that is different. I'm don't know enough of swift to find out the way to change this.
Some answers suppose you should rewrite the code because some types could have change in swift 2, but I wouldn't know where to start rewriting.
Thanks in advance for your help.
var task = session.dataTaskWithRequest(request, completionHandler: { data, response, reqError in
// We sync the callback with the main thread to make UI programming easier
let syncCompletion = { res in NSOperationQueue.mainQueue().addOperationWithBlock { completionHandler (res) } }
// Give up if there was a net error
if reqError != nil {
syncCompletion(.Error (reqError))
return
}
// Try to parse the JSON
// println(NSString (data: data, encoding: NSUTF8StringEncoding))
var jsonError: NSError?
var jsonResult: AnyObject!
do {
jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)
} catch var error as NSError {
jsonError = error
jsonResult = nil
} catch {
fatalError()
}
if let error = jsonError {
syncCompletion(.Error (error))
return
}
// Interpret the JSON
if let a = jsonResult as? [[String: AnyObject]] {
syncCompletion(.Dataset (a))
}
else if let d = jsonResult as? [String: AnyObject] {
if let e : AnyObject = d["error"] {
if let m : AnyObject = d["message"] {
syncCompletion(.Error (NSError(domain: "SODA", code: 0, userInfo: ["Error": m])))
return
}
}
syncCompletion(.Dataset ([d]))
}
else {
syncCompletion(.Error (NSError(domain: "SODA", code: 0, userInfo: nil)))
}
})
Solved, I replaced:
if reqError != nil
With
if let error = reqError
and:
syncCompletion(.Error (reqError))
with
syncCompletion(.Error (error))
I am using the code below to check for an internet connection. It was working correctly but after the Swift 2.0 update I now see an error on the line var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: nil) as NSData? saying extra argument 'error' in call.
class func isConnectedToNetwork()->Bool{
var Status:Bool = false
let url = NSURL(string: "http://google.com/")
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "HEAD"
request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
request.timeoutInterval = 10.0
var response: NSURLResponse?
var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: nil) as NSData?
if let httpResponse = response as? NSHTTPURLResponse {
if httpResponse.statusCode == 200 {
Status = true
}
}
return Status
}
Do you have any ideas what I should change it to?
Thanks
If you look at apples documentation (https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/#//apple_ref/occ/clm/NSURLConnection/sendSynchronousRequest:returningResponse:error:) you'll see that the definition changed to this:
class func sendSynchronousRequest(_ request: NSURLRequest,
returningResponse response: AutoreleasingUnsafeMutablePointer<NSURLResponse?>) throws -> NSData
They have removed the error parameter and the method throws now an ErrorType, if the request fails. So this should work:
do {
let data = try NSURLConnection.sendSynchronousRequest(request, returningResponse: &response)
} catch (let e) {
print(e)
}
However you shouldn't use this method: It's deprecated in favor of NSURLSession since iOS 9 and OS X 10.11.