2
votes

error handling with swift 2.0 is different and ran into the wall when was trying to setup the NSURL Session and using the completion handler where the error parameter is available in swift 1.2 but when looking at documentation the error parameters is no longer there and it is trying to tell me to use throwing function statements instead, not really familiar with syntax. The block quotes are where the error messages appear and this code works just fine with Xcode 6.4 or earlier but not with 7.0

/Volumes/MyData/AppDevXcode7/Training/iOS8.0Dev/connecting_swift/connecting_swift/JSONViewController.swift:28:78: Invalid conversion from throwing function of type '(_, _, _) throws -> _' to non-throwing function type '(NSData?, NSURLResponse?, NSError?) -> Void'

@IBAction func callURLButtonTapped(sender: AnyObject){
    urlTextField.resignFirstResponder()
    let requestedURL = NSURL(string: urlTextField.text!)
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithURL(requestedURL!, completionHandler: {data, response, error in
            if let actualError = error {
                let errorResponse = "Response status: \(actualError.description)"
                self.responseText.text = errorResponse
            }else{
                var parseError: NSError?
                let jsonArray = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError ) as! NSDictionary

                    if let actualParseError = parseError {
                        let errorResponse = "Response status: \(actualParseError.description)"
                        self.responseText.text = errorResponse
                    }else{
                        dispatch_async(dispatch_get_main_queue(), {
                            let httpResponse = response as! NSHTTPURLResponse
                            let responseStatus = "Response status: \(httpResponse.statusCode)"
                            self.responseStatusLabel.text = responseStatus
                            let responseAsString = jsonArray.description
                            self.responseText.text = responseAsString
                        })
                    }

                }
    })
    task.resume()
}
2

2 Answers

1
votes

Error messages in the current Swift compiler aren't always indicative of the root cause of a problem. (In particular, in cases like this where there's a type-checking error deep within a closure that's passed to some function, it gives an error message about the closure-taking function instead of the root cause.) File bugs when you see these kinds of errors and Apple can get better about giving good error messages.

Here, it's correct that there's a problem regarding the new error handling syntax, but it gets the location wrong (because of that nested closure thing). Your problem is on this line:

let jsonArray = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError ) as! NSDictionary

The NSJSONSerialization.JSONObjectWithData(_:options:error:) call is one of those methods that got turned into a throws function with Swift 2.0. So you need to call it like this:

let jsonArray = try NSJSONSerialization.JSONObjectWithData(data, options: [] ) as! NSDictionary

Note also that the nil options parameter becomes an empty OptionSetType literal (empty brackets), because option sets are actual sets in Swift 2.

But wait... you still get a compile error here because data is an optional that has to be checked/unwrapped. And you need an appropriate place to handle your errors. Let's revamp this method to handle everything in the right places:

@IBAction func callURLButtonTapped(sender: AnyObject){
    urlTextField.resignFirstResponder()
    let requestedURL = NSURL(string: urlTextField.text!)
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithURL(requestedURL!, completionHandler: {data, response, error in
        // First, make sure we have real data, and handle the error if not.
        // (That's a better use of the API contract than checking the error parameter, because the error parameter is not guaranteed to be non-nil in all cases where correct data is received.)
        // Use a guard clause to keep the "good path" less indented.
        guard let actualData = data else {
            self.responseText.text = "Response status: \(error!.description)"
            return
        }
        do {
            // Use do/try/catch to call the new throwing API.
            // Use the new OptionSetType syntax, too.
            let jsonArray = try NSJSONSerialization.JSONObjectWithData(actualData, options: [])
            dispatch_async(dispatch_get_main_queue(), {
                let httpResponse = response as! NSHTTPURLResponse
                self.responseStatusLabel.text = "Response status: \(httpResponse.statusCode)"
                self.responseText.text = jsonArray.description
            })
        } catch let parseError {
            // No need to treat as NSError and call description here, because ErrorTypes are guaranteed to be describable.
            self.responseText.text = "Response status: \(parseError)"
        }
    })
    task.resume()
}
0
votes

In Swift 2, XCode 7, Apple replaced NSError with ErrorType.

You use NSError in your own code explicitly. This works in most cases (like assigning) but not in all. Replace your own NSError usage with ErrorType.