How to use "enumerateChildNodesWithName" with Swift in SpriteKit?

rv123 picture rv123 · Jun 13, 2014 · Viewed 9.9k times · Source

I'm using Swift to make a game in SpriteKit.

In Objective-C I could use the following method:

 (void)enumerateChildNodesWithName:(NSString *)name usingBlock:(void (^)(SKNode *node, BOOL *stop))block

to perform actions on that *node, but I can't get this function working in Swift. Basically, I don't know how to reference that node in Swift.

This is the code I'm using, but I'm having trouble with the "usingBlock:" part. I've tried many things for many hours, but have not succeeded. Help please!

func spawnEnemy() -> () {
  let enemy = SKSpriteNode(imageNamed: "enemy")
  enemy.name = "enemy"
  enemy.position = CGPointMake(100, 100)
  self.addChild(enemy)
}

func checkCollisions() -> () {
  self.enumerateChildNodesWithName("enemy", usingBlock: ((SKNode!, CMutablePointer<ObjCBool>) -> Void)?)
}

Answer

rickster picture rickster · Jun 13, 2014

For now, don't trust autocomplete to insert the code you need — it drops in signatures from the "header", but a block signature is not the same as the declaration you need when inserting your own closure for a block parameter.

The formal way to write a closure would be to replicate the signature inside braces, adding local parameter names and using the in keyword to mark the start of the closure body:

self.enumerateChildNodesWithName("enemy", usingBlock: {
    (node: SKNode!, stop: UnsafeMutablePointer <ObjCBool>) -> Void in 
    // do something with node or stop
})

But Swift's type inference means you don't have to write that much. Instead, you can just name the parameters, because their type (as well as the closure's return type) is known:

self.enumerateChildNodesWithName("enemy", usingBlock: {
    node, stop in 
    // do something with node or stop
})

You can also use trailing closure syntax:

self.enumerateChildNodesWithName("enemy") {
    node, stop in 
    // do something with node or stop
}

(You can even drop the local parameter names and refer to parameters by position — e.g. $0 for node — but here isn't a great place to do that because it makes your code far less readable. It's best to reserve $0 and friends for closures where it's blindingly obvious what the parameters are, like the closures you use with map and sort.)

See Closures in The Swift Programming Language for further explanation.


Also, because stop is an UnsafeMutablePointer, the syntax for using it is a bit different than in ObjC: set stop.memory = true to break out of enumeration.