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?
See Question&Answers more detail:os