Swift3 ignoring Objective-C properties?

I am having so much fun converting our code to Swift 3. That's on us, but what drives me crazy is how slow Xcode is in reacting to anything - Quick Help, code completion, etc… things that in Swift 2 on XCode 7 were instant on Swift 3 take 10 seconds to a minute to display.


Anyhow - here is the latest issue - and I'm genuinely stumped:


Objective-C class

@interface AKContext : AKIntermediaryObject
@property(nonatomic, readonly) NSString *title;
@property(nonatomic, readonly) NSString *icon;
- (instancetype)initWithPIContext:(PIContext *)context;
@end


Swift3 "generated interface"

open class AKContext : AKIntermediaryObject {

    public init!(piContext context: PIContext!) /
}


For a brief second when I select the option, I get the correct properties. This of course gives me problems where when using AKContext in Swift - for example sorting an array of them:

return contexts.sorted{ $0.title.lowercased.localizedCompare($1.title.lowercased) == .orderedAscending }

I get an error:

Ambiguous reference to member 'title'


Now here is the interesting thing - in the implementation file (the .m) we have :

@interface AKContext ()
@property(nonatomic, strong) NSString *title;
@property(nonatomic, strong) NSString *icon;
@end


So that obviously the properties can be set internally in the implementation. If I comment that out - Swift works fine! Why is Swift caring about my implementation file and why would that make the properties invisible? Is this a bug or is this a Swift change? I've followed Swift Evolution, but I've never see anything like this.

So more interestingly, if I go ahead and remove the implementation interface category, then I stillget build errors:

error: ambiguous reference to member 'title'
                return contexts.sorted{ $0.title.lowercased.localizedCompare($1.title.lowercased) == .orderedAscending }
                                        ^~
__ObjC.AKContext:3:14: note: found this candidate
    open var title: String { get }
             ^
AddressBook.NSObject:5:15: note: found this candidate
    open func title(for person: ABPerson!, identifier: String!) -> String!
              ^


It knows its an AKContext - its an array of AKContexts. Swift knows this - or at least Quick Help shows that's what it is. So why is it confusing itself with addressbook? We have hundreds of classes with a title property on it … why is this so special?

Everything you say would make sense if the AKContext class extension:


@interface AKContext ()
@property(nonatomic, strong) NSString *title;
@property(nonatomic, strong) NSString *icon;
@end


was taken to be overriding the public @interface with new private properties rather than extending the public readonly properties with private setters. ("Private" because they're in the .m file, and therefore not exposed to clients of the class.) In fact, when I first looked at this, I immediately thought that you were Doing It Wrong™, because I thought clang required the extension to say "readwrite" explicitly, rather than letting it default to readwrite.


However, I just tried this and there's no longer an error in this situation. Either "strong" or "readwrite" in the class extension compiles without error.


OTOH, when I made a mixed project using such a class extension, I got different results in the generated interface of the Swift class that declared an array of AKContext. With "strong", I got:


internal var contexts: <<error type>>


but with "readwrite" I got:


internal var contexts: [AKContext]


So, I suggest you change your "strong" property redeclarations to "readwrite" and see what happens.


Obviously there is a bug or inconsistency somewhere (maybe not in Swift), so you should submit a bug report about this too.

I suspect this part of the problem is a secondary error. These are the sorts of complaints you'd expect if the Swift compiler inferred the type of $0 to be NSObject or AnyObject — like in Obj-C it will allow you to invoke any method that's been seen in the compilation unit, but in Swift it won't ignore conflicting method signatures amongst the candidates.


You could try declaring the closure parameter types explicitly, and see what happens.

Thank you QuinceyMorris. A bit more info - this is ONLY happening to two of my classes, but not the others. Unfortunately, readwrite doesn't make any difference.


As I'm playing with this, trying to understand what is unique about these classes (these are two of my simplest classes with both having almost no new code, just those couple properties and subclassing a parent class) - why its working with other classes, but not these - Swift is now segfaulting trying to build 🙂


I'll keep working on it. Its got to be something simple.

The other thing to check is to make sure you've #imported all necessary .h files in your bridging header.


This is a mixed-language target, right? (As opposed to a pure Swift target using a pure Obj-C framework.) Is it an app target or a framework target. The bridging seems to work slightly differently in the 2 cases.

I actually did try declaring the types explicitly as well with no luck. Right now, Swift isn't even giving me valid auto-completion and well…let me just say I've filed a dozen radars on xcode 8 tonight due to crashes, auto-complete/quick-help not working most of the time, having to do a full rebuild of the project constantly to see where I'm at with my conversion, and so on… right now Xcode 8 is a grand-ol mess for me. If it wasn't for iOS 10 SDK being necessary for me, I'd not bother. And the only reason we're converting to Swift 3 right now is that Swift 2.3 was even worse in Xcode 8 for us. We got no code formatting, errors in random places on the screen, etc..


Create a new project - that's super simple, it works fine. Open any of our large projects - and Xcode 8 is a mess.

Correct. Its bridged correctly. Using the object outright - it works fine:


let context = AKContext()
let title = context.title


Works fine.


However do this:

let contexts = [AKContext]()
let title = contexts.first?.title


And it breaks the same way. If I use other classes - they are fine. I tried to create a new Swift class and just port AKContext to swift - but that action is broken as well. Xcode creates a partial file and then bails out. I'm about to just do it by hand.

I sympathize. Stupid question: if you're converting code, why are you keeping Obj-C source? Could you rewrite the AKContext class hierarchy in Swift instead? I suspect your problem is some conflict across the super-/sub-class relationships. Rewriting in Swift might reveal a problem that slides under the radar in Obj-C source.

Swift3 ignoring Objective-C properties?
 
 
Q