Recover in Swift

Swift makes it possible to recover from a fatal error, by using the defer statement and try-catch blocks. This can stop a fatal error from terminating the program and let it continue with execution instead.

An example of where this can be useful: a server wouldn’t want to crash if one of the client connections exhibits a critical error. Instead, the server would want to close that connection and continue serving other clients.

import Foundation

// This function throws an error.
func mayThrow() throws {
    throw NSError(domain: "ExampleDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "a problem"])
}

func main() {
    // defer must be called before the potential error-throwing code.
    // It will be executed when the scope is exited, whether normally or due to an error.
    defer {
        if let error = try? String(reflecting: NSException.current()) {
            print("Recovered. Error:\n", error)
        }
    }

    do {
        try mayThrow()
    } catch {
        // This catch block is necessary to trigger the defer block,
        // but we don't handle the error here.
        NSException.raise(NSExceptionName(rawValue: "CustomException"), format: "%@", arguments: getVaList([error.localizedDescription]))
    }

    // This code will not run, because mayThrow() throws an error.
    // The execution of main stops at the point of the error and resumes in the deferred closure.
    print("After mayThrow()")
}

main()

To run the program:

$ swift recover.swift
Recovered. Error:
 CustomException: a problem

In Swift, we use throws, try, catch, and defer to handle errors and perform cleanup operations. The defer block is similar to Go’s deferred function, executing when the scope is exited. We simulate Go’s panic and recover using NSException and a combination of defer and do-catch blocks.