Cast closures/blocks
block, closures, objective-c, swift
Solution
How about a generic `Block` parameterized with the function type?
class Block<T> {
let f : T
init (_ f: T) { self.f = f }
}
Allocate one of these; it will be a subtype of `AnyObject` and thus be assignable into dictionaries and arrays. This doesn't seem too onerous especially with the trailing closure syntax. In use:
5> var b1 = Block<() -> ()> { print ("Blocked b1") }
b1: Block<() -> ()> = {
f = ...
}
6> b1.f()
Blocked b1
and another example where the `Block` type is inferred:
11> var ar = [Block { (x:Int) in print ("Block: \(x)") }]
ar: [Block<(Int) -> ()>] = 1 value {
[0] = {
f = ...
}
}
12> ar[0].f(111)
Block: 111
Problem
In Objective-C, I often pass around blocks. I use them very often to implement patterns that help avoid storing stuff into instance variables, thus avoiding threading/timing issues. For example, I assign them to a `CAAnimation` via `-[CAAnimation setValue:forKey:]` so I can execute the block when the animation is finished. (Objective-C can treat blocks as objects; you also can do `[someBlock copy]` and `[someBlock release]`.) However, trying to use these patterns in Swift together with Objective-C seems to be very difficult. (Edit: and we can see that the language is still in flux: have adapted the code so it works on Xcode6-beta2, previous version worked on Xcode6-beta1.) For example, I can't convert `AnyObject` back to a block/closure. The following yields an error from the compiler: ``` override func animationDidStop(anim: CAAnimation!, finished flag: Bool) { let completion : AnyObject! = anim.valueForKey("completionClosure") (completion as (@objc_block ()->Void))() // Cannot convert the expression's type 'Void' to type '@objc_block () -> Void' } ``` I have found a workaround, but it's pretty ugly, IMHO: in my bridging header, I have: ``` static inline id blockToObject(void(^block)()) { return block; } static inline void callBlockAsObject(id block) { ((void(^)())block)(); } ``` And now I can do this in Swift: ``` func someFunc(completion: (@objc_block ()->Void)) { let animation = CAKeyframeAnimation(keyPath: "position") animation.delegate = self animation.setValue(blockToObject(completion), forKey: "completionClosure") … } override func animationDidStop(anim: CAAnimation!, finished flag: Bool) { let completion : AnyObject! = anim.valueForKey("completionClosure") callBlockAsObject(completion) } ``` It works, but I'd need a new function for every block type that I'd like to use and I'm hacking around the compiler which can't be good either. So is there a way to solve this in a pure Swift way?