The Swift defer
statement allows you to execute code right before a function returns. This post presents a number of example use cases where defer
can be used in Swift:
Close Files with Defer
The defer
statement allows you to ensure a block of code is executed at the end of a function, for example, closing a file:
func write(data: Data, offset: Int) throws {
// Compute the filepath to write to
let documentsPath = NSSearchPathForDirectoriesInDomains(
.documentDirectory,
.userDomainMask,
true
)[0]
let filename = "data.txt"
let filepath = "\(documentsPath)/\(filename)"
// Open a file handle
guard let file = FileHandle(forUpdatingAtPath: filepath) else {
throw WriteError.notFound
}
// Ensure the file is closed at the end of this function
defer {
try? file.close()
}
// Seek to specified offset
try file.seek(toOffset: UInt64(offset))
// Write data at specified offset
file.write(data)
}
Release Locks with Defer
A defer
statement can ensure critical state is updated before a function returns even if your code has multiple branches and paths, like in the case of a lock:
func criticalSectionWork(task: Task, lock: NSLock) -> Int {
// lock.unlock() will always be called before
// the return statement
defer {
lock.unlock()
}
// Handle results
let result = task.perform()
if result == .success {
modifyDatabase(task: task)
return 0
}
else if result == .error {
return -1
}
else {
return -2
}
}
Call Completion Blocks Using Defer
Managing state with multiple branches and cases can lead to error prone code. A defe
r statement can be used to ensure a completion block is called with the correct variables regardless of the branches taken:
func process(tasks: [Tasks], completion: ([Int]?, Error?) -> Void) {
var results = [Int]()
var error: Error?
// completion will be always executed when this function ends,
// even if there are multiple branches of logic later in the
// function implementation
defer {
completion(results, error)
}
for task in tasks {
do {
results.append(task.perform())
}
catch let taskError {
error = taskError
break
}
}
if let error = error {
results = nil
}
}
Defer in Swift
That’s it! By using defer
in Swift you can close files, release locks, and call completion blocks.