Skip to Content
Course content

241: Isolation Regions and Sendable Checking

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you have a high-security physical file folder. Inside this folder is a sensitive document that isn't "digitized"—meaning it can't be emailed or copied (this is our non-Sendable type). Normally, if you wanted to give that document to a colleague in another building, you'd be told it's impossible because the document is too fragile to move. However, if you put the document inside a locked, tracked briefcase (the isolation region) and physically carry it to the other building, the security guards let you through. They aren't worried about the document itself being "portable"; they are worried about the process of moving it. As long as the document stays within that tracked briefcase during the transfer and remains under a single person's control at any given time, the safety rules are satisfied.

In Swift, an Isolation Region is essentially that tracked briefcase. For a long time, the compiler was binary: either a type was Sendable (safe to move across actor boundaries) or it wasn't. If it wasn't, the compiler would stop you dead in your tracks the moment you tried to pass it to an actor. But the Swift 6 compiler is smarter. It now tracks the "region" of isolation. If it can prove that a non-Sendable object is being moved from one isolated context to another without ever being accessed concurrently, it lets the code slide. It's not that the object suddenly became Sendable; it's that the compiler has proven the region of access is safe.

The "Wait, Why is This Allowed?" Moment

You've probably run into this if you've started experimenting with Swift 6 mode. You'll see a non-Sendable class being passed into an actor method, and you'll expect a warning. But sometimes, the warning doesn't appear. Here is why.

class UserSettings {
    var theme: String = "Dark"
}

actor SettingsManager {
    var currentSettings: UserSettings?

    func applySettings(_ settings: UserSettings) {
        // The compiler sees 'settings' entering the isolation region of SettingsManager.
        // Since it's passed here and then stored, and not leaked to a 
        // background thread simultaneously, it's considered safe.
        self.currentSettings = settings
    }
}

func setup() async {
    let mySettings = UserSettings() 
    let manager = SettingsManager()
    
    // This looks like a Sendability violation, but the compiler 
    // identifies an isolation region transfer.
    await manager.applySettings(mySettings)
}

In this snippet, UserSettings is a plain class—definitely not Sendable. In older versions of Swift, await manager.applySettings(mySettings) would have triggered a warning. But now, the compiler tracks the "region." It sees that mySettings is created in the setup function and then handed over to the SettingsManager actor. Because there's no way for setup to keep mutating mySettings while SettingsManager is using it, the isolation is preserved.

Where the Region Breaks

The magic fails the moment you try to "leak" the object out of that tracked briefcase. The compiler is great at tracking linear transfers, but it can't track objects that escape into closures that might run whenever, wherever.

I like to think of this as leaving the briefcase open in a public hallway. Suddenly, the security guard (the compiler) has no idea who is touching the document.

actor SettingsManager {
    func processSettings(_ settings: UserSettings) {
        // This is fine... until we do this:
        Task {
            // ERROR: Capture of 'settings' with non-sendable type 'UserSettings' 
            // in a @Sendable closure
            print("Processing \(settings.theme) in background")
        }
    }
}

Inside the Task closure, we've broken the isolation region. Task creates a new asynchronous context that is @Sendable. Because the closure could run at any time on any thread, the compiler can no longer guarantee that the settings object isn't being modified elsewhere. The "briefcase" has been opened, and the Sendable checking kicks back in with full force.

Balancing Strictness and Flexibility

You might be tempted to just mark everything as @unchecked Sendable to make these warnings go away. Please, don't. That's essentially telling the compiler, "Trust me, I'm a professional," right before you accidentally introduce a data race that crashes your app in production. Isolation regions are designed to give you flexibility without sacrificing safety. When you see a Sendable warning despite using isolation regions, it's usually a sign that your data flow is too complex or your closures are escaping in ways that could actually cause a crash.




📋 Practical Task

Fixing the Leaky Database Connection

You are working on a legacy codebase where a DatabaseConnection class is not Sendable because it manages a raw C-pointer to a socket. You have a DatabaseCoordinator actor that is supposed to handle these connections.

The following code currently fails to compile in Swift 6 mode because the isolation region is being breached. Your task is to modify the DatabaseCoordinator so that the connection is handled within the actor's isolation region without using @unchecked Sendable.

class DatabaseConnection {
    var connectionId: Int = 123
    func query() { print("Querying database...") }
}

actor DatabaseCoordinator {
    func performQuery(with connection: DatabaseConnection) {
        // BUG: This closure breaks the isolation region
        Task {
            connection.query()
        }
    }
}

// Execution context
func runDatabaseTask() async {
    let conn = DatabaseConnection()
    let coordinator = DatabaseCoordinator()
    await coordinator.performQuery(with: conn)
}

Your Goal: Modify performQuery so that it executes the query without breaking the isolation region. Hint: Think about whether the work actually needs to be wrapped in a new Task, or if it can be performed directly within the actor's current isolated context.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.