I have a Settings class that conform to the TestProtocol. From the function of the protocol I need to call the setString function and this function needs to be on the MainActor. Is there a way of make this work in Swift6, without making the protocol functions running on @MainActor
The calls are as follows:
class Settings: TestProtocol{
var value:String = ""
@MainActor func setString( _ string:String ){
value = string
}
func passString(string: String) {
Task{
await setString(string)
}
}
}
protocol TestProtocol{
func passString( string:String )
}
For those reading along at home, the code that reza___f posted fails to compile with an isolation error. Here’s a slightly edited version that makes the reason for that clearer:
class Settings: TestProtocol {
…
func passString(string: String) {
let s = self
Task {
// ^ Passing closure as a 'sending' parameter risks causing data races
// between code in the current task and concurrent execution of the
// closure
await s.setString(string)
}
}
}
This fails because:
-
The task initialiser expects a sendable closure.
-
But the task captures
s
. -
s
is not sendable.
There are various ways around this but probably the easiest is to declare the class as @MainActor
:
@MainActor
class Settings: TestProtocol {
…
nonisolated func passString(string: String) {
let s = self
Task {
await s.setString(string)
}
}
}
Or to return to your simpler form:
@MainActor
class Settings: TestProtocol {
…
nonisolated func passString(string: String) {
Task {
await setString(string)
}
}
}
This works because main-actor objects are a bit like actual actors, that is, they are sendable. That’s because the compiler knows that everything in the object is either only accessible on the main actor or non-isolated, and hence safe to call from other contexts.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"