Passing an array of closures to a swift method form objectiv-c file

guys,

I created a swift class like this:


class SwiftClass
{

    override init()
    {
        super.init()
    }

    static func Test1(closure:()->Void){
        closure()
    }

    static func Test2(array:[()->Void]){
        for closure in array {
            closure()
        }
    }
}


Both 2 methods wok fine from swift file.


SwiftClass.Test1(closure:{print("Test1")})
SwiftClass.Test2(array:[{print("Test2_closure1")},{print("Test2_closure2")}])


The function Test1 work fine from objective-c file too.

[SwiftClass Test1WithClosure:^{
        NSLog(@"SwiftClass Test1") ;
    }] ;


but Test2 can't be used from objective-c file,the automatically generated -Swift.h file didn't declare it at all.

SWIFT_CLASS("_TtC17ArrayClosuresTest10SwiftClass")
@interface SwiftClass : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
+ (void)Test1WithClosure:(void (^ _Nonnull)(void))closure;
@end


Is it possible to call Test2 from objective-c file?

Replies

If you explicitly declare the array-based method as "@objc", you'll get the following compilation error message: "method cannot be marked @objc because the type of the parameter cannot be represented in Objective-C".


Since you don't get this error with an array of (say) Int, and with a non-array closure parameter, this suggests the compiler could make an @objc-compatible method, but chooses not to try. Or, there may be a technical reason why this array/closure combination can't be represented in Obj-C.


As a workaround, you could change the Test2 parameter type to [AnyObject], or specify the parameter type as NSArray, and cast individual elements as the closure type.


Either way, it's probably worth a bug report, in case this is something that is really supposed to work.

THX,

I tried the [AnyObject] and NSArray workaround ,

static func Test3(array:[AnyObject]){
        for closure in array {
            (closure as! ()->Void)()
        }
    }

It build successlly but I got a runtime error

"Could not cast value of type '__NSGlobalBlock__' (0x1188e9040) to '(()) -> ()' (0x1188ef030)."

Is there any mistake in the casting?

The reason why your first attempt didn’t work is that Swift closures and Objective-C blocks are not identical (although they are closely related). You can get your

test2(array:)
method exposed to Objective-C by adopting
@convention(block)
, as shown here:
typealias Callback = @convention(block) () -> Void

class Test : NSObject {
    class func test2(array: [Callback]) {
        …
    }
}

Or, as you’ve already discovered, using NSArray. However, the cast required to call the callbacks still fails. If you expand the above to this:

@objc class func test2(array: [Callback]) {
    for item in array {
        item()
    }
}

you get:

fatal error: NSArray element failed to match the Swift Array Element type

And, as you’ve noted, in the NSArray case, where you have to do an explicit cast, you get a more focused error (even if you use

@convention(block)
).

Unfortunately I was unable to find a way around this, which brings me to the same conclusion as QuinceyMorris: this is definitely worth a bug.

Please post your bug number, just for the record.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I posted the bug and got a bug number 28995623. Can other peaple browse it via the number?

btw,I found a not so good workaround :

var closureArray:[()->Void]=[]

public func add(closure:@escaping ()->Void)
{
   closureArray.append(closure)
}


😕

I posted the bug …

Thanks.

Can other [people] browse it via the number?

Not directly. You can publish its status via Open Radar or, alternatively, file a bug in Swift’s bug system, which is world readable (I shoulda sent you that link the first time around but wasn’t thinking clearly).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

any news on the fix for this bug?

any news on the fix for this bug?

No. If you’re feeling enthusiastic you could try fixing it yourself (-:

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"