Logo for tanaschita.com

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.

Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE

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.

Sponsorship logo
Preparing for a technical iOS job interview
Preparing for a technical iOS Job Interview with over 300 questions & answers. Covering Swift & Objective-C, SwiftUI & UIKit, Combine, HTTP Networking, iOS File System, Core Data, Concurrency with async/await, Security, Automated Testing, Dependency Management, AI & Machine Learning and more.
LEARN MORE
Sponsorship logo
Architecture & Design Patterns for iOS
This book is a practical guide to essential architectural principles and design patterns for iOS development. It covers strategies for building maintainable apps with Swift and SwiftUI, including dependency injection, navigation, common patterns, and modularization.
LEARN MORE
Sponsorship logo
Become a sponsor of tanaschita.com
By publishing an article on different iOS topics every week, tanaschita.com is constantly growing in the developer community and may provide a great audience for you as a sponsor.
CLICK TO LEARN MORE

Newsletter

Image of a reading marmot
Subscribe

Like to support my work?

Say hi

Related tags

Articles with related topics

async/await

concurrency

swift

Understanding Sendable in Swift

Learn what Sendable means in Swift concurrency and how it helps prevent data races.

06 Jul 2026 · 8 min read

Latest articles and tips

© 2026 tanaschita.com

Privacy policy

Impressum