swiftconcurrencysprite-kitscenekit

Why is SKNode bound to MainActor?


In the following code:

public class  Foo {
    
    public func createNode() -> SCNNode {
        return SCNNode()
    }

    public func createNode() -> SKNode {
        return SKNode()
    }
}

Compiling with Swift 6 strict concurrency checking, the line return SKNode() doesn't compile with the error Call to main actor-isolated initializer 'init()' in a synchronous nonisolated context and the proposed fix Add '@MainActor' to make instance method 'createNode()' part of global actor 'MainActor'.

Any call to a SKNode method or property does the same.

This doesn't happen with the return SCNNode().

In my app, these methods should be called inside my SCNSceneRendererDelegate methods, such as renderer(_:updateAtTime:), so I can't take the penalty of going back to the MainActor.

Why is SKNode bound to MainActor (when SCNNode is not), and how to walk this around ?


Solution

  • SKNode is a subclass of UIResponder, and UIResponder is isolated to the main actor, so SKNode is, too.

    Also see Getting Started with Nodes: Ensure Node Access on the Main Thread. They are explicit that you must do this on the main thread (justifying the isolation to the main actor).

    To resolve this, you can probably solve this by isolating createNode to the main actor, e.g.,

    @MainActor
    public func createNode() -> SKNode {…}
    

    And, because SKNode is not Sendable, this createNode must be called from a context that is also isolated to the main actor, too. Or, if Foo warrants it, perhaps isolate the whole type to the main actor, too. It’s hard to be specific without seeing more of the Foo implementation, how you’re ensuring the thread-safety of that, etc. But hopefully this is enough to get you going.