3

I've just finished debugging a situation where some of my labels were not displaying any text, despite the string in question definitely containing a value (it was printing the line before). I eventually pinned it down to returning different values depending on the way I accessed the string. Please note I'm not looking for ways to work around this, I am looking for a reason I am missing as to why this behaviour happens.

I had a protocol with the following optional String property, set to default to nil:

protocol SomeProtocol {
    var titleString: String? { get }
}

extension SomeProtocol {
    var titleString: String? {
        return nil
    }
}

Which was implemented in a class with a not optional String, with title set elsewhere:

class SomeClass: SomeProtocol {
    var title: String
    var titleString: String {
        return title
    }
}

When attempting to access the value of titleString from a SomeClass object, it always returned nil, regardless of the title property. This was seen through assigning to a label like:

label.text = someClassInstance.titleString

However, when I printed the value or set the label through

label.text = "\(someClassInstance.titleString)"

everything worked, and it displayed the text.

I've narrowed the source of this down to where I've overridden the optional property with one not optional. Clearly when I access the property directly it is returned by the protocol implementation, while using it by string interpolation returns the class one. What is actually behind this behaviour?

Edit: to demonstrate this behaviour run this gist in an Xcode playground: https://gist.github.com/CaileanWilkinson/357c17f36d04b522b9bcf1241a825d9f

6
  • Can you please post code that actually reproduces the issue? I tried your code in an Xcode 9.4 playground and I did not see nil either way. Commented Jul 30, 2018 at 3:30
  • @rmaddy Try assigning it to a label's text property and then print the label's text in your playground Commented Jul 30, 2018 at 3:37
  • @rmaddy It's displayed in the code in this gist: gist.github.com/CaileanWilkinson/… Commented Jul 30, 2018 at 3:39
  • Using a label's text property allowed my to reproduce the issue. Commented Jul 30, 2018 at 3:41
  • So it seems that if the result is known to be a String? then the titleString with the return type of String? is called. If the result is known to be a String then the titleString with the return type of String is called. I don't know if this is a bug in some way but it is enough info to avoid the problem. Commented Jul 30, 2018 at 3:45

1 Answer 1

3

I feel like this is somewhat similar to the solution of this question of mine. In that question, there is also two almost identical properties - one optional, the other non-optional. And I experienced a similar situation where Swift can't figure out which property I want.

Your titleString in SomeClass is not overriding the titleString property in the protocol. This is reflected in Xcode's suggestions:

enter image description here

You can access both properties like this:

someObject.titleString as String // accesses the one in SomeClass
someObject.titleString as String? // accesses the one in the protocol

My point here is that the type of the expression matters. If the type of expression swift expects is String, then it resolves to the one in SomeClass. If the expected type of the expression is String?, then it evaluates to the one in the protocol.

This explains why setting the label's text without string interpolation will call the property in the protocol (label.text is String?, so it expects a String?) and why using string interpolation will call the property in SomeClass (String interpolation expects a non-optional).

Sign up to request clarification or add additional context in comments.

1 Comment

Ah, I see. So rather than being a Swift issue, it seems there is a bug in AppCode which was (somewhat understandably) showing SomeClass.titleString as implementing the SomeProtocol.titleString property.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.