Search code examples
swiftexceptionruntimeexception

Is it possible to throw a "RuntimeException" in Swift without declaring it?


I would like to throw an exception from some "deep" function, so it bubbles up to another function, where I want to catch it.

f1 calls f2 calls f3 calls ... fN which may throw an error

I would like to catch the error from f1.

I've read that in Swift I have to declare all methods with throws, and also call them using try.

But that's quite annoying:

enum MyErrorType : ErrorType {
    case SomeError
}

func f1() {
    do {
        try f2()
    } catch {
        print("recovered")
    }
}

func f2() throws {
    try f3()
}

func f3() throws {
    try f4()
}

...

func fN() throws {
    if (someCondition) {
      throw MyErrorType.SomeError
    }
}

Isn't there a similar concept to the RuntimeException in Java, where throws doesn't leak all the way up the call chain?


Solution

  • The error handling mechanism in Swift does not involve raising unchecked (runtime) exceptions. Instead, explicit error handling is required. Swift is certainly not the only recently designed language to go for this design – for instance Rust and Go also in their own ways also require explicitly describing the error paths in your code. In Objective-C the unchecked exception feature exists, but is largely used only for communicating programmer errors, with the notable exception of a few key Cocoa classes such as NSFileHandle which tends to catch people out.

    Technically you do have the ability to raise Objective-C exceptions in Swift with the use of NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise() as is explained in this excellent answer to this question, arguably a duplicate of your question. You really shouldn't raise NSExceptions though (not least because you have no Objective-C exception catching language feature available to you in Swift).

    Why did they go with this design? Apple's "Error Handling in Swift 2.0" document explains the rationale clearly. Quoting from there:

    This approach […] is very similar to the error handling model manually implemented in Objective-C with the NSError convention. Notably, the approach preserves these advantages of this convention:

    • Whether a method produces an error (or not) is an explicit part of its API contract.
    • Methods default to not producing errors unless they are explicitly marked.
    • The control flow within a function is still mostly explicit: a maintainer can tell exactly which statements can produce an error, and a simple inspection reveals how the function reacts to the error.
    • Throwing an error provides similar performance to allocating an error and returning it – it isn’t an expensive, table-based stack unwinding process. Cocoa APIs using standard NSError patterns can be imported into this world automatically. Other common patterns (e.g. CFError, errno) can be added to the model in future versions of Swift.

    […]

    As to basic syntax, we decided to stick with the familiar language of exception handling. […] by and large, error propagation in this proposal works like it does in exception handling, and people are inevitably going to make the connection.