< What is actor hopping and how can it cause problems? | Important: Do not use an actor for your SwiftUI data models > |
Updated for Xcode 15
Swift provides four concrete nominal types for defining custom objects: actors, classes, structs, and enums. Each of these works a little differently from the others, but the first three might seem similar so it’s worth spending a little time clarifying what they have in common and where their differences are.
Tip: Ultimately, which you use depends on the exact context you’re working in, and you will need them all at some point.
Actors:
Actor
protocol.AnyObject
protocol, and can therefore conform to Identifiable
without adding an explicit id
property.await
.Classes:
Actor
protocol.AnyObject
protocol, and can therefore conform to Identifiable
without adding an explicit id
property.Structs:
Actor
protocol.AnyObject
protocol; if you want to add Identifiable
conformance you must add an id
property yourself.You might think the advantages of actors are such that they should be used everywhere classes are currently used, but that is a bad idea. Not only do you lose the ability for inheritance, but you’ll also cause a huge amount of pain for yourself because every single external property access needs to use await
.
However, there are certainly places where actors are a natural fit. For example, if you were previously creating serial queues to handle specific workflows, they can be replaced almost entirely with actors – while also benefiting from increased safety and performance. So, if you have some work that absolutely must work one at a time, such as accessing a database, then trying converting it into something like a database actor.
There is one area in particular where using actors rather than classes is going to cause problems, so I really can’t say this clearly enough:
Do not use actors for your SwiftUI data models. You should use a class that conforms to the ObservableObject
protocol instead. If needed, you can optionally also mark that class with @MainActor
to ensure it does any UI work safely, but keep in mind that using @StateObject
or @ObservedObject
automatically makes a view’s code run on the main actor. If you desperately need to be able to carve off some async work safely, you can create a sibling actor – a separate actor that does not use @MainActor
, but does not directly update the UI.
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.