Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-6873] Can't use Swift closure as ObjC block type (Any type) #49422

Open
krzyzanowskim opened this issue Jan 30, 2018 · 5 comments
Open

[SR-6873] Can't use Swift closure as ObjC block type (Any type) #49422

krzyzanowskim opened this issue Jan 30, 2018 · 5 comments

Comments

@krzyzanowskim
Copy link
Contributor

krzyzanowskim commented Jan 30, 2018

Previous ID SR-6873
Radar None
Original Reporter @krzyzanowskim
Type Bug
Environment

Xcode 9.2, Swift 4.0.3

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug
Assignee None
Priority Medium

md5: 3454906c1c80a880911892efb72c45b0

Issue Description:

Consider this ObjC block:

typedef void (^PSPDFRenderDrawBlock)(CGContextRef context, NSUInteger page, CGRect cropBox, NSUInteger rotation, NSDictionary<NSString *, id> *_Nullable options);

that is imported to Swift as (notice no @convention(block) here)

public typealias PSPDFRenderDrawBlock = (CGContext, UInt, CGRect, UInt, [String : Any]?) -> Swift.Void

and stored in Dictionary on Swift side:

let renderDrawBlock: PSPDFRenderDrawBlock = { context, page, cropBox, _, _ in
    // body
}

let dict: [String: Any] = ["foo": renderDrawBlock]

next, the `dict` is passed to the ObjC method as an argument of `NSDictionary<NSString, id>` type.

So far so good, everything compiles and works.

Now, when I try to retrieve the closure on the ObjC side and use it (call it). It crashes in runtime.

auto value = dictionary[@"foo"]; // _SwiftValue *

now, trying to use `value` it will crash of course.

The known workaround is to specify @convention(block) manually and unsafeBitCast() to AnyObject. See: https://stackoverflow.com/a/32787157/699944 for complete problem and example

@jckarter
Copy link
Member

jckarter commented Jan 30, 2018

It's an unfortunate limitation of id-as-Any that we can't dynamically bridge ObjC blocks back and forth between closures if we don't statically know that the elements are closures at compile time. You can make the dictionary have [String: PSPDFRenderDrawBlock] type instead of [String: Any] type, or if it needs to be Any, assign explicitly @convention(block) values into it as you noted.

@krzyzanowskim
Copy link
Contributor Author

krzyzanowskim commented Jan 30, 2018

Is it possible, in any way to import ObjC block type as @convention(block) automatically without declaring additional type?

@jckarter
Copy link
Member

jckarter commented Jan 30, 2018

@belkadan might be a better person to answer that question.

@belkadan
Copy link
Contributor

belkadan commented Jan 30, 2018

Hm, not at the moment, but it fits into some existing requests we have not to bridge classes to values. I'll keep this in mind.

@swift-ci
Copy link
Collaborator

swift-ci commented May 13, 2020

Comment by Hovhannes (JIRA)

for this header:

```

#import <Foundation/Foundation.h>

typedef void (^ExperimentBlock)(void);

NS_ASSUME_NONNULL_BEGIN

@interface MyClass : NSObject

+ (void)f:(NSDictionary<NSString *, ExperimentBlock> *)experimentVariant;

@EnD

NS_ASSUME_NONNULL_END

```

generated swift code is:

```

import Foundation

public typealias ExperimentBlock = () -> Void

open class MyClass : NSObject {

open class func f(experimentVariant: \[String : @convention(block) () -\> Void\])

}

```

f function swift version is not accepting `ExperimentBlock` .

Is there a way to fix it?

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants