WishIHadThreeGuns
WishIHadThreeGuns

Reputation: 1469

Throw from a trailing closure in Swift

I'm doing some networking and want to throw from the trailing closure according to the response.

As a result, I've found that I'm unable to do so with an "invalid conversion" error.

I've reduced the problem to a minimal example - no cliches allowed:

func innerClosure(a: String, completion: (String)->Void) {
    completion(a + ", World")
}

func iCanThrow(name: String, closure: (String,(String)->Void)->(Void) ) throws {
     closure(name, {response in
        print (response)
        if response == "Hello, World" {
            throw ClicheError.cliche
        }
    })

}

try iCanThrow(name: "Hello", closure: innerClosure)

Ad you can see I want to throw a ClicheError from within the trailing closure - but the specific error is Invalid conversion from throwing function of type '(String) throws -> Void' to non-throwing function type '(String) -> Void'

I've tried using various combinations of throws and rethrows but I simply can't get this to work - even if I get the inner closure to throw I get the following:

enum ClicheError: Error {
    case cliche
    case latecliche
}

func innerClosure(a: String, completion: (String) throws ->Void) {
    try? completion(a + ", World")
}

func iCanThrow(name: String, closure: (String,(String) throws ->Void) throws ->(Void) ) throws {
     try closure(name, {response in
        print (response)
        if response == "Hello, World" {
            throw ClicheError.cliche
        }
    })

}

try iCanThrow(name: "Hello", closure: innerClosure)

Which compiles, throws but doesn't actually throw from iCanThrow - meaning I can't throw!

Upvotes: 0

Views: 272

Answers (1)

Martin R
Martin R

Reputation: 539765

func innerClosure(a: String, completion: (String) throws ->Void) {
    try? completion(a + ", World")
}

does not throw an error because try? is an “optional try”: It returns an Optional which is nil in the case of an error. Here the return value is ignored, so that errors are ignored silently.

What you want is

func innerClosure(a: String, completion: (String) throws ->Void) rethrows {
    try completion(a + ", World")
}

which makes innerClosure() a throwing function only if called with a throwing parameter.

func iCanThrow() can also be marked as rethrows instead of throws because it does not throw an error “on its own.” See also What are the differences between throws and rethrows in Swift?.

Upvotes: 4

Related Questions