Hi there,
With a couple of other developers we have been busy with migrating our SpriteKit games and frameworks to Swift 6.
There is one issue we are unable to resolve, and this involves the interaction between SpriteKit and GameplayKit.
There is a very small demo repo created that clearly demonstrates the issue. It can be found here:
https://github.com/AchrafKassioui/GameplayKitExplorer/blob/main/GameplayKitExplorer/Basic.swift
The relevant code also pasted here:
import SwiftUI
import SpriteKit
struct BasicView: View {
var body: some View {
SpriteView(scene: BasicScene())
.ignoresSafeArea()
}
}
#Preview {
BasicView()
}
class BasicScene: SKScene {
override func didMove(to view: SKView) {
size = view.bounds.size
anchorPoint = CGPoint(x: 0.5, y: 0.5)
backgroundColor = .gray
view.isMultipleTouchEnabled = true
let entity = BasicEntity(color: .systemYellow, size: CGSize(width: 100, height: 100))
if let renderComponent = entity.component(ofType: BasicRenderComponent.self) {
addChild(renderComponent.sprite)
}
}
}
@MainActor
class BasicEntity: GKEntity {
init(color: SKColor, size: CGSize) {
super.init()
let renderComponent = BasicRenderComponent(color: color, size: size)
addComponent(renderComponent)
let animationComponent = BasicAnimationComponent()
addComponent(animationComponent)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
@MainActor
class BasicRenderComponent: GKComponent {
let sprite: SKSpriteNode
init(color: SKColor, size: CGSize) {
self.sprite = SKSpriteNode(texture: nil, color: color, size: size)
super.init()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class BasicAnimationComponent: GKComponent {
let action1 = SKAction.scale(to: 1.3, duration: 0.07)
let action2 = SKAction.scale(to: 1, duration: 0.15)
override init() {
super.init()
}
override func didAddToEntity() {
if let renderComponent = entity?.component(ofType: BasicRenderComponent.self) {
renderComponent.sprite.run(SKAction.repeatForever(SKAction.sequence([action1, action2])))
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
As SKNode is designed to run on the MainActor
, the BasicRenderComponent
is attributed with MainActor
as well. This is needed as this GKComponent
is dedicated to encapsulate the node that is rendered to the scene.
There is also a BasicAnimationComponent
, this GKComponent
is responsible for animating the rendered node.
Obviously, this is just an example, but when using GameplayKit
in combination with SpriteKit
it is very common that a GKComponent
instance manipulates an SKNode
referenced from another GKComponent
instance, often done via open func update(deltaTime seconds: TimeInterval)
or as in this example, inside didAddToEntity
.
Now, the problem is that in the above example (but the same goes foupdate(deltaTime seconds: TimeInterval)
the methoddidAddToEntity
is not isolated to the MainActor
, as GKComponent
is not either.
This leads to the error Call to main actor-isolated instance method 'run' in a synchronous nonisolated context
, as indeed the compiler can not infer that didAddToEntity
is isolated to the MainActor
.
Marking BasicAnimationComponent
as @MainActor
does not help, as this isolation is not propogated back to the superclass inherited methods.
In fact, we tried a plethora of other options, but none resolved this issue.
How should we proceed with this? As of now, this is really holding us back migrating to Swift 6. Hope someone is able to help out here!
Hello @sanderfrenken,
Thank you for raising this issue!
Unfortunately, I don't have a good solution for you. Anything that you could do to get this integration working in Swift 6 would rely on very fragile assumptions that I won't recommend you make. It is better for you to file an enhancement request using Feedback Assistant, to request changes to SpriteKit and GameplayKit that would better facilitate their integration in a Swift 6 app.
For now, I recommend that you stick with the Swift 5 language mode while you wait for resolution on your bug report.
Best regards,
Greg