Why was UITextField's text property changed to an optional in Swift 2?

10,473

Solution 1

In short, (answer to the title) it wasn't.

In detail:

To me it makes a lot more sense to have it as an optional that isn't forced unwrapped. Apple is pushing devs to never just use optional! and it only makes sense that they apply the same rules to the API's.

The reason for this is that it can be nil, and it doesn't make any difference if it is declared with ? or ! for running the code. Using ! actually just removes the warnings in Xcode which are really handy, especially when it comes to API code. If you don't realise it actually is an optional you are just asking for trouble.

Checking for nil is also much nicer now with guard and you can chain this with a check for "" so it's not really more work.

In general optionals are better because something that is nil is not using memory. The more optionals we got, the lighter we can make our apps. Also it doesn't even look bad and doesn't add to the pyramid of doom.

This example will take both strings as arguments, remove the ? in the func parameter and Xcode will be there to warn you.

I forgot to answer this part directly : It becomes nil when you set it to nil, which you might do to save a little bit of memory. It just doesn't make sense to have the option to set it to nil and not have xcode warn you to handle it properly. => This is impossible...

var forcedUnwrappedString : String! = ""
var optionalString : String? = ""

forcedUnwrappedString = nil
optionalString = nil

func doSomethingWithString(string : String?) -> String? {
    guard var unwrappedString = string else {
        // error handling here
        return nil
    }
    let tempString = unwrappedString + "!"

    return tempString
}

func doSomethingUnsafeWithString(string : String) -> String {

    let tempString = string
    return tempString

}

var newString = doSomethingWithString(optionalString)
var newString2 = doSomethingWithString(forcedUnwrappedString)

newString = doSomethingUnsafeWithString(optionalString!) // this will crash without a warning fro xcode
newString2 = doSomethingUnsafeWithString(forcedUnwrappedString) // this will crash without a warning fro xcode

Update:

The text property of the UITextfield has a setter that always sets to "" in case of nil, no info on this anywhere in the docs or in the UIKit .h files.

var textField = UITextField(frame: CGRect(x: 0, y: 0, width: 0, height: 0))
var string = textField.text // string = ""
textField.text = nil
string = textField.text // string = ""

Solution 2

A simple way around this problem is to create an extension to UITextField and use that instead of the .text property.

extension UITextField {
    var unwrappedText: String {
        return self.text ?? ""
     }
}

Now you can say textfield.unwrappedText without worrying about optionals. (Of course this is just for reading the value).

Solution 3

As Menke mentioned himself, it's actually impossible to set text to nil. Obviously Apple wants it to be documented as nullable, despite the current implementation, and to me it doesn't make any sense - I mean doesn't make enough sense to me. The thing is that it's definitely not "wrong" for Apple to decide that, but is it the elegant way of doing things to everyone? Obviously not. And if you don't agree with them, don't worry, it's totally fine for you to reserve your opinions on some of Apple's decisions.

So what's the purpose of this change? Maybe they started an internal rule that said never make any properties of UIKits components non-nullable for consistency concern. Maybe they think in theory it's possible for the text property to be released due to memory pressure in some extreme case. Whatever they may be, I don't think they are valid, yet we have to obey. This however doesn't make me a fanboy.

Share:
10,473

Related videos on Youtube

Will
Author by

Will

Frontend & iOS in Los Angeles, CA. Passionate about UI / UX and Interaction Design Portfolio

Updated on June 06, 2022

Comments

  • Will
    Will almost 2 years

    According to the UIKit diff document, in ios9/Swift 2

    var text: String! has become var text: String?

    According to the documentation for UITextField it specficially says

    This string is @"" by default.

    I don't understand the purpose of this change. Shouldn't that property always be an empty string if the text field exists at all? At what point does this field return an empty string? Once the user interacts with it? Once it's been added to the view hierarchy? At what point does it return nil?

    If the text field exists in the first place, is it always safe to assume the text property exists as well? This just seems like it's going to lead to a lot of find/replace .text to .text!

    I don't see where it's mentioned in the docs so maybe someone has some backstory or help on why this changed.

    • Will
      Will over 8 years
      For more detail, I just setup a playground and made a UITextField. The text property is in fact instantly an optional empty string. I'm now more confused
    • teradyl
      teradyl about 8 years
      It seems like Apple doesn't trust their API to always give us a non-nil value for the text property, so they're just transferring the guard responsibility to us. Not a fan of this approach.
  • Will
    Will over 8 years
    I don't think you parsed my question here. I definitely understand the value of optionals. I am specifically asking about UITextField and it's text:String property. In iOS 9 it was made an optional, while before it was not, and the documentation still says it will always be an empty string. Testing the playground confirms this. I am wondering why this was changed or if there is some use case that makes this valuable.
  • R Menke
    R Menke over 8 years
    I did read your entire question, I also didn't bother explaining what optionals are. I explained why Apple changed it from a forced unwrapped optional to one that isn't because your question states it was a forced unwrapped optional before and is a regular in Swift2. Now in your comment you state it wasn't an optional before?
  • R Menke
    R Menke over 8 years
    Just checked, it was a forced unwrapped optional before, so you could set it to nil. But xcode wouldn't warn you about it.
  • R Menke
    R Menke over 8 years
    Updated to code snippet to better illustrate what I mean. As you can see it wasn't safe.
  • Will
    Will over 8 years
    "it was a forced unwrapped optional before" answers why they made it not hide it's unsafe behavior. But why is it optional to begin with? I made a simple test project with one UITextField and set each deletgate func to print out the text field's text property. Always Optional(""). I then added a textField.text = nil in textFieldShouldBeginEditing: but printing out the textField.text right after the assignment reports that it's still Optional(""). So the setter enforces the string to always be at least an empty string. Eg, it's never nil even if it's assigned that value.
  • R Menke
    R Menke over 8 years
    You are right. Haven been going through the source files. No reference to this behaviour anywhere. Maybe update you're question so it reflects what the issue really is. Too bad we can't look at UIKit .m files. My best guess is that this isn't limited to the UITextfield and that they are updating more stuff to be optionals. Having a setter with a default value is a safe transition.
  • Nicolas Miari
    Nicolas Miari over 8 years
    If it never becomes nil, it is safe to force-unwrap? I'm trying to get the text form a UITextField, and if it is the empty string use the placeholder instead. Adding optional checking/unwrapping to this logic makes it quite challenging...
  • Matt Le Fleur
    Matt Le Fleur over 8 years
    @NicolasMiari For your case this should be fine since it sounds like you are using strings. However, if you force unwrap a UITextField then immediately try and convert it to an int, e.g. Int(textField.text!) you will get nil not the empty string. In this situation I would suggest using something like if let number = Int(textField.text!) { ... }.
  • Nicolas Miari
    Nicolas Miari over 8 years
    Yes, I am just trying to get the string and use it as a string, nothing fancy.
  • eLillie
    eLillie over 7 years
    @RMenke, to clarify, when you say "it wasn't", you're differentiating iOS SDK from Swift version, or is there more going on here? It might be helpful to some to spell out what you mean more explicitly.
  • jscs
    jscs over 6 years
    "something that is nil is not using memory" nil and an empty NSString take up the same insignificant amount of space -- 1 word -- in ObjC, and Swift's nil is a distinct value in an enum that also takes up a few bytes. Nothing's free (although the cost may not matter).
  • thisIsTheFoxe
    thisIsTheFoxe over 2 years
    Apple is pushing devs to never just use optional! I know I've heard that a Buch before but I never found it concrete in the docs or anywhere. Did Apple ever officially suggest "never" to use force unwraps?