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

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


Yes, it is possible!

Use: fatalError("your message here") to throw runtime exception


To elaborate on Максим Мартынов's answer, Swift has 3 ways to do throw undeclared, uncatchable errors (but other approaches are possible if you want to venture outside Swift's standard library). These are based on the 3 levels of optimization:

  1. -Onone: No optimization; debug build
  2. -O: Normal optimization; release build
  3. -O SWIFT_DISABLE_SAFETY_CHECKS: Unchecked optimization; extremely optimized build

1. assertionFailure(_:)

Write this line when you're doing debugging tests and hit a line you don't think should ever be hit. These are removed in non-debug builds, so you must assume they will never be hit in the production app.

This has a sister function called assert(_:_:), which lets you assert at runtime whether a condition is true. assertionFailure(_:) is what you write when you know the situation is always bad, but don't think that'll harm the production code very much.

Usage:

if color.red > 0 {    assertionFailure("The UI should have guaranteed the red level stays at 0")    color = NSColor(red: 0, green: color.green, blue: color.blue)}

2. preconditionFailure(_:)

Write this line when you're sure some condition you've described (in documentation, etc.) was not met. This works like assertionFailure(_:), but in release builds as well as debug ones.

Like assertionFailure(_:), this one's got a sister function called precondition(_:_:), which lets you decide at runtime whether a precondition was met. preconditionFailure(_:) is essentially that, but assuming the precondition is never met once the program gets to that line.

Usage:

guard index >= 0 else {    preconditionFailure("You passed a negative number as an array index")    return nil}

Note that, in extremely optimized builds, it is not defined what happens if this line is hit! So if you don't want your app to wig out if it might ever hit this, then make sure the error state is handleable.

3. fatalError(_:)

Used as a last resort. When every other attempt to save the day has failed, here is your nuke. After printing the message you pass to it (along with the file and line number), the program stops dead in its tracks.

Once the program gets to this line, this line always runs, and the program never continues. This is true even in extremely optimized builds.

Usage:

#if arch(arm) || arch(arm64)    fatalError("This app cannot run on this processor")#endif

Further reading: Swift Assertions by Andy Bargh


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.