Ethan G Ethan G - 9 months ago 35
Objective-C Question

Converting to Swift 3 renamed my own Objective-C method

I have swift classes mixed in with my Objective-C code. With Swift 2.3, everything was fine and worked as expected.

I recently converted to Swift 3, and it updates several API calls because of all the renaming that occurred for Swift 3. That's fine; I get that.

But what's not fine is that Swift 3 seems to have renamed a method in one of my Objective-C classes. I own the Objective-C class and I called the method what I wanted:

readDeliveryInfoItems
. But now, after converting to Swift 3, I can't call
.readDeliveryInfoItems()
anymore in my Swift class. It's telling me it has been renamed to
.readItems()
.

That makes no sense. And the Objective-C class still calls the method
readDeliveryInfoItems
, so there is something under the covers going on here.

I have tried renaming the Objective-C
readDeliveryInfoItems
method to
readDeliveryInfo
, building (Swift fails because it says that the
readInfo()
method doesn't exist, which is good), and then renaming the method back to
readDeliveryInfoItems
. However, when I build after this, Swift goes back to thinking the method is called
readInfo()
. I was hoping this would trick Xcode into refreshing the Swift bridging and renaming the method back to the correct name
readDeliveryInfoItems()
, but it did not.

How can I fix this?

UPDATE TO ADD MORE INFO

The interface of my Objective-C class has this function declaration:

- (nullable NSArray<XMPPDeliveryInfoItem *> *)readDeliveryInfoItems;


But in the Generated Interface (see MartinR's comment below) for that class, the function declaration is this instead:

open func readItems() -> [XMPPDeliveryInfoItem]?


There are other functions in that class that are similar to the
readDeliveryInfoItems
function, such as this one:

- (nullable NSArray<XMPPDeliveryInfoItem *> *)sentDeliveryInfoItems;


And they look correct in the Generated Interface:

open func sentDeliveryInfoItems() -> [XMPPDeliveryInfoItem]?


So I can't figure out why I'm having this problem with only the one function.

Answer Source

The translation process is described in detail in

The relevant part for your question is (emphasis mine):

Prune a match for the enclosing type from the base name of a method so long as the match starts after a verb. For example,

extension UIViewController {
  func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)? = nil)
}

becomes:

extension UIViewController {
  func dismissAnimated(flag: Bool, completion: (() -> Void)? = nil)
}

This pruning algorithm is – as far as I can see – implemented in StringExtras.cpp (and uses a lot of heuristics), and PartsOfSpeech.def contains a list of words which are considered a verb, such as

VERB(dismiss)
VERB(read)
VERB(send)

but not VERB(sent). That explains why – simplifying your example slightly –

@interface DeliveryInfo : NSObject
-(void)readDeliveryInfoItems;
-(void)sentDeliveryInfoItems;
@end

becomes

open class DeliveryInfo : NSObject {
    open func readItems()
    open func sentDeliveryInfoItems()
}

The type name is pruned after the verb "read", but not after the non-verb "sent". (You can verify that by changing the second method name to sendDeliveryInfoItems which is then mapped to sendItems().)

You can override the mapping with NS_SWIFT_NAME:

-(void)readDeliveryInfoItems NS_SWIFT_NAME(readDeliveryInfoItems());