Fixing “Reference to captured var in concurrently-executing code” in Swift

Fixing “Reference to captured var in concurrently-executing code” in Swift


Revealed on: July 31, 2024

When you begin migrating to the Swift 6 language mode, you will more than likely activate strict concurrency first. As soon as you’ve got accomplished this there will probably be a number of warings and errors that you will encounter and these errors could be complicated at instances.

I will begin by saying that having a stable understanding of actors, sendable, and knowledge races is a large benefit once you wish to undertake the Swift 6 language mode. Just about the entire warnings you will get in strict concurrency mode will inform you about potential points associated to working code concurrently. For an in-depth understanding of actors, sendability and knowledge races I extremely suggest that you simply check out my Swift Concurrency course which can get you entry to a sequence of movies, workouts, and my Sensible Swift Concurrency ebook with a single buy.

WIth that out of the way in which, let’s check out the next warning that you simply may encounter in your challenge:

Reference to captured var in concurrently-executing code

This warning tells you that you simply’re capturing a variable within a physique of code that can run asynchornously. For instance, the next code will end result on this warning:

var activity = NetworkTask(
    urlsessionTask: urlSessionTask
)

add(fromTask: urlSessionTask, metaData: metaData, completion: { end in
    Job {
        await activity.sendResult(end result) // Reference to captured var 'activity' in concurrently-executing code; that is an error within the Swift 6 language mode
    }
})

The activity variable that we create a few traces earlier is mutable. Because of this we are able to assign a special worth to that activity at any time and that would end in inconsistencies in our knowledge. For instance, if we assign a brand new worth to the activity earlier than the closure begins working, we’d have captured the previous activity which might be surprising.

Since strict concurrency is supposed to assist us ensure that our code runs as freed from surprises as doable, Swift desires us to ensure that we seize a continuing worth as an alternative. On this case, I am not mutating activity anyway so it is secure to make it a let:

let activity = NetworkTask(
    urlsessionTask: urlSessionTask
)

add(fromTask: urlSessionTask, metaData: metaData, completion: { end in
    Job {
        await activity.sendResult(end result)
    }
})

This alteration removes the warning as a result of the compiler now is aware of for certain that activity will not be given a brand new worth at some surprising time.

One other approach to repair this error could be to make in specific seize within the completion closure that I am passing. This seize will occur instantly as a let so Swift will know that the captured worth is not going to change unexpectedly.

var activity = NetworkTask(
    urlsessionTask: urlSessionTask
)

add(fromTask: urlSessionTask, metaData: metaData, completion: { [task] end in
    Job {
        await activity.sendResult(end result.mapError({ $0 as any Error }))
    }
})

Altenatively, you can make an specific fixed seize earlier than your Job runs:

var activity = NetworkTask(
    urlsessionTask: urlSessionTask
)

let theTask = activity
add(fromTask: urlSessionTask, metaData: metaData, completion: { end in
    Job {
        await theTask.sendResult(end result)
    }
})

This isn’t as elegant however could be wanted in circumstances the place you do wish to move your variable to a chunk of concurrently executing code however you additionally need it to be a mutable property for different objects. It is basically the very same factor as making a seize in your completion closure (or straight within the activity if there is not any further wrapping closures concerned).

Once you first encounter this warning it could be instantly apparent why you are seeing this error and the way you need to repair it. In digital all circumstances it implies that it is advisable both change your var to a let or that it is advisable carry out an specific seize of your variable both by making a shadowing let or by means of a seize listing on the primary concurrent little bit of code that accesses your variable. Within the case of the instance on this put up that is the completion closure however for you it could be straight on the Job.

Leave a Reply

Your email address will not be published. Required fields are marked *