Search code examples
iosswiftsprite-kitcrashskphysicscontact

extension for SKPhysicsContact crashing


I'm creating a game with SpriteKit, that has collision between 2 bodies. After setting up the bodies, I've implemented the didBegin(_contact:) moethod as shown below:

func didBegin(_ contact: SKPhysicsContact) {
    if contact.bodyA.categoryBitMask == 0 && contact.bodyB.categoryBitMask == 1 {
        gameOver()
    }
}

and it worked perfectly.

Later, while inspecrting the documentation for this method, I found the following:

The two physics bodies described in the contact parameter are not passed in a guaranteed order.

So to be on the safe side, I've extended the SKPhysicsContact class with a function the swaps the categoryBitMask between both bodies, as following:

extension SKPhysicsContact {

    func bodiesAreFromCategories(_ a: UInt32, and b: UInt32) -> Bool {
        if self.bodyA.categoryBitMask == a && self.bodyB.categoryBitMask == b { return true }
        if self.bodyA.categoryBitMask == b && self.bodyB.categoryBitMask == a { return true }
        return false
    }
}

The problem is that when the function gets called, the app crashes, and I get the following error:

2017-07-18 13:44:18.548 iSnake Retro[17606:735367] -[PKPhysicsContact bodiesAreFromCategories:and:]: unrecognized selector sent to instance 0x60000028b950

2017-07-18 13:44:18.563 iSnake Retro[17606:735367] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[PKPhysicsContact bodiesAreFromCategories:and:]: unrecognized selector sent to instance 0x60000028b950'


Solution

  • This apparently is a bug, as answered here: https://stackoverflow.com/a/33423409/6593818

    The problem is, the type of contact is PKPhysicsContact (as you've noticed), even when you explicitly tell it to be an SKPhysicsContact, and the extension is on SKPhysicsContact. You'd have to be able to make an extension to PKPhysicsContact for this to work. From this logic, we can say that no instance methods will work in SKPhysicsContact extensions at the moment. I'd say it's a bug with SpriteKit, and you should file a radar. Class methods still work since you call them on the class itself.

    In the meantime, you should be able to move that method into your scene or another object and call it there successfully.

    For the record, this is not a Swift-specific problem. If you make the same method in an Objective-C category on SKPhysicsContact you'll get the same crash.

    You can submit a bug report to apple:

    https://developer.apple.com/bug-reporting/

    And report it to the community:

    https://openradar.appspot.com/search?query=spritekit

    However, what you really want to do with your code is to add the category masks together. And then check for the sum (2 + 4 and 4 + 2 always equals 6, regardless of bodyA and bodyB order).

    This is how you get unique contacts, if you set up your masks correctly in powers of two (2, 4, 8, 16, etc)