Isn't there a similar concept to the RuntimeException
in Java, where throws
doesn't leak all the way up the call chain?
Swift does indeed have error handling which doesn't propegate at compile time.
However, before I discuss those, I must say that the one you point out, where you use the language's do...catch
, try
, throw
, and throws
keywords/features to handle errors, is by far the safest and most preferred. This ensures that every single time an error might be thrown or caught, it's handled correctly. This completely eliminates surprise errors, making all code more safe and predictable. Because of that inherent compile- and run-time safety, you should use this wherever you can.
func loadPreferences() throws -> Data {
return try Data(contentsOf: preferencesResourceUrl, options: [.mappedIfSafe, .uncached])
}
func start() {
do {
self.preferences = try loadPreferences()
}
catch {
print("Failed to load preferences", error)
assertionFailure()
}
}
guard let fileSizeInBytes = try? FileManager.default.attributesOfItem(atPath: path)[.size] as? Int64 else {
assertionFailure("Couldn't get file size")
return false
}
Probably the easiest way to silence Swift's compiler is with try!
- this will allow you to use native Swift errors, but also ignore them.
Here's what your example code would look like with that:
enum MyErrorType : ErrorType {
case SomeError
}
func f1() {
f2()
}
func f2() {
f3()
}
func f3() {
try! f4()
}
...
func fN() throws {
if (someCondition) {
throw MyErrorType.SomeError
}
}
Obviously, this has the problem of not allowing you to ever catch these, so if you want a silent error you can catch, read on.
There are also assertion
s, precondition
s, and fatalError
s, which I described in detail in my answer from October of 2017. The compiler provides reasonable handling of these, such as ensuring that return statements and other control flow are placed and omitted when appropriate. Like try!
, however, these cannot be caught.
exit
is in this family if your goal is to stop the program immediately.
If you venture outside Swift into the wider Apple ecosystem (that is, if you are writing Swift on an Apple platform), you also see Objective-C's NSException
. As you desire, this can be thrown by Swift without using any language features guarding against that. Make sure you document that! However, this cannot be caught by Swift alone! You can write a thin Objective-C wrapper that lets you interact with it in the Swift world.
func silentButDeadly() {
// ... some operations ...
guard !shouldThrow else {
NSException.raise(NSExceptionName("Deadly and silent", format: "Could not handle %@", arguments: withVaList([problematicValue], {$0}))
return
}
// ... some operations ...
}
func devilMayCare() {
// ... some operations ...
silentButDeadly()
// ... some operations ...
}
func moreCautious() {
do {
try ObjC.catchException {
devilMayCare()
}
}
catch {
print("An NSException was thrown:", error)
assertionFailure()
}
}
Of course, if you're writing Swift in a Unix environment, you still have access to the terrifying world of Unix interrupts. You can use Grand Central Dispatch to both throw and catch these. And, as you desire, there's no way for the compiler to guard against them being thrown.
import Dispatch // or Foundation
signal(SIGINT, SIG_IGN) // // Make sure the signal does not terminate the application.
let sigintSource = DispatchSource.makeSignalSource(signal: SIGINT, queue: .main)
sigintSource.setEventHandler {
print("Got SIGINT")
// ...
exit(0)
}
sigintSource.resume()
exit
is in this family if your goal is to trap it and read its code.