4
votes

I had this code with a completion handler working in Xcode 6 beta 4 that no longer works in Xcode 6 beta 5.

dropsToRemove.bridgeToObjectiveC().makeObjectsPerformSelector("removeFromSuperview")

Full method...

func animateRemovingDrops(dropsToRemove: [UIView]) {
    println(__FUNCTION__)
    UIView.animateWithDuration(1.0,
        animations: {
            for dropView in dropsToRemove {
                let x = CGFloat(UInt(arc4random_uniform(UInt32(UInt(self.gameView.bounds.size.width) * 5)))) - self.gameView.bounds.size.width * 2
                let y = self.gameView.bounds.size.height
                dropView.center = CGPointMake(x, -y)
        }}, completion: { finished in
                dropsToRemove.bridgeToObjectiveC().makeObjectsPerformSelector("removeFromSuperview")
        })
}

The error is '[UIView]' does not have a member named 'bridgeToObjectiveC'

Note that the CGFloat and Uint casting in the method is for a beta 4 workaround, I just haven't updated that part yet. That issue is covered at: ‘CGFloat’ is not convertible to ‘UInt8' and other CGFloat issues with Swift and Xcode 6 beta 4

I thought the solution for dealing with the completion handler might be to treat the array as an NSArray as detailed in: What is the swift equivalent of makeObjectsPerformSelector?

(dropsToRemove as NSArray).makeObjectsPerformSelector("removeFromSuperview")

However, assuming I got the syntax right, simply results in another error 'makeObjectsPerformSelector' is unavailable: 'performSelector' methods are unavailable

Is this a new Swift bug, or something I'm missing in the release notes?

2
It's possible they just added it to the banned list. dropsToRemove.map { $0.removeFromSuperview() } is pretty short.Brian Nickel
@BrianNickel, it is a dangerous precedence to use map to produce side effects. That is not the purpose of map.drewag
@drewag Fair enough point: for drop in dropsToRemove { drop.removeFromSuperview() } is still much shorter than the bridging code.Brian Nickel
@BrianNickel, plus that is type safe :)drewag
Thanks @BrianNickel. Should that be posted as an Answer? I'm always happy to see a better way that using bridging and selectors...kasplat

2 Answers

8
votes

The bridgeToObjectiveC and bridgeFromObjectiveC functions are not available in Xcode 6.0 beta 5. Instead, cast to/from the appropriate Foundation type when you need to use that type's API on a Swift object. For example:

var arr = ["One", "Two"]
(arr as NSArray).indexOfObject("One")

Apple has warned against (or explicitly made unavailable) using performSelector and related methods since the first Swift beta. Presumably any such API that were still available before beta 5 were unintentionally so.

As the question you cited notes, you can use map to call a function/method on every element of an array. You can also use filter, find or a for-in loop, or after casting to NSArray, one of the enumerateObjects methods. Note that many consider it bad style to use the functional-programming constructs (map, filter, reduce, find) for tasks that aren't "functional" -- that is, to run code that has side effects. So a for-in loop might be the cleanest way to do what you're after.

1
votes

Someone from Apple has stated that those bridgeToObjectiveC and bridgeFromObjectiveC functions are meant as private functions, i.e. for Apple internal use only, and that they would go away.

I will try to find the link to that statement in the developer forums, if you happen to be in the developer program.