Async cleanup with defer in Swift
Learn how to perform asynchronous cleanup operations using Swift's async defer support.
14 Jun 2026 · 3 min read
Swift's defer statement is useful for cleanup logic. It guarantees that a block of code runs when the current scope exits, regardless of whether execution completes normally, throws an error, or returns early.
Before Swift 6.4, we couldn't perform asynchronous operations directly inside a defer block. Starting with Swift 6.4, it is possible.

The limitation of defer without async support
Consider a function that opens a resource and needs to close it once the work is finished:
func processFile() async throws {let file = try await openFile()defer {file.close()}try await process(file)}
This works as long as cleanup is synchronous. However, many modern APIs are asynchronous. If closing the file requires an await, the code above no longer works:
defer {await file.close() // Compiler error on Swift version < 6.4}
To solve the compiler error, we had to move the cleanup logic elsewhere:
let file = try await openFile()do {try await process(file)await file.close()} catch {await file.close()throw error}
Besides introducing duplication, this makes the cleanup logic less obvious because it is separated from the resource acquisition.
Async cleanup with defer
With defer supporting asynchronous operations, we can keep acquisition and cleanup next to each other:
func processFile() async throws {let file = try await openFile()defer {await file.close()}try await process(file)}
The cleanup operation is now guaranteed to run when the scope exits, while still supporting asynchronous work.
Note on actor isolation
A defer block inherits the actor isolation of its enclosing scope. If the surrounding function runs on @MainActor, the cleanup code inside defer also runs on @MainActor.



Newsletter
Like to support my work?
Say hi
Related tags
Articles with related topics
Latest articles and tips



