To elaborate on that, one of the best mental model shifts you can do when working with actors is to stop designing "cute" and "clean" interface like
actor ImageLoader {
func startLoadingImage(url: URL) async throws
func addTagToImage(_ tag: String)
func saveImageToDisk() throws
}
with the intent to "compose" these calls from the outside:
try await myActor.startLoadingImage(url: url)
await myActor.addTagToImage("cute")
await myActor.addTagToImage("kitty")
await myActor.saveImageToDisk()
— these four calls can be interleaved in many possible combinations; you're more likely to corrupt your data this way than not.
It really helps instead to turn a complete 180 when it comes to API aesthetics: the "unwieldier" a signature looks, the more likely it is that the function exhibits correct transactionality:
func loadImage(at: URL, addingTags: [String], saveToDisk: Bool) async throws
This is a very artificial example, but it aims to show that the interface of an actor should only offer indivisible, complete entry points to the whole "batch" of operations that needs to be performed. If you need variations, you should either parametrize the function or offer a separate function, so that your callers never have to "compose" logic out of smaller steps.