SwiftData’s ModelActor Is Just Weird
So, no doubt there’s lots of historical stuff going on here.
But, that still doesn’t explain how much trouble people have with ModelActor. I’m not sure anyone has ever used
ModelActor
without at least some surprises.[…]
Actors exist to protect mutable state. The purpose of a
ModelActor
is to own and isolate theModelContext
. It does that! But if we start to dig into how exactly it does it, we will discover something very bizarre.[…]
Somehow, we are on our custom, minimal, SwiftData-defined actor and also the
MainActor
at the same time.[…]
It is bad because consumers of this API have a very reasonable expectation that this will execute off the main thread. This type doesn’t do that. But worse, its relationship with the main thread isn’t visible in the type system. These things are not marked
MainActor
, so the compiler doesn’t know what’s going on. This means even though you are on the main thread here, you cannot accessMainActor
stuff.
Anyone know if SwiftData’s
ModelActor
still has weird concurrency behavior in OS 26?[…]
Based on some limited testing, no, not fixed.
ModelActor
types can still ultimately execute on the main thread, depending on calling context.
AFAIK the legit workaround will continue to be to ensure the
ModelActor
is created offmain
. Which leads to workarounds like what we do inImmutableData
sample products when we “box” theModelActor
with alazy
property in another actor that is created onmain
.
Someone proved that init off main is insufficient. I have a theory on what’s happening, and I think this workaround you suggest will always work. But yeah I’m hoping this all just goes away.
Previously:
- Sendable, @unchecked Sendable, @Sendable, sending, and nonsending
- Ways SwiftData’s ModelContainer Can Error on Creation
- SwiftData and Core Data at WWDC25
- @MainActor Not Guaranteed