Search code examples
swiftoption-typeoptional-chaining

Why do I get error when I do optional chaining?


protocol textingprotocol : class {
    func didEnterText (text:String)
}

class secondViewController: UIViewController {    
weak var delegate:textingprotocol?

@IBOutlet weak var txtField: UITextField?

@IBAction func dismissButton(sender: UIButton) {

    delegate!.didEnterText(txtField?.text) // A: doesn't work
    delegate!.didEnterText(txtField?.text!) // B: doesn't work
    delegate!.didEnterText((txtField?.text)!) // C: works

}

A: Am I not already doing optional chaining and the line would only work if text has a value if not then it would gracefully fail? Yet it gives:

Value of optional type 'String?' not unwrapped; did you mean to use '!' or '?'?

B: Even when I am given compiler error of above, well I do unwrap it, still it's not satisfied it wants it to be like C. To my understanding with the ? I have unwrapped txtField and with the ! I have unwrapped text, still confused why it doesn't work

Why does C work but not B? Isn't there a more cleaner way than line C? Line C looks very unappealing.


Solution

  • didEnterText does not take a String?. It takes a String. Thus, you cannot use simple optional chaining with question marks to get its argument. You must actually unwrap to a String, not tentatively unwrap to a String?-or-nil.

    Now, txtField is an Optional, so you need to unwrap it.

    And a UITextField's text property is an Optional, so you need to unwrap it too.

    So the simplest approach is to unwrap them both, absolutely:

        delegate!.didEnterText(txtField!.text!)
    

    If you don't want to do that (because you fear that one of them will be nil and crash your app), you will have to protect the whole thing with if let:

        if let s = txtField?.text {
            delegate!.didEnterText(s)
        }