1

I am returning an SKNode from one function and I need to cast it to a custom SKNode. I get the error Cannot assign value ofSKNodeto typeGroundNode. If I force cast, it compiles, but fails at runtime. What am I missing?

// Custom node class
class GroundNode: SKNode {
        weak var entity: GKEntity!
    }

// Return node function
func returnNode() -> SKNode {
    ...
    return node
}

// Where I am getting the error
func setupNode() {
    var ground: GroundNode
    ground = returnNode() // error here.
    //// ground = returnNode() as! GroundNode fails at runtime.
}

EDIT: I am getting an SKNode from an sks file. My returnNode() just get the child with name, and returns it to my setupNode() function. I need to add the entity property, so I want to cast my returned SKNode to a GroundNode type.

I have seen this stackoverflow post.

This works with SKSpiteNode, but apparently not with SKNode, which does not make much sense to me.

If I cast my SKNode from my sks file to a GroundNode, it crashes at runtime.

Community
  • 1
  • 1
Siriss
  • 3,737
  • 4
  • 32
  • 65
  • Might I ask why you are pursuing this setup? When assigning a regular SKNode to this GroundNode, you are promising whatever logic accessing this variable that it is a GroundNode - even though it might not be since returnNode can return _any_ SKNode object. Without more context, this sounds like a bad idea to me. – CloakedEddy May 26 '16 at 13:10
  • Are you sure that in `setupNode` the call to `returnNode` actually returns a `GroundNode` instance? It doesn't seems to be... Use the debugger to observe what really happens. – Jean-Baptiste Yunès May 27 '16 at 08:18
  • I think you've forgot to set your custom class, I've explain that in my answer. – Alessandro Ornano May 27 '16 at 15:29

3 Answers3

1

Based on your code:

// Custom node class
class GroundNode: SKNode {
    weak var entity: GKEntity! = GKEntity() // or some custom initialization...
}

// Where I am getting the error
func setupNode() {
   var ground: GroundNode
   ground = returnNode() as? GroundNode
}

This happened because returnNode output is a generic SKNode and you must explicit your casting to the subclassed GroundNode.

EDIT: Ok, with your update I think I've understand your issue, you've forgot to set the custom class for your GroundNode:

enter image description here

Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
  • 1
    I need to add the Module name, but that fixed it. Crashes on other targets now because the module name does not match, but that is a separate issue. Thanks! – Siriss May 27 '16 at 17:19
1

first

returnNode() returns an instance of SKNode class. It might also return an instance of derived class (e.g. GroundNode), but call site doesn't know that from the function declaration. You are trying to assign this instance to the variable of SKNode's subclass - GroundNode which is not appropriate in OOP.

See Dynamic Binding OOP concept:

In object- oriented programming languages, a variable of a superclass type can contain a subclass instance.

(possibly, someone else could explain this in more details, but this is how things look to me)

second

Type Casting. When you suspect that some variable of a class type, might hold an instance of derived class, you could do a type casting in order to proof that.

returnNode() returns an instance of SKNode OR any derived class, but the call site wants to process only instances of derived class (GroundNode), hence one of type casting technique should be used.

import GameplayKit
import SpriteKit

// Custom node class
class GroundNode: SKNode {
    var entity: GKEntity = GKEntity()
}

// Return node function
func returnNode() -> SKNode {
    // let's assume that you are correctly getting an instance
    // of GroundNode class here, and return it from the function
    // as of SKNode type
    let returnNode: SKNode = GroundNode()
    return returnNode
}

func setupNode() {
    var groundNode: GroundNode?

    // Type Casting

    // Approach #1
    let someNode = returnNode()
    if someNode is GroundNode {
        groundNode = someNode as! GroundNode
    }

    // Approach #2
    groundNode = returnNode() as? GroundNode

    print("groundNode = \(groundNode)")
}
Yevhen Dubinin
  • 4,657
  • 3
  • 34
  • 57
  • This took care of the crashing, but would make my node empty, as I was assigning it to an empty GroundNode. – Siriss May 26 '16 at 15:21
  • Instance of what type `func returnNode()` should **actually** return at runtime (`GroundNode` or `SKNode`)? Are you trying to initialize `GroundNode` from `.sks`? Could you please shed some light on **what** you are actually trying to achieve? Thanks. – Yevhen Dubinin May 26 '16 at 15:38
  • thank you for your answers. I have updated my question. returnNode returns an SKNode. If I cast my SKNode to a GroundNode in `returnNode()` I get a crash at runtime. – Siriss May 26 '16 at 15:50
0

I think it should be

func returnNode() -> GroundNode {

Kashif
  • 4,642
  • 7
  • 44
  • 97
  • I tried, but I am getting an SKNode from an SKS file. It can't cast SKNode to GroundNode. If I force cast, it crashes at runtime, not compile time. – Siriss May 26 '16 at 14:19