Revealed on: August 15, 2024
When you begin migrating to the Swift 6 language mode, you may most certainly activate strict concurrency first. As soon as you’ve got completed this there will likely be a number of warings and errors that you’re going to encounter and these errors may be complicated at instances.
I will begin by saying that having a strong understanding of actors, sendable, and information races is a big benefit whenever you wish to undertake the Swift 6 language mode. Just about all the warnings you may get in strict concurrency mode will let you know about potential points associated to working code concurrently. For an in-depth understanding of actors, sendability and information races I extremely advocate that you simply check out my Swift Concurrency course which is able to get you entry to a collection of movies, workout routines, and my Sensible Swift Concurrency e book with a single buy.
WIth that out of the best way, let’s check out the next warning that you simply may encounter in your venture:
reference to var myVariable shouldn’t be concurrency-safe as a result of it includes shared mutable state
There are a number of causes for this warning to pop up in Xcode. For instance, the code under would trigger Xcode to warn us:
// Var 'myVariable' shouldn't be concurrency-safe as a result of it's nonisolated international shared mutable state; that is an error within the Swift 6 language mode
var myVariable = UUID()
func randomCharacter() async -> Character {
myVariable = UUID()
return myVariable.uuidString.randomElement() ?? "1"
}
The next code makes myVariable
a static var
which leads to the identical warning being proven:
struct CharacterMaker {
// Var 'myVariable' shouldn't be concurrency-safe as a result of it's nonisolated international shared mutable state; that is an error within the Swift 6 language mode
static var myVariable = UUID()
static func randomCharacter() async -> Character {
myVariable = UUID()
return myVariable.uuidString.randomElement() ?? "1"
}
}
The Swift compiler considers any globally accessible var
to be unsafe from a concurrency perspective. The rationale for that’s that nothing is stopping us from making a number of calls to randomCharacter
concurrently which might lead to a knowledge race on myVariable
. We might find yourself with a number of learn and write operations on the identical time.
To repair this, myVariable
ought to both be moved into an actor or be remoted to a world actor.
For instance, you possibly can isolate myVariable
to @MainActor
like this:
// with a world variable
@MainActor
var myVariable = UUID()
// or as a static property
struct CharacterMaker {
@MainActor
static var myVariable = UUID()
// ...
}
The draw back of that is, in fact, that we must be on the primary actor to work together with the variable. You possibly can work round this by defining your personal (empty) international actor which is able to be certain that our accesses are on the worldwide executor as an alternative of the primary actor:
@globalActor
actor GlobalIsolator {
static let shared = GlobalIsolator()
}
@GlobalIsolator
var myVariable = UUID()
// or as a static property
struct CharacterMaker {
@GlobalIsolator
static var myVariable = UUID()
// ...
}
This makes accessing myVariable
a bit much less handy since you’ll want to put your self on the GlobalIsolator
actor when interacting with myVariable
:
@GlobalIsolator
static func randomCharacter() async -> Character {
myVariable = UUID()
return myVariable.uuidString.randomElement() ?? "1"
}
In some instances you may know that regardless that the compiler does not like your shared mutable state, you know that it is effective as a result of manner your code is structured.
If that is the case, and also you’re completely 100% certain that you simply will not have any points associated to your shared mutable state, you should utilize nonisolated(unsafe)
in your variable to inform the compiler that the dearth of isolation is intentional and that you simply’re conscious of its information issues of safety:
// with a world variable
nonisolated(unsafe) var myVariable = UUID()
// or as a static property
struct CharacterMaker {
nonisolated(unsafe) static var myVariable = UUID()
// ...
}
It is best to solely use nonisolated(unsafe)
as a last-resort resolution as a result of the compiler will now not have the ability that can assist you detect potential information races round myVariable
.