Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store this Objective-C block inside a Swift variable?

Here is the Objective-C block:

@property (copy) void (^anObjcBlock)();

anObjcBlock = ^{
    NSLog(@"Yea man this thing works!!");
};
NSMutableArray *theArrayThatHasTheBlockInItAtIndexZero = [NSMutableArray array];
[theArrayThatHasTheBlockInItAtIndexZero addObject:anObjBlock];

Here's what I did in Swift:

var theBlock: (()->Void)?

theBlock = theArrayThatHasTheBlockInItAtIndexZero[0] as? ()->Void
// Now call the block
theBlock!()

But with this I get runtime error.
Basically, the theBlock = theArrayThatHasTheBlockInItAtIndexZero[0] as? ()->Void statement would make theBlock nil because the as? failed. And when I changed the statement to theBlock = theArrayThatHasTheBlockInItAtIndexZero[0] as! ()->Void, I get a runtime error:

enter image description here

I'm not sure what else to do. This is an empty project, there really is no code in it.

like image 568
Just a coder Avatar asked Jan 08 '23 12:01

Just a coder


1 Answers

It looks like the issue, in this case, comes from the NSMutableArray.

[NSMutableArray objectAtIndex:] returns id in Objective-C, which gets translated to AnyObject by Swift.

You will get an error if you attempt to cast AnyObject to () ->Void.

A workaround is the following:

// Create your own typealias (we need this for unsafeBitcast)
typealias MyType = @convention(block) () -> Void

// Get the Obj-C block as AnyObject
let objcBlock : AnyObject = array.firstObject! // or [0]

// Bitcast the AnyObject Objective-C block to a "Swifty" Objective-C block (@convention(block)) 
// and then assign the result to a variable of () -> Void type

let block : () -> Void = unsafeBitCast(objcBlock, MyType.self)

// Call the block
 
block()

This code works for me.


FUNNY FACT

If you edit your Objective-C code to look like this...

// Typedef your block type
typedef void (^MyType)();

// Declare your property as NSArray of type MyType
@property (strong) NSArray<MyType>* array;

Swift will now report the array type as [MyType]!.

For some reason, generics on NSMutableArray doesn't seem to be picked up by Swift.

Despite that, you'll get a runtime error if you execute:

let block : MyType? = array[0]
like image 108
Matteo Pacini Avatar answered Jan 14 '23 22:01

Matteo Pacini