I am using the iOS15 Swift + Core Data code for async/await, and while using private queue concurrency, I am trying to call a await perform block inside another await perform block:
await privateMainContext.perform({
var displayName: String? = nil
await self.managedObjectContext?.perform {
displayName = self.displayName
}
// ... use displayName for privateContext object
})
But I get an error with the compiler that says:
Cannot pass function of type '() async -> ()' to parameter expecting synchronous function type
If I remove the await from the inner perform call, it works OK, but doesn't that mean the self.managedObjectContext will call that in a block that might not execute before I need to use the displayName property? With old Objective-C code, I would call performBlockAndWait within another context's performBlockAndWait block and it worked fine.
The fundamental problem here is that the "perform" function is declared like this:
And the relevant part to your question is
block: @escaping () throws -> T. It takes a block that has to be synchronous code (the code in the block can't be suspended).If it could suspend it would be:
block: @escaping () async throws -> T(note the addition of
async)To solve the problem your block has to be synchronous code. That's what happens when you remove the
awaitas you've noticed. Now you're going to be calling theperformmethod of the managed context whose Swift representation is:func perform(_ block: @escaping () -> Void)which is a synchronous call, but it runs its block asyncronously using GCD and you are right to be concerned.
The only synchronous call that also runs the block synchronously is
performAndWait. So to solve your problem you can use:The only goal no served by this code is the apparent desire to convert all this code to use Swift Concurrency to the exclusion of any GCD. But there are plenty of examples where the GCD and Swift concurrency models are not equivalent. For example, it's straightforward to create a bunch of blocks and ensure they run one after the other by putting them into a serial queue. It's not easy to create plan a bunch of Tasks and coordinate them so that they run sequentially.