Justin
Justin

Reputation: 65

Cannot invoke != with an argument list of type SKNode?

I'm following a tutorial on an intro to SpriteKit, and I'm getting this error, in the tutorial, it is just like this, but the tutorial was posted a few months back, so some of the syntax might have changed.

The error comes up in the if helloNode != nil { ... }

Any help?

Thanks!

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    var helloNode:SKNode = childNodeWithName("helloNode")!

//I get the error in the next line
    if helloNode != nil {
        helloNode.name = nil

        var moveUp:SKAction = SKAction.moveByX(0, y: 100, duration: 0.5)
        var zoom:SKAction = SKAction.scaleTo(2, duration: 0.25)
        var pause:SKAction = SKAction.waitForDuration(0.5)
        var fadeAway = SKAction.fadeOutWithDuration(0.25)
        var remove = SKAction.removeFromParent()
        var moveSequence = SKAction.sequence([moveUp, zoom, pause, fadeAway, remove])
        helloNode.runAction(moveSequence)
    }
}

Upvotes: 0

Views: 235

Answers (3)

rickster
rickster

Reputation: 126137

There are decent answers here already, but they (and the original question) involve code that isn't as concise or clear as it might be.

  1. The return type of childNodeWithName is already optional (SKNode?), so there's no need to explicitly declare it when assigning to your local helloNode variable. Likewise, type inference lets you not need to declare the types for all the SKActions you're creating.

  2. Using if let and shadowing the locally bound non-optional with the same name as the enclosing scope's optional is not the most readable of code.

  3. Besides, if you're only assigning to helloNode once and only using it after checking that assignment, you only need it bound in one scope. And speaking of only assigning to a name once, there's no need to use var for things that won't change.

  4. SKActions are reusable. If this is going to happen more than once in the course of your game, you might consider creating moveSequence once (lazily even) and stashing it in a property.

So, an improved version:

lazy var moveSequence: SKAction = {
    let moveUp = SKAction.moveByX(0, y: 100, duration: 0.5)
    let zoom = SKAction.scaleTo(2, duration: 0.25)
    let pause = SKAction.waitForDuration(0.5)
    let fadeAway = SKAction.fadeOutWithDuration(0.25)
    let remove = SKAction.removeFromParent()
    return SKAction.sequence([moveUp, zoom, pause, fadeAway, remove])
}()

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    if let helloNode == childNodeWithName("helloNode") {
        helloNode.name = nil
        helloNode.runAction(moveSequence)
    }
}

Upvotes: 0

Antonio
Antonio

Reputation: 72760

In this line:

var helloNode:SKNode = childNodeWithName("helloNode")!

the return value of childNodeWithName, which is an optional, is applied the forced unwrapping operator, which:

  • throws a runtime exception if the return value is nil
  • evaluates to a non optional SKNode if the return value is not nil

Besides that, helloNode is declared as a non optional SKNode, and a non optional can never be nil.

To fix the compilation error, do the following changes:

var helloNode: SKNode? = childNodeWithName("helloNode")

if let helloNode = helloNode {
    ...
}

With these changes, helloNode is an optional SKNode, which can contain either nil or an instance of SKNode. In the next like optional binding is used: if helloNode is nil, the let helloNode = helloNode evaluates to false, and the code in the if statement is skipped. On the other hand, if helloNode is not nil, the value it contains is unwrapped and assigned to a non optional variable (having the same name, but you can choose any name you want) whose scope is limited to the code block of the if statement.

Upvotes: 2

mdnghtblue
mdnghtblue

Reputation: 1117

The ! at the end of childNodeWithName("helloNode")! means that the value cannot be nil, so the nil check is invalid. Try this:

 if let temp = helloNode {
    // use temp instead of helloNode

    ...
}

Or change your variable declaration line to this:

var helloNode:SKNode? = childNodeWithName("helloNode")

You can read more about how to work with Optionals here: Optionals in Swift

Upvotes: 1

Related Questions