7

I'm trying to build an Objective-C block in Swift 2 in order to add it to an NSArray like so :

typealias CompletionBlock = () -> Void
let aBlock:CompletionBlock =  {
    print("Hello world!")
}
let nsArray = NSMutableArray()
nsArray.addObject(aBlock) // Error

I know it will work just fine with a Swift array, but I need an NSArray here for compatibility with existing Objective-C code. And if I use a swift array the compiler will refuse to cast it to an NSArray because it won't be a [AnyObject] (it will be a [Any]).

The problem here is that a swift closure is not an object contrary to Objective-C blocks which are objects behind the scene (they are instances of NSBlock which is a subclass of NSObject)

So my question is : How do a create an Objective-C block in swift ? I've tried using @convention (block) in the typealias but it doesn't work.

deadbeef
  • 5,409
  • 2
  • 17
  • 47
  • 1
    Duplicate of http://stackoverflow.com/questions/28211973/swift-closure-as-anyobject or http://stackoverflow.com/questions/24586293/cast-closures-blocks ? – Martin R Feb 12 '16 at 12:20
  • Thanks ! I missed that second answer with the proper use of `unsafeBitCast`. Sorry for the duplicate... – deadbeef Feb 12 '16 at 12:39

1 Answers1

7

EDIT : As of Swift 3, this is completely unnecessary (and doesn't even work). Adding closures to Objective-C arrays works out of the box in Swift 3. The answer below is valid for Swift 2 only.

I know this is a duplicate but I will still post a refactored answer from swift-closure-as-anyobject and cast-closures-blocks in case anyone lands on this one first.

The solution is to use the unsafeBitCast function to convert the Swift closure to an Objective-C compatible object before adding it to an NSArray and back before using it in Swift.

// The `@convention(block)` is important here in order to get
// Objective-C like memory management
typealias CompletionBlock = @convention(block) () -> Void

let aBlock:CompletionBlock = {
    print("Hello world!")
}
let nsArray = NSMutableArray()
let blockObject = unsafeBitCast(aBlock, AnyObject.self)
nsArray.addObject(blockObject)

let closureObject = nsArray[0]
let closure = unsafeBitCast(closureObject, CompletionBlock.self)
closure()
Community
  • 1
  • 1
deadbeef
  • 5,409
  • 2
  • 17
  • 47