Thursday, September 11, 2025
HomeiOS DevelopmentFixing “Seize of non-sendable kind in @Sendable closure” in Swift

Fixing “Seize of non-sendable kind in @Sendable closure” in Swift


Printed on: August 7, 2024

When you begin migrating to the Swift 6 language mode, you will most definitely activate strict concurrency first. As soon as you have performed this there will likely be a number of warings and errors that you will encounter and these errors could be complicated at occasions.

I am going to begin by saying that having a stable understanding of actors, sendable, and information races is a large benefit if you need to undertake the Swift 6 language mode. Just about all the warnings you will get in strict concurrency mode will inform you about potential points associated to operating code concurrently. For an in-depth understanding of actors, sendability and information races I extremely advocate that you just check out my Swift Concurrency course which is able to get you entry to a sequence of movies, workout routines, and my Sensible Swift Concurrency ebook with a single buy.

WIth that out of the best way, let’s check out the next warning that you just may encounter in your venture:

Seize of non-sendable kind in @Sendable closure

This warning tells us that we’re capturing and utilizing a property inside a closure. This closure is marked as @Sendable which implies that we must always count on this closure to run in a concurrent surroundings. The Swift compiler warns us that, as a result of this closure will run concurrently, we must always guarantee that any properties that we seize inside this closure can safely be used from concurrent code.

In different phrases, the compiler is telling us that we’re risking crashes as a result of we’re passing an object that may’t be used from a number of duties to a closure that we must always count on to be run from a number of duties. Or at the least we must always count on our closure to be transferred from one job to a different.

After all, there isn’t any ensures that our code will crash. Neither is it assured that our closure will likely be run from a number of locations on the identical time. What issues right here is that the closure is marked as @Sendable which tells us that we must always guarantee that something that is captured inside the closure can be Sendable.

For a fast overview of Sendability, try my put up on the subject right here.

An instance of the place this warning may happen might appear like this:

func run(accomplished: @escaping TaskCompletion) {
    guard !metaData.isFinished else {
        DispatchQueue.most important.async {
            // Seize of 'accomplished' with non-sendable kind 'TaskCompletion' (aka '(Consequence, any Error>) -> ()') in a `@Sendable` closure; that is an error within the Swift 6 language mode
            // Sending 'accomplished' dangers inflicting information races; that is an error within the Swift 6 language mode
            accomplished(.failure(TUSClientError.uploadIsAlreadyFinished))
        }
        return
    }

    // ...
}

The compiler is telling us that the accomplished closure that we’re receiving within the run operate cannot be handed toDispatchQueue.most important.async safely. The explanation for that is that the run operate is assumed to be run in a single isolation context, and the closure handed to DispatchQueue.most important.async will run in one other isolation context. Or, in different phrases, run and DispatchQueue.most important.async may run as a part of completely different duties or as a part of completely different actors.

To repair this, we want. to guarantee that our TaskCompletion closure is @Sendable so the compiler is aware of that we are able to safely go that closure throughout concurrency boundaries:

// earlier than
typealias TaskCompletion = (Consequence<[ScheduledTask], Error>) -> ()

// after
typealias TaskCompletion = @Sendable (Consequence<[ScheduledTask], Error>) -> ()

In most apps, a repair like it will introduce new warnings of the identical type. The explanation for that is that as a result of the TaskCompletion closure is now @Sendable, the compiler goes to guarantee that each closure handed to our run operate does not captuire any non-sendable sorts.

For instance, one of many locations the place I name this run operate may appear like this:

job.run { [weak self] end in
    // Seize of 'self' with non-sendable kind 'Scheduler?' in a `@Sendable` closure; that is an error within the Swift 6 language mode
    guard let self = self else { return }
    // ...
}

As a result of the closure handed to job.run must be @Sendable any captured sorts additionally have to be made Sendable.

At this level you will typically discover that your refactor is snowballing into one thing a lot larger.

On this case, I must make Scheduler conform to Sendable and there is two methods for me to do this:

  • Conform Scheduler to Sendable
  • Make Scheduler into an actor

The second choice is most definitely the most suitable choice. Making Scheduler an actor would permit me to have mutable state with out information races attributable to actor isolation. Making the Scheduler conform to Sendable with out making it an actor would imply that I’ve to eliminate all mutable state since courses with mutable state cannot be made Sendable.

Utilizing an actor would imply that I can now not immediately entry quite a lot of the state and capabilities on that actor. It would be required to start out awaiting entry which implies that quite a lot of my code has to turn out to be async and wrapped in Activity objects. The refactor would get uncontrolled actual quick that means.

To restrict the scope of my refactor it is sensible to introduce a 3rd, short-term choice:

  • Conform Scheduler to Sendable utilizing the unchecked attribute

For this particular case I bear in mind, I do know that Scheduler was written to be thread-safe. Which means it’s very secure to work with Scheduler from a number of duties, threads, and queues. Nevertheless, this security was applied utilizing outdated mechanisms like DispatchQueue. In consequence, the compiler will not simply settle for my declare that Scheduler is Sendable.

By making use of @unchecked Sendable on this class the compiler will settle for that Scheduler is Sendable and I can proceed my refactor.

As soon as I am able to convert Scheduler to an actor I can take away the @unchecked Sendable, change my class to an actor and proceed updating my code and resolving warnings. That is nice as a result of it means I haven’t got to leap down rabbit gap after rabbit gap which might end in a refactor that will get means out of hand and turns into nearly unimaginable to handle accurately.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments