Hi Marc, There are several subtleties here, but I think the compiler is actually doing the right thing.
The second class defines a static property that looks like it is 'overriding' the static property from the protocol extension, but since the types don't match (String vs. String?), it sort of 'overloads' the property (similar to function overloading). Nevertheless, the class still fulfills the requirements of the Trackable protocol, by inheriting the static property from the protocol extension. When you access analyticsID like a regular static property, the Swift compiler will choose the String property, because it shadows the String? property: let x = Something2.analyticsID print(x) // Wrong but compilers, returns wrong value print(type(of: x)) // String However, when the context of the expression Something2.analyticsID expects a String?, the Swift compiler will choose the String? property: let a: String? = Something2.analyticsID // explicit type annotation demands a String? print(a) // nil print(type(of: a)) // Optional<String> let b = Something2.analyticsID as String? // type cast demands a String? print(b) // nil print(type(of: b)) // Optional<String> A similar thing happens, when you write Something2.analyticsID ?? "nil". The nil coalescing operator ?? demands that the first parameter is an optional. Therefore, the Swift compiler will choose the String? property instead of the String property. I hope this helps! Best regards, Toni > Am 01.01.2018 um 18:29 schrieb Marc Palmer via swift-users > <swift-users@swift.org>: > > Hi, > > I hope everybody had a great New Year celebration. > > I was tracking down a weird bug in my Swift code today. A property defined in > a class in order to conform to a protocol was not being seen. A protocol > extension provided a default value of `nil` for this property, so I knew > where it was coming from. Turned out, in my class I had defined the property > with the correct name but incorrect type - I declared it as `String` instead > of `String?`. > > I isolated this behaviour in a playground, shown below, and it is pretty > weird behaviour. > > The output is: > > Something1 has id: nil > Something2 has id: nil > Something3 has id: Correct > -- Direct access-- > Something1 - nil > Something2 - nil > Something2 with String(describing:) - Wrong but compiles, returns wrong value > Something3 - Correct > > The playground code: > > ====================== > > protocol Trackable { > static var analyticsID: String? { get } > } > > extension Trackable { > static var analyticsID: String? { return nil } > } > > class Something1: Trackable { > } > > class Something2: Trackable { > static var analyticsID: String = "Wrong but compiles, returns wrong value" > } > > class Something3: Trackable { > static var analyticsID: String? = "Correct" > } > > func getID<T: Trackable>(_ trackable: T.Type) { > if let id = trackable.analyticsID { > print("\(trackable) has id: \(id)") > } else { > print("\(trackable) has id: nil") > } > } > > getID(Something1.self) > getID(Something2.self) > getID(Something3.self) > > print("-- Direct access--") > print("Something1 - \(Something1.self.analyticsID ?? "nil")") > print("Something2 A - \(Something2.self.analyticsID ?? "nil")") > print("Something2 with String(describing:) - \(String(describing: > Something2.self.analyticsID))") > print("Something3 - \(Something3.self.analyticsID ?? "nil")”) > ====================== > > Thanks in advance for any information about my misinterpretations or > recommendations of what parts are actually undesirable so that I can raise > the JIRAs. > > Cheers > > — > Marc Palmer > Montana Floss Co. Ltd. > > Soundproof – Music Player for Practice > http://getsoundproof.com > > > > _______________________________________________ > swift-users mailing list > swift-users@swift.org > https://lists.swift.org/mailman/listinfo/swift-users
_______________________________________________ swift-users mailing list swift-users@swift.org https://lists.swift.org/mailman/listinfo/swift-users