class Something {
var things: [String] = []
func doit() {
let newMessages: [String] = []
Task { @MainActor in
self.things = newMessages
}
}
}
This code fails to compile with swift 6 saying Sending 'newMessages' risks causing data races
-- how can I move an array to the MainActor in a safe way?
UPDATE: it seems the capture is a red herring and actually I can't use self
from MainActor at all?
class Something {
func doit() {
Task { @MainActor in
self.doit()
}
}
}
give me error Task or actor isolated value cannot be sent
The issue is that Something
/self
isn't "safe". It could be attached to any actor
or nonisolated
. There are many ways to make it safe.
The 2 main ways to make something safe is with Sendable
or globalActor
.
The "easy" solution is to put Something
on the MainActor
so MainActor
handles safety, this isn't ideal because we don't want to clutter the main.
@MainActor
class Something {
var things: [String] = []
func doit() async {
let newMessages: [String] = await [] //await is to highlight that the work should be done async.
self.things = newMessages
}
}
Another option is to use actor
which is usually appropriate for services.
actor Something {
var things: [String] = []
func doit() async {
let newMessages: [String] = []
self.things = newMessages
}
}
The 3rd option is to use a custom globalActor
which is very convenient for things that use delegates or other non isolated methods
@LocationActor
class Something {
var things: [String] = []
func doit() async {
let newMessages: [String] = []
self.things = newMessages
}
nonisolated func doSomethingElse() {//This function is not isolated
let newMessages: [String] = []
Task { @LocationActor in // Safely modify the property
self.things = newMessages
}
}
}
@globalActor
struct LocationActor {
static let shared: LocationActor = .init()
typealias ActorType = LocationActor
actor LocationActor {
}
}
The 4th option is to use a struct that can be marked Sendable
.
struct Something: Sendable {
var things: [String] = []
mutating func doit() async {
let newMessages: [String] = []
self.things = newMessages
}
}
Which is "right" depends on your use case.