Home > OS >  How can I indicate I want the synchronous version of a function when in an async context in Swift?
How can I indicate I want the synchronous version of a function when in an async context in Swift?

Time:01-01

If you are using structured concurrency in Swift, and are in an async context, the compiler will assume that if an async version of a function exists, that is the one you want to use. Is there a way to tell it you want the synchronous version?

Here is an example (and to show that it is at least possible in theory).

In vanilla SpriteKit, if you have an SKNode and these lines:

node.run(SKAction.rotate(byAngle: -1.5*CGFloat.pi, duration: 6))
node.run(SKAction.scale(by: 25, duration: 6))

The node will rotate AND scale over the course of 6 seconds at the same time.

...if you are using structured concurrency run will use the async version of this function and the required code:

await node.run(SKAction.rotate(byAngle: -1.5*CGFloat.pi, duration: 6))
await node.run(SKAction.scale(by: 25, duration: 6))

Will cause the node to rotate over 6 seconds and THEN scale for 6 seconds. The completion callback is replaced with an async style flow control.

The problem is I don't want it to wait and return, but I don't see any way to say "Hey - just use the sync version of this function". If I just omit await it is a compiler error.

To show it is possible, I can force the use of a sync version of the function like this:

node.run(SKAction.rotate(byAngle: -1.5*CGFloat.pi, duration: 6)){}
node.run(SKAction.scale(by: 25, duration: 6)){}

...providing the completion trailing closure. So there isn't anything 'special' about the sync version of run. This works, but is there no other way to say "just use the sync function"?

I can also do this:

Task{ await node.run(SKAction.rotate(byAngle: -1.5*CGFloat.pi, duration: 6))}
Task{ await node.run(SKAction.scale(by: 25, duration: 6)) }

...but that seems even more cumbersome.

So - the bottom line - how do you indicate in Swift, when using structured concurrency and there is both a sync and async version of a function that you want to use the sync version?

CodePudding user response:

This works, but is there no other way to say "just use the sync function"?

Not really, and there really couldn't be. You are the unfortunate victim of the fact that there are two SKNode run methods that are indistinguishable except for the presence or absence of the completion handler.

open func run(_ action: SKAction)

open func run(_ action: SKAction, completion block: @escaping () -> Void)

open func run(_ action: SKAction) async

The completion handler (in the second signature) is optional, so if it isn't there, the compiler has to make a decision (between the first signature and the third signature). Thus, the way you are disambiguating is a very good way. You are clarifying which you want to call by correctly using the full signature of the one you want to call.

I don't see why you don't like that, but if you don't, an alternative would be to write an extension of SKNode where you call each run method (unambiguously) using your own invented method names that are not ambiguous. You could call them runSync and runAsync, for example:

extension SKNode {
    func runSync(_ action: SKAction) {
        run(action) {}
    }
    func runAsync(_ action: SKAction) async {
        await run(action)
    }
}
  • Related