Lets assume a model which consists of a graph of mutable reference types. Such an object graph must be isolated to serialized access to avoid data races. Core Data is a good example for such a setup: the managed object context defines the serialized access using a dispatch queue (main or private queue), and all access from other queues to the managed objects of the context must go through the context’s perform methods to ensure proper isolation.
How do I create such a setup using Swift concurrency? A global actor would work, but would force all instances of the model to the same serialized context. This is not desirable: you may need one instance to run in the main actor for use by the UI and another instance to run in a background queue for e.g. an export operation.
With Swift 5.5 it seems to be possible to use an actor as a kind of serial dispatch queue: route all outside access through this actor, than dispatch back to the objects of the graph. If all objects in the graph have access to that actor (typically via a context-style concept like used in Core Data), any object can create actor-isolated tasks as needed by delegating to the actor.
This obviously works with all synchronous functions in the object graph, but it is not so clear with asynchronous functions. The recently accepted SE-0338 "Clarify the Execution of Non-Actor-Isolated Async Functions" (https://github.com/apple/swift-evolution/blob/main/proposals/0338-clarify-execution-non-actor-async.md) says that it is currently not guaranteed that a non-isolated asynchronous function called from an actor will always stay on the executor of this actor. And it suggests that in the future it will be guaranteed that such functions immediately leaves the actor’s executor. If I understand this correctly, this means that such functions become concurrent with the actor. Please correct me if I’m wrong.
Note that asynchronous execution in model graphs is often needed, e.g. for file access.
Is there any way to implement this very common pattern with Swift concurrency?