Defer in Swift

Swift provides the defer keyword to ensure that a function call is performed later in a program’s execution, usually for purposes of cleanup. defer is often used where try-finally would be used in other languages.

Suppose we wanted to create a file, write to it, and then close when we’re done. Here’s how we could do that with defer:

import Foundation

func main() {
    let f = createFile("/tmp/defer.txt")
    defer { closeFile(f) }
    writeFile(f)
}

func createFile(_ path: String) -> FileHandle {
    print("creating")
    guard let file = FileHandle(forWritingAtPath: path) else {
        fatalError("Unable to create file")
    }
    return file
}

func writeFile(_ file: FileHandle) {
    print("writing")
    if let data = "data\n".data(using: .utf8) {
        file.write(data)
    }
}

func closeFile(_ file: FileHandle) {
    print("closing")
    do {
        try file.close()
    } catch {
        print("error: \(error)")
        exit(1)
    }
}

main()

Immediately after getting a file object with createFile, we defer the closing of that file with closeFile. This will be executed at the end of the enclosing function (main), after writeFile has finished.

It’s important to check for errors when closing a file, even in a deferred function. In Swift, we use a do-catch block to handle potential errors when closing the file.

Running the program confirms that the file is closed after being written:

$ swift defer.swift
creating
writing
closing

In Swift, the defer statement provides a clean way to ensure that necessary cleanup code is executed, regardless of how a function exits. This can be particularly useful for resource management, such as closing files or network connections.

Note that Swift’s defer statements are executed in reverse order of their appearance. If you have multiple defer statements in the same scope, the last one will be executed first.