Swift try inside Objective-C block

2019-06-20 15:02发布

I need to create a function foo that takes a throwing closure as a parameter. I can implement it with either Swift or ObjC but I need to be able to call it from both.

Like this:

// Swift
func bar() throws
func foo(_ block: () throws -> void)

foo {
  try bar()
}

and

// Objc
[self foo:^(
  [other barBar];
)];

I tried to implement it with both Swift and ObjC without succes. With Swift:

@objc
func foo(block: () throws -> Void)

I get this error:

Method cannot be marked @objc because the type of the parameter 1 cannot be represented in Objective-C

If I try to implement it with ObjC:

typedef BOOL (^ThrowingBlock)(NSError **);
- (void)foo:(ThrowingBlock)block;

Then it does not translate to a block that throws (as it would with a function):

func foo(_: (NSErrorPointer) -> Bool)

Any idea how to achieve this?

1条回答
We Are One
2楼-- · 2019-06-20 15:47

You could use the NS_REFINED_FOR_SWIFT macro to provide a unified interface between Objective-C and Swift, with throws in Swift and NSError ** in Objective-C.

From the Apple documentation:

You can use the NS_REFINED_FOR_SWIFT macro on an Objective-C method declaration to provide a refined Swift interface in an extension, while keeping the original implementation available to be called from the refined interface. For instance, an Objective-C method that takes one or more pointer arguments could be refined in Swift to return a tuple of values.

In your case you can declare foo as refined for Swift, and add a same method, in a class extension:

@interface MyClass : NSObject

- (void)foo:(void (^)(NSError **))block NS_REFINED_FOR_SWIFT;

@end

and in Swift:

extension MyClass {
    func foo(block: @escaping () throws -> Void) {
        // Objective-C's `foo` is now imported as `__foo`
        __foo { errPtr in
            do {
                try block()
            } catch {
                errPtr?.pointee = error as NSError
            }
        }
    }
}

Now you can call foo from both worlds, with the distinction that Objective-C code will need to pass a NSError ** block, while Swift callers can pass a nicer throws closure.

查看更多
登录 后发表回答