问题
I'm trying to add a throws
to my existing function with a completion handler but I keep getting a warning saying no calls throwing functions occur within try expression
. In the section where I throw the errors, I get an error saying
invalid conversion from throwing function of type '() throwing -> Void' to non-throwing function type.
enum LoginError: ErrorType {
case Invalid_Credentials
case Unable_To_Access_Login
case User_Not_Found
}
@IBAction func loginPressed(sender: AnyObject) {
do{
try self.login3(dict, completion: { (result) -> Void in
if (result == true)
{
self.performSegueWithIdentifier("loginSegue", sender: nil)
}
})
}
catch LoginError.User_Not_Found
{
//deal with it
}
catch LoginError.Unable_To_Access_Login
{
//deal with it
}
catch LoginError.Invalid_Credentials
{
//deal with it
}
catch
{
print("i dunno")
}
}
func login3(params:[String: String], completion: (result:Bool) throws -> Void)
{
//Request set up
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves) as? NSDictionary
if let parseJSON = json
{
let userID = parseJSON["user_id"] as? Int
let loginError = parseJSON["user_not_found"] as? String
let validationError = parseJSON["invalid_credentials"] as? String
let exception = parseJSON["unable_to_access_login"] as? String
var responseArray = [(parseJSON["user_id"] as? Int)]
if userID != nil
{
dispatch_async(dispatch_get_main_queue()) {
completion(result:true)
}
}
else if loginError != ""
{
dispatch_async(dispatch_get_main_queue()){
completion(result: false)
self.loginErrorLabel.text = loginError
throw LoginError.User_Not_Found
}
}
else if validationError != ""
{
dispatch_async(dispatch_get_main_queue()){
completion(result:false)
self.validationErrorLabel.text = validationError
throw LoginError.Invalid_Credentials
}
}
else if exception != nil
{
dispatch_async(dispatch_get_main_queue()){
completion(result:false)
self.exceptionErrorLabel.text = "Unable to login"
throw LoginError.Unable_To_Access_Login
}
}
}
else
{
}
}
catch let parseError {
// Log the error thrown by `JSONObjectWithData`
})
task.resume()
}
回答1:
What you can do is encapsulating the error into a throwable closure like in the following code to achieve what you want:
func login3(params:[String: String], completion: (inner: () throws -> Bool) -> ()) {
let task = session.dataTaskWithRequest(request, completionHandler: { data, response, error -> Void in
let json = try NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves) as? NSDictionary
if let parseJSON = json {
let userID = parseJSON["user_id"] as? Int
let loginError = parseJSON["user_not_found"] as? String
let validationError = parseJSON["invalid_credentials"] as? String
let exception = parseJSON["unable_to_access_login"] as? String
var responseArray = [(parseJSON["user_id"] as? Int)]
if userID != nil {
dispatch_async(dispatch_get_main_queue()) {
completion(inner: { return true })
}
}
else if loginError != ""
{
dispatch_async(dispatch_get_main_queue()) {
self.loginErrorLabel.text = loginError
completion(inner: { throw LoginError.User_Not_Found })
}
}
else if validationError != ""
{
dispatch_async(dispatch_get_main_queue()) {
self.validationErrorLabel.text = validationError
completion(inner: {throw LoginError.Invalid_Credentials})
}
}
else if exception != nil
{
dispatch_async(dispatch_get_main_queue()){
self.exceptionErrorLabel.text = "Unable to login"
completion(inner: {throw LoginError.Unable_To_Access_Login})
}
}
}
else
{
}
}
task.resume()
}
And the you can call it like in the following way:
self.login3(dict) { (inner: () throws -> Bool) -> Void in
do {
let result = try inner()
self.performSegueWithIdentifier("loginSegue", sender: nil)
} catch let error {
print(error)
}
}
The trick is that the login3
function takes an additional closure called 'inner'
of the type () throws -> Bool
. This closure will either provide the result of the computation, or it will throw. The closure itself is being constructed during the computation by one of two means:
- In case of an error:
inner: {throw error}
- In case of success:
inner: {return result}
I strongly recommend you an excellent article about using try/catch
in async calls Using try / catch in Swift with asynchronous closures
I hope this help you.
回答2:
You're asking for X and I'm answering Y, but just in case...
There's always the possibility to add the throwing capability to your function instead of the completion handler:
func login3(params:[String: String], completion: (result:Bool) -> Void) throws {
...
}
Then you can call it from inside IBAction:
do {
try self.login3(dict) { result -> Void in
...
}
} catch {
print(error)
}
回答3:
Read the below with a grain of salt. I haven't worked much with Swift 2.0 yet:
You created a "dataTask" task
who's's completion handler has a throw in it, but the only actual code in your login3 method is task.resume()
. The completion handler won't get executed until after login3
returns. (In fact, it's a parameter to another object, so the compiler has no idea what's going to happen with that code.)
As I understand it, the actual top-to-bottom body of your login3
method must contain a throw. Since it's an async method, you can't do that. Thus, don't make your login3
function throw. Instead have it pass an error object to it's completion handler.
回答4:
In my impression this is caused by the signature of your functions. In @IBAction func loginPressed(sender: AnyObject)
you don't have to use try when calling login3
as the function itself is not marked as throwing but the completion handler is. But in fact the completion closure of login3
will never throw as you execute all throwing functions inside a do {} catch {}
block, so you could try to remove the throws
annotation from the login3
completion closure and also call the closure if you catch an error in login3
with an according result. This way you handle all throwing functions inside of login3
in a do {} catch {}
block and call the completion handler with a suitable value.
Generally I am also not aware that you can catch without a preceding do {}
block like you did in the @IBAction.
来源:https://stackoverflow.com/questions/33402348/cant-get-throws-to-work-with-function-with-completion-handler