Understanding ? and ! in swift - Specific case

108 Views Asked by At

I am begining with swift and iOS and I have crashed against a doubt. Let see, having this particular source code:

if object?.stringValue != nil {
    // send it to server
    session.stopRunning()
    sendQRCodeToApiServer(stringValueObtained: (object?.stringValue)!)
}

I understand that in if object?.stringValue != nil the ? character means that object var could be nil, so it is an optional. But in the internal line:

sendQRCodeToApiServer(stringValueObtained: (object?.stringValue)!)

The interpreter throws an error if I don't unwrap the object?.stringValue in () and with a exclamation mark (!), that (if I had understood perfect) means that you are sure that the result have a value and it has the type required, I mean, the object.stringValue has a value and IT IS a string.

Well, I understand this perfect, but the compiler says that ? and ! are required, first says that maybe has a value and second that is sure that has a value, ........

I see this syntax incongruent and confusing, can anyone explain me?

Thanks a lot for your time

6

There are 6 best solutions below

1
On BEST ANSWER

Optionality / force unwrapping / optional chaining can be very confusing.

As a rule of thumb if you are finding that you have to force unwrap variables 'unexpectedly' then it is absolutely worth revisiting the code to deliberately unwrap variables you wish to access.

Some might say "Every time you use an ! , a baby unicorn dies (horribly)"

In your first line of code, object is an optional

if object?.stringValue != nil {

unless you specifically unwrap object you have to use the ? notation whenever you access it or it's members. Thats why when you access it again later inside the method call to sendQRCodeToApiServer, you have to unwrap it again.

When you unwrap an optional, the name you give to the unwrapped version of the variable works just like any other variable in that scope. Also when you unwrap an optional bear in mind that you are actively reducing the number of ? and ! that appear in your code so you are really making it much more readable and understandable for yourself in 6 months time or the next developer that sees your code (Hey and the unicorns love you).

Let's unwrap specifically the only variable you wish to use (if you had many lines of code that access object members then we could unwrap object - but let's stick with your code example)

if let stringValueObtained = object?.stringValue {
    // send it to server
    session.stopRunning()
    sendQRCodeToApiServer(stringValueObtained: stringValueObtained)
}

Note:

  • Outside of the scope of the if statement stringValueObtained does not exist

  • I specifically have named the unwrapped variable for it's intended use

0
On

The syntax force-unwraps an optional chained expression object?.stringValue. As you can't write object?.stringValue! because the result of stringValue is not an optional you have add the parentheses (object?.stringValue)!

Unfortunately the compiler suggests that syntax although it's practically exactly the same as object!.stringValue which is perfectly fine in your case as object is definitely not nil after the check.

0
On

use it with optional binding with 'if let'

if let stringVal = object?.stringValue {
    // send it to server
    session.stopRunning()
    sendQRCodeToApiServer(stringValueObtained: stringVal)
}

Always when you use optional chaining , then use if let binding because optional chaining always return an optional value ,whether the declaration of that value container means object is non optional.

1
On

if object?.stringValue != nil is only checked not nil. So, you need to unwrapping value. Recommand to use this code. if let stringValue = object?.stringValue { session.stopRunning() sendQRCodeToApiServer(stringValueObtained: stringValue) }

0
On
 sendQRCodeToApiServer(stringValueObtained: (object?.stringValue)!)

In this line the compiler prompts you to unwrap because the function "stringValueObtained" needs a non-nill value as an argument. If you change the parameter of the function as optional it won't prompt you to unwrap. Hope this helps you.

1
On

First, let inspect this:

if object?.stringValue != nil {

If object is nil or stringValue is nil, the condition will be false. That's objects?.whatEver will return nil if object is nil, and object?.stringValue will return nil if stringValue is nil.

sendQRCodeToApiServer(stringValueObtained: (object?.stringValue)!)

sendQRCodeToApiServer require a String, but object?.stringValue return a String? so you need to unwrap. Because of the if condition, so it's safe to unwrap here. Either by object!.stringValue (unwrap object) or (object?.stringValue)! - unwrap the returned value.

The better option is unwrap in if condition:

if let stringVal = object?.stringValue {
    // send it to server
    session.stopRunning()
    sendQRCodeToApiServer(stringValueObtained: stringVal)
}