External Libraries with the SpriteKit Visual Editor

I’m in the process of developing a library of useful, reusable components that could be dropped as an external library into a SpriteKit project.

They include things like my NodeComponent and an abstracted way of managing three different kinds of input from either macOS or iOS (single tap/left click, double tap/right click, and pan/mouse drag). It also has a physics component and a render component—things that come up in games of all different types.

The components often have a lot of editable properties that affect how entities behave in the game. Tagging these properties with the @GKInspectable tag allows you to use these components within the SpriteKit visual editor.

You can customise properties with a range of types from the components panel.

A screenshot of the SpriteKit visual editor in Xcode. Highlighted in the top right are the components with editable GKInspectable properties that have been applied to a node within the visual editor.

There are advantages to this:

  • Developing components with the editor in mind keeps that component focused on what it should be—a big bag of editable state—and stops me adding methods and functionality that would be better suited to a system or the game scene and its delegates.
  • It’s fewer lines of code that I have to write—SpriteKit will automatically create the entities and then apply the components to them at runtime.
  • It makes the editing environment closer to that of Unity, which should make moving that much easier.

Sadly, this support does not extend to libraries.

Reduced Test Case

I created a new Cocoapods library with an example project. The example project is set up exactly like the Game template that ships with Xcode.

This library had a single file called ExternalComponent.swift that looks like this:

import GameplayKit
import SpriteKit
class ExternalComponent : GKComponent {
	@GKInspectable var externalValue : Double = 0
}

I then added another component directly to the project that looks like this:

import GameplayKit
import SpriteKit
class InternalComponent : GKComponent {
	@GKInspectable var internalValue : Double = 0
}
Screenshot of the SpriteKit Visual Editor in Xcode. There are two large arrows pointing to the components panel that show that both the internal and external components were available in the editor.

Both components showed up in the editor and both the properties were editable.

To convert the nodes with components within a visual scene into entities, there’s a special abstract class to load the scene file—GKScene—which finds all the nodes with components and automatically creates the entities for them.

If you use the Xcode Game template with Use GameplayKit checked when setting up the project, Xcode automatically uses the GKScene class to create the entities in the GameViewController:

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
			}
		} else {
			print("Error with finding root node")
	} else {
		print("Error loading the SpriteKit scene")
	}
}

This was where things started failing. It seems that the GKScene(fileNamed:) method works correctly and returns the scene. However, it would fail when trying to cast the rootNode property to my custom SKScene subclass.

Screenshot showing the iPhone Simulator with a gray screen over the GameViewController code. The console log reads 'Error with finding root node'

If I removed the component that belonged to the Cocoapods library, everything worked as expected.

Screenshot showing the iPhone Simulator with the game running over the SpriteKit visual editor. The external component has been removed from the components panel.

I don’t see any good reason for why it should be like this, especially as Storyboards allow you to use UIKit @IBInspectable properties in external libraries and frameworks without issue.

Using the SpriteKit visual editor would be an asset when designing adventure games—easily moving around the starting position of NPCs and objects would make iteration that much quicker—but it’s a nice-to-have at the moment.

The advantages of not having to rewrite a lot of standard game code far outweighs the benefits of being able to adjust properties in the components panel of the visual editor.

I do think this is a bug and I have filed a radar. I imagine it’s about a low a priority as something can get for the SpriteKit team, but I still hope Apple gets this fixed.