I'm new (like most everyone, I suppose) to Swift concurrency, and I'm running into a compiler error I don't know what to do with.
struct Thing {
var counter = 0
mutating func increment() async {
counter += 1
}
}
class Controller: UIViewController {
var thing = Thing()
func mutate() async {
await thing.increment()
print(thing.counter)
}
}
let c = Controller()
Task {
await c.mutate()
}
The first line of the mutate()
function gives me the following error.
Actor-isolated property 'thing' cannot be passed 'inout' to 'async' function call
If I just inherit from class
instead of UIViewController
things work fine, but I need the controller here, so I need to figure out how to make this work in that specific context.
I think the issue comes from Thing
being a struct
. A mutating
func on a struct will assign a new value to the thing
property on the Controller
. In order for that to work, thing
is treated as an inout
parameter in the call to thing.increment()
.
If you make thing an actor
instead of a struct
then increment()
won't need to be a mutating func
and so thing
won't be treated as an inout
parameter.
A possible workaround is to make a copy of the struct first, then call the mutating func on the copy, then store it back on the property in the controller.
func mutate() async {
var thing = self.thing
await thing.increment()
self.thing = thing
print(thing.counter)
}
The reason it's an issue is the UIViewControllers are all actors now, so the properties are considered actor isolated. There is a nonisolated
keyword but it cant be applied to stored properties so it doesn't seem to help here.
If the controller is changed to be an actor
, the error message changes a bit to say that.
error: cannot call mutating async function 'increment()' on actor-isolated property 'thing'
await thing.increment()
^