I've encountered a concurrency safety issue in Swift 5.10 StrictConcurrency mode that I'm struggling to resolve. I'm working with an EnvironmentKey
structure that includes a static property defined as an asynchronous closure returning an optional custom actor. Here is the simplified code snippet:
struct DataHandlerKey: EnvironmentKey {
static var defaultValue: @Sendable () async -> Hello? = { nil }
}
actor Hello {}
The defaultValue
is a closure marked with @Sendable
that asynchronously returns an optional Hello
actor instance. However, Swift 5.10 with StrictConcurrency compiler raises a concurrency safety error, stating:
Static property 'defaultValue' is not concurrency-safe because it is non-isolated global shared mutable state; this is an error in Swift 6.
I understand that the issue is related to the static property potentially introducing non-isolated global shared mutable state, but I'm unsure how to adjust my code to adhere to Swift 6's enhanced concurrency safety requirements. The Hello
actor is designed to be concurrency-safe, yet I'm unable to use it as intended in this context.
Any insights, suggestions, or references to relevant documentation would be greatly appreciated. Thank you in advance for your help!
defaultValue
should be a let
(or a computed property). It doesn't need to be a var
:
static let defaultValue: @Sendable () async -> Hello? = { nil }
If it is a var
, any code from any thread can assign to it and change its value, and since it is not isolated to an actor, this can cause data races.
What allows you to change environment values is not that defaultValue
is a var
, but that the EnvironmentValues
property has a setter.
extension EnvironmentValues {
var dataHandler: @Sendable () async -> Hello? {
get { self[DataHandlerKey.self] }
// this setter is what allows you to chang the environment
set { self[DataHandlerKey.self] = newValue }
}
}