Tuesday, August 26, 2025

SwiftData’s ModelActor Is Just Weird

Matt Massicotte (Mastodon):

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 the ModelContext. 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 access MainActor stuff.

Matt Massicotte:

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.

Rick van Voorden:

AFAIK the legit workaround will continue to be to ensure the ModelActor is created off main. Which leads to workarounds like what we do in ImmutableData sample products when we “box” the ModelActor with a lazy property in another actor that is created on main.

Matt Massicotte:

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:

Comments RSS · Twitter · Mastodon

Leave a Comment