Swift 2 closure with throw

Hello guys,


While using Swift 2 I got one problem – how to throw inside closure of function. Example:


// this func can't be changed and modified

func httpRequest(param:String, completionHTTP:(result:String) -> Void){

completionHTTP(result:"some result")

}


// this func can be changed and modified

func myFunc() throws {

httpRequest("param1") { (result) -> Void in

throw NSError(domain: "MyDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Error description"])

}

}


myFunc - gives error of invalid conversion from thowing function to non-throwing function


How can I call throw inside of closure of non-throwing func?

No way. You cannot throw any sort of errors from within the func or closure.

All errors thrown inside the non-throws funcs, you need to enclose the possibly-throws line with do-catch and need to catch all possible errors, the error cannot be propagated out of the func or closure.


You need to find another way to handle errors.

ADDITION: a possible solution:

func myFunc(onError errorHandler: (NSError -> Void)?) {
    httpRequest("param1") { result in
        errorHandler?(NSError(domain: "MyDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Error description"]))
    }
}

I think we have another case of "ah Swift has now exceptions" - It HAS NOT. The throw/catch in Swift is just syntax sugar for the NSError idiom.

That's true. But stack rewinding based exception does not work as expected in most multi-threaded/asynchronous cases. I believe the httpRequest method in the original post is actually (if not, then it should be) implemented in an asynchronous manner.

Yes you are right of course. I'm just so annoyed by all the new guys in the forum that don't get that Swift2 has no Exceptions. It appears to be really hard to crasp for them.

I now see your point. With some other features of Swift2, I feel every day nearly the same.

True, but to be totally fair, Apple kind of set themselves up for this by adopting the "try-throw-catch" idiom that is already heavily associated with exceptions in many popular languages. Semantics do matter; if they had called it "maybe-fail-then" (or something :) ), there may be far less confusion on this particular point

I would guess that the most famous exception model is the one in Java, and the part of that model that uses checked exceptions is more or less the same as the one in Swift. Compared to Java, Swift lacks unchecked exceptions, that's all, which is hardly surprisning, given that Swift is all about static typing.

They have very simmilar syntax - but Swift doesn't have exceptions, it just wraps NSError in a better readable syntax. What is so hard to understand at this?

You could make a file that looks like this:


class ErrorHolder {
    private var error: ErrorType? = nil
}

func trySomething(@noescape closure: (ErrorHolder) -> ()) throws {
    let errorHolder = ErrorHolder()

    closure(errorHolder)

    if let error = errorHolder.error {
        throw error
    }
}

func trySomethingInClosure(errorHolder: ErrorHolder, @noescape closure: () throws -> ()) {
  do {
      try closure()
  } catch {
      errorHolder.error = error
  }
}


Then, whenever you have a function like this:


func doSomething(@noescape closure: () -> ()) {
    closure()
}


and you want to try to call it like this, but can't, due to the compiler error:


do {
    doSomething { // error: invalid conversion from throwing function of type '() throws -> _' to non-throwing function type '@noescape () -> ()'
        throw NSError(domain: "Foo", code: 1, userInfo: nil)
    }
} catch {
    print("Something went wrong: \(error)")
}


You can just invoke the trySomething functions like so:


do {
    try trySomething { errorHolder in
        doSomething {
            trySomethingInClosure(errorHolder) {
                throw NSError(domain: "Foo", code: 1, userInfo: nil)
            }
        }
    }
} catch {
    print("Something went wrong: \(error)")
}


and get the output:


Something went wrong: Error Domain=Foo Code=1 "The operation couldn’t be completed. (Foo error 1.)"


It introduces a little pyramid-of-doom-ness into the code, but it will work to let you propagate errors out of a closure. Of course, this won't work with async code.

In that case, Swift doesn't have function calls, it just puts values in registers and pushes the return address on the stack. This, however, is not very meaningful.


If you want to argue that Java checked exceptions are exceptions, but Swift's are not, then you have to present some kind of difference between them.

Swift 2 closure with throw
 
 
Q