why do we need Bool
and throw
This question boils down to what you in fact want to achieve. I agree the given behaviour is inconsistent, and moreover, the Swift bridging rules were changed in the past and is likely a subject to change in future, so what you have currently broken, can turn into working sample in foreseeable future. However for now, this error says it all:
Throwing method cannot be an implementation of an @objc requirement because it returns a value of type Bool
; return Void
or a type that bridges to an Objective-C class
In other words, if your Swift method is marked with throws
keyword (and exposed to Objective-C runtime), it has to return a plain Objective-C type (not magically bridged Swift structures, like Int
, Bool
, Double
, etc.. it has to be something subclassed from NSObject
, NSProxy
or other Objective-C entities) OR return nothing. Period. Take it as a rule (at least for now). (P.S. This specific situation indeed looks like an LLVM bug, because the same set of requirements works perfectly fine when applied to a non-protocol Objective-C method (submitted it here, so the community has a chance to review it))
Having that said, the proper workaround to this situation depends on your final goal.
"Conventional" failable method
By "conventional" here I mean a contract that Cocoa/Cocoa touch programmer would usually expect. In this scenario a method with the following signature:
- (BOOL)failableMethodWithError:(NSError **)error;
Is commonly meant to fail wherever it returns NO
value (in the world of Objective-C). It's uncommon for Swift to deal with indirect parameters like NSError **
so in order to keep it consistent such methods are bridged as follows:
func failableMethod() throws
You can read more about this convention in the About Imported Cocoa Error Parameters documentation.
Failable method with preserved return type with swift_error(nonnull_error)
attribute
If you want to preserve return type AND the failable signature, you have two options. First is by giving the method swift_error(nonnull_error)
attribute. However in this case, in order to comply with existing bridging rules, your method has to have a type that "bridges" to an Objective-C class, e.g. NSNumber *
:
- (NSNumber *)failableMethodWithError:(NSError ** _Nullable)error __attribute__((swift_error(nonnull_error)));
And here is how you implement such a method in Swift:
func failableMethod() throws -> NSNumber {
return NSNumber(booleanLiteral: false)
}
Failable method with preserved return type with swift_error(none)
attribute
Another option to preserve the return type is by disabling Objective-C - Swift error signature conversion at all with use of swift_error(none)
attribute:
- (BOOL)failableMethodWithError:(NSError ** _Nullable)error __attribute__((swift_error(none)));
In this scenario you can return a scalar type from the failable method, but at the same time you will have to deal with NSErrorPointer
type:
func failableMethodWithError(_ error: NSErrorPointer) -> Bool {
if (/*error condition */) {
error?.pointee = NSError(domain: TDWErrorDomain, code: TDWErrorDomainErrorCode)
}
return true
}