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?
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.