I'm new to Swift and is trying out the beginner's project of building a calculator. I understand that "display.text" returns an optional string and the string value inside of it has to be unwrapped with "!" before it can be used.

However, I have noticed "display.text" only needs to be unwrapped once and then it can be used multiple times without unwrapping it again. Is this true for Swift optional values? Where could I find some guidelines regarding to the matter?

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var display: UILabel!

    var userIsInTheMiddleOfTypingANumber = false

    @IBAction func appendDigit(sender: UIButton) {
        let digit = sender.currentTitle!
        if userIsInTheMiddleOfTypingANumber {
            display.text = display.text! + digit
        } else {
            display.text = digit
            userIsInTheMiddleOfTypingANumber = true
        }
    }
3

There are 3 best solutions below

2
On BEST ANSWER

You should generally avoid to forcibly unwrap optionals (with operator !), as this will yield a runtime exception in case the optional contains nil. Below follows some techniques to handle unwrapping of optionals.

Optional binding

Note that the only way you can "unwrap it once and then use it multiple times" if if you unwrap and assign it to another non-optional variable to the same inherent type.

This is what is done when using optional binding:

/* Example setup */
let display: UILabel = UILabel()
let digit = "1"

/* optional binding using if-let:
   _assign_ unwrapped value (if non-nil) to 'unwrapped' */
if let unwrappedText = display.text {
    // 'unwrapped' resides in scope inside of the if-let block
    display.text = unwrappedText + digit
}
else {
    display.text = digit
}

/* optional binding using guard-let-else:
   _assign_ unwrapped value (if non-nil) to 'unwrapped' */
func foo(disp: UILabel, _ dig: String) {
    guard let unwrappedText = display.text else {
        display.text = digit
        return
    }
    // 'unwrapped' resides in scope outside of the guard-let-else block
    display.text = unwrappedText + digit
}
foo(display, digit)

Nil coalescing operator

If you don't want to explicitly assign the unwrapped value using conditional binding, you can make use of the nil coalescing operator for safe unwrapping.

/* nil coalescing operator */
display.text = (display.text ?? "") + digit

Now, you could, however, use the nil coalescing operator in a semi-optional-binding fashion; assign the unwrapped value of an optional or some default value if the optional is nil:

let metaUnwrapped = display.text ?? ""

Immutable metaUnwrapped would be available in its scope, and contain the value of display.text (at assignment), if non-nil, or the default value "", if display.text was nil at assignment. You could use metaUnwrapped in the same fashion as immutable unwrapped in the optional binding examples above:

display.text = metaUnwrapped + digit

Optional chaining

This is slightly off-base w.r.t. your question, but since we're on the subject of optionals and unwrapping, I might as well mention optional chaining.

Optional chaining can be used to access properties of some optional property, given that the optional property is not nil. As an example, say you want to count the number of characters in the display.text, but naturally only if the optional .text property is non-nil. In this case, optional chaining combined with the nil coalescing operator could be a proper method of choice:

let numCharacters = display.text?.characters.count ?? 0
    /* if text != nil, returns character count; otherwise, by
       nil coalescing operator, returns 0 /*
0
On

Two standard options for unwrapping an optional where it can be used again:

1) if let

if let unwrappedValue = someOptional {
  // unwrappedValue can be used only inside this block of code
}

2) guard

guard let unwrappedValue = someOptional else { return } 
/// unwrappedValue can be used as long as it is in scope
0
On

Try this.

guard let displayText = display.text else {
    // handle the case when optional has nil value
}

// In this scope you can use non-nil value of display.text as displayText.
print("displayText = \(displayText)")

This is how you can use optional values after unwrapping once. Another simpler way would be to just use instead of guard-let.

if let displayText = display.text else {
    // safely unwrapped
}

Hope that helped!

Check this link for more help.