I am using Swift (XCode 7.2.1) and SpriteBuilder (1.4.9) to create a game. However I am having some problems with the creating code connections between CCB files and Swift classes. I followed the tutorials online and added a doc root var code connection name in SpriteBuilder and then set up the corresponding property in the class as a private weak var m_myVar: MyClass!
. However whenever I try and access the property I get a nil
reference. If I remove the weak
attribute from the code connection then I can access the property, but I receive deallocation issues when the scene is detroyed, e.g. navigating to a new scene using the CCDirector.shareDirector().replaceScene()
method.
In my specific case I have a MainScene
in SpriteBuilder with the following node hierarchy within it: CCNode
-> CCNodeGradient
-> Tile
. Where Tile
a subclass of CCNode
defined in a different CCB file and has the doc root var
code connection name of m_levelLabel
, which maps to the private weak var m_levelLabel: Tile!
property of the MainScene
class.
The Tile
class has 2 properties m_background
of type CCSprite9Slice
and m_label
of type CCLabelTTF
, both of which are mapped to private weak var
properties in the Tile
Swift class.
When the MainScene
loads I attempt to set some properties on the m_levelLabel
using the following code:
internal func didLoadFromCCB() {
m_levelLabel.setValue("2");
}
and Tile.setValue
is defined in the Tile
class as:
internal func setValue(value: String) {
m_label.string = value;
}
But when the setValue
method executes the m_label
property is nil
and so the fatal error: unexpectedly found nil while unwrapping an Optional value
occurs. This is odd as clearly my code connections in the MainScene
class have worked fine as I was able to call m_levelLabel.setValue
.
Interestingly if I change the properties in the Tile
class to be defined as strong references like:
private var m_label: CCLabelTTF!;
Then the code executes fine and the value of the label is updated when the MainScene
loads. However the app now crashes when I try and navigate away from the MainScene
with the error malloc: *** error for object 0x7a9f3200: pointer being freed was not allocated
which sounds like there is a problem when destroying the, now, strong properties in the Tile
class owned by the MainScene
.
What could be going on here? What is the correct way to define code connections in Swift? Note I receive no warnings in the console about missing code connection properties.
For reference here is the MainScene
and Tile
class in their entirety:
Main.swift
import Foundation
internal class MainScene: CCScene {
internal func didLoadFromCCB() {
m_levelLabel.setValue("1");
m_progressLabel.setValue("0%");
}
internal func play() {
let gameplayScene: CCScene = CCBReader.loadAsScene("Gameplay/GameplayScene");
CCDirector.sharedDirector().replaceScene(gameplayScene);
}
internal func levelSelection() {
print("Pressed level selection");
}
internal func progress() {
print("Pressed progress button");
}
private weak var m_playButton: CCButton!;
private weak var m_levelSelectionButton: CCButton!;
private weak var m_progressButton: CCButton!;
private weak var m_levelLabel: Tile!;
private weak var m_progressLabel: Tile!;
}
Tile.swift
import Foundation
internal class Tile : CCNode {
internal var value: String? {
return m_label?.string;
}
internal func setValue(value: String) {
m_label.string = value;
}
private weak var m_label: CCLabelTTF!;
private weak var m_background: CCSprite9Slice!;
}
So it turns out that for the code connections to always work they must have internal
accessibility rather than private
. E.g.
internal weak var m_label: CCLabelTTF!;
rather than
private weak var m_label: CCLabelTTF!;
I'm not sure why private
works in some instances, but not in others and it's a shame that I can't properly encapsulate this data. I guess this is either due to a bug in the SpriteBuilder library or a limitation due to the Swift language.