3

Consider this Objective-C class:

@interface TestAPI : NSObject

-(nullable id)aNullableMethod;

-(nullable id)aNullableMethodWithError:(inout NSError **)error;

@end

I'm using TestAPI as an abstract base class and want to inherit from it in Swift.

Swift bridges this code to look like this (i.e. Counterparts menu -> Swift 4.2 Interface):

open class TestAPI : NSObject {

    open func aNullableMethod() -> Any?

    open func aNullableMethodWithError() throws -> Any
}

The problem is that aNullableMethodWithError() can return nil as a valid value.

How do I convince Swift that aNullableMethodWithError() returns Any? and not Any?

Or is there a better way to write the Objective-C method signature?

Daniel Pietsch
  • 121
  • 1
  • 8
  • What is the significance of returning `nil`, if it isn't "there was an error and I couldn't return anything meaningful"? – matt Sep 18 '18 at 21:55
  • @matt - My example is deliberately simple to show the bridging issue that I am seeing. Imagine a class that represented a contact and I wanted to ask for a field name "BusinessPhoneNumber". It's not an error to be missing a business phone number, so I would like to return `nil`. – Daniel Pietsch Sep 18 '18 at 22:00
  • But in that example, why would there be a method that takes an `NSError**`? Do you see what I'm getting at? You are artificially positing an antipattern, and I'm asking you justify that in practical terms. There is no "bridging issue" as long as you obey the pattern, and there is no reason not to, so why not just obey it? – matt Sep 18 '18 at 22:06
  • See https://stackoverflow.com/q/34786115/2976878 – the importer assumes that returning `nil` means that the method threw an error (as such is Cocoa convention). If you really want to disregard that convention, you could use `__attribute__((swift_error(nonnull_error)))` as shown at the bottom of Martin's answer to change the behaviour such that the method is considered to have thrown if it leaves a non-nil `NSError*` in the out-parameter (thus preserving the optionality of the return). – Hamish Sep 18 '18 at 22:27

1 Answers1

4

You may need to use NS_SWIFT_NOTHROW:

-(nullable id)aNullableMethodWithError:(inout NSError **)error NS_SWIFT_NOTHROW;

This is imported into Swift as:

open func aNullableMethodWithError(_ error: NSErrorPointer) -> Any?

If you want to import your method as throws-function, you may need to change the method signature to follow the convention:

  • Return true or non-nil on no error
  • Return false or nil on error

Something like this:

-(BOOL)aNullableMethod:(id*)object withError:(inout NSError **)error;

In Swift:

open func aNullableMethod(_ object: AutoreleasingUnsafeMutablePointer<AnyObject?>!) throws
OOPer
  • 47,149
  • 6
  • 107
  • 142