GKScene unable to load custom class from module ‘(null)’

I have a game project with two targets, one for macOS and one for iOS. I want to use the same SKScene file for both. I add the SKS file and set the targets as follows:

Screenshot showing that the SKS file is targeting both the macOS and iOS platforms.

Usually, to load an SKS file, I would use the following in the view controller:

override func viewDidLoad() {
	super.viewDidLoad()
		if let view = self.view as? SKView {
			if let scene = GameScene(fileNamed: "GameScene" ) {
				scene.scaleMode = .aspectFill
				view.showsFPS = true
				view.showsNodeCount = true
				view.showsPhysics = true
				view.presentScene(scene)
			}
		}
	}
}

I call my SKScene subclass with the init(fileNamed:) method and it would load the SKS file with my subclass as the root node.

However, I want to use the GameplayKit features that are now supported in SKS files. To do this, I have to do it a little bit differently.

From Apple’s Game template for iOS:

override func viewDidLoad() {
	super.viewDidLoad()
	// Load 'SceneSelection.sks' as a GKScene. This provides gameplay related content
	// including entities and graphs.
	if let scene = GKScene(fileNamed: "SceneSelection") {
		// Get the SKScene from the loaded SceneSelection
		if let sceneNode = scene.rootNode as! SceneSelection? {
			// Copy gameplay related content over to the scene
			sceneNode.entities = scene.entities
			sceneNode.graphs = scene.graphs
			// Set the scale mode to scale to fit the window
			sceneNode.scaleMode = .aspectFill
			// Present the scene
			if let view = self.view as! SKView? {
				view.presentScene(sceneNode)
				view.ignoresSiblingOrder = true
				view.showsFPS = true
				view.showsNodeCount = true
			}
		}
	}
}

I use the init(fileNamed:) initialiser of **GKScene** to load the gameplay related content, then cast the rootNode of that to my custom class and present that.

This means that I have to make sure that the root node is set to my custom class within the SKS file:

Screenshot of the SpriteKit Scene Editor's Custom Class panel. The Custom Class is set to SceneSelection and the module is blank.

However, when I added a new target (for macOS) and went to use the same SKS file for both, it would fail to load on macOS with the following error:

GKScene unable to load custom class from module ‘(null)’

My understanding is that if you leave a module name blank, it’s supposed to just look in the main bundle for the correct class. The class was included in both targets, so it should have found it.

After a little bit of digging, I found a workaround. If I made the target -> Build Settings -> Product Module Name identical for both targets:

Screenshot of the Build Settings for both the macOS and iOS targets, with the Product Module Name set to BasicComponents_Example

Then set the module name in the SKS file to that same module name:

Screenshot of the SpriteKit Scene Editor's Custom Class panel. The Custom Class is set to SceneSelection and the module is now set to BasicComponents_Example

Then everything would work as expected—it would load correctly on both iOS and macOS:

Screenshot showing the macOS app and the iOS simulator both loading the same scene correctly.

I don’t love having the same Product Module Name for both targets—I’m not sure if that’s going to cause any other issues down the line—but it does work.

For some reason, this only happened when I added a brand new SKS file to an existing project and then tried targeting both macOS and iOS.

When I tried to recreate this issue using the default iOS game with Apple’s Game template, then added a new macOS target with the same template and had them both using the same GameScene.sks and GameScene.swift files, this issue didn’t reappear.

I don’t know if it’s because I was using Cocoapods in the original project or if its just some bad Xcode weather, but this is what got it to work.

If you know why this got it to work, I would love to know!