ChatGPT解决这个技术问题 Extra ChatGPT

Swift: Testing optionals for nil

I'm using Xcode 6 Beta 4. I have this weird situation where I cannot figure out how to appropriately test for optionals.

If I have an optional xyz, is the correct way to test:

if (xyz) // Do something

or

if (xyz != nil) // Do something

The documents say to do it the first way, but I've found that sometimes, the second way is required, and doesn't generate a compiler error, but other times, the second way generates a compiler error.

My specific example is using the GData XML parser bridged to swift:

let xml = GDataXMLDocument(
    XMLString: responseBody,
    options: 0,
    error: &xmlError);

if (xmlError != nil)

Here, if I just did:

if xmlError

it would always return true. However, if I do:

if (xmlError != nil)

then it works (as how it works in Objective-C).

Is there something with the GData XML and the way it treats optionals that I am missing?

Can we see a full example for your unexpected case and error cases, please? (And I know it's hard, but try to start losing the brackets around the conditionals!)
this is changed in Xcode 6 beta 5
I just updated the question with the unexpected case.
I've not updated to beta 5 yet. I will do that soon; nice to see ?? in Swift, and more consistent optional behavior.

p
paulmelnikow

In Xcode Beta 5, they no longer let you do:

var xyz : NSString?

if xyz {
  // Do something using `xyz`.
}

This produces an error:

does not conform to protocol 'BooleanType.Protocol'

You have to use one of these forms:

if xyz != nil {
   // Do something using `xyz`.
}

if let xy = xyz {
   // Do something using `xy`.
}

Can someone elaborate on why xyz == nil is not working?
The second example should read: // Do something using xy (which is the main difference in use cases between the two variants)
@Christoph: The error message is that constant 'xyz' used before being initalized. I think you need to append '= nil' at the end of line of declaration of xyz.
For those who are new to swift as I am and are wondering: You still have to unwrap xyz inside the if block. It's safe though, even if you force unwrap
@Omar That's the beauty of the second form, you use xy inside the block without having to unwrap it.
F
Flimm

To add to the other answers, instead of assigning to a differently named variable inside of an if condition:

var a: Int? = 5

if let b = a {
   // do something
}

you can reuse the same variable name like this:

var a: Int? = 5

if let a = a {
    // do something
}

This might help you avoid running out of creative variable names...

This takes advantage of variable shadowing that is supported in Swift.


I
Isaac Drachman

One of the most direct ways to use optionals is the following:

Assuming xyz is of optional type, like Int? for example.

if let possXYZ = xyz {
    // do something with possXYZ (the unwrapped value of xyz)
} else {
    // do something now that we know xyz is .None
}

This way you can both test if xyz contains a value and if so, immediately work with that value.

With regards to your compiler error, the type UInt8 is not optional (note no '?') and therefore cannot be converted to nil. Make sure the variable you're working with is an optional before you treat it like one.


I find this method to be bad because I create a new variable (constant) unnecessarily, ans having to created a new name that most of the time is gonna be worst than the previous. Takes me more time to write and for the reader.
It's the standard method and recommended method to unwrap an optional variable. "'if let " is seriously powerful.
@gnasher729 but what if you only want to use the else part
F
Fangming

Swift 3.0, 4.0

There are mainly two ways of checking optional for nil. Here are examples with comparison between them

1. if let

if let is the most basic way to check optional for nil. Other conditions can be appended to this nil check, separated by comma. The variable must not be nil to move for the next condition. If only nil check is required, remove extra conditions in the following code.

Other than that, if x is not nil, the if closure will be executed and x_val will be available inside. Otherwise the else closure is triggered.

if let x_val = x, x_val > 5 {
    //x_val available on this scope
} else {

}

2. guard let

guard let can do similar things. It's main purpose is to make it logically more reasonable. It's like saying Make sure the variable is not nil, otherwise stop the function. guard let can also do extra condition checking as if let.

The differences are that the unwrapped value will be available on same scope as guard let, as shown in the comment below. This also leads to the point that in else closure, the program has to exit the current scope, by return, break, etc.

guard let x_val = x, x_val > 5 else {
    return
}
//x_val available on this scope

I
Iulian Onofrei

From swift programming guide

If Statements and Forced Unwrapping You can use an if statement to find out whether an optional contains a value. If an optional does have a value, it evaluates to true; if it has no value at all, it evaluates to false.

So the best way to do this is

// swift > 3
if xyz != nil {}

and if you are using the xyz in if statement.Than you can unwrap xyz in if statement in constant variable .So you do not need to unwrap every place in if statement where xyz is used.

if let yourConstant = xyz {
      //use youtConstant you do not need to unwrap `xyz`
}

This convention is suggested by apple and it will be followed by devlopers.


In Swift 3 you have to do if xyz != nil {...}.
In Swift 3 optionals cannot be used as boolean. First because it is a C-ism that should never have been in the language in the first place, and second because it causes absolute confusion with optional Bool.
C
Chris Frederick

Although you must still either explicitly compare an optional with nil or use optional binding to additionally extract its value (i.e. optionals are not implicitly converted into Boolean values), it's worth noting that Swift 2 has added the guard statement to help avoid the pyramid of doom when working with multiple optional values.

In other words, your options now include explicitly checking for nil:

if xyz != nil {
    // Do something with xyz
}

Optional binding:

if let xyz = xyz {
    // Do something with xyz
    // (Note that we can reuse the same variable name)
}

And guard statements:

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    // e.g. by calling return, break, continue, or throw
    return
}

// Do something with xyz, which is now guaranteed to be non-nil

Note how ordinary optional binding can lead to greater indentation when there is more than one optional value:

if let abc = abc {
    if let xyz = xyz {
        // Do something with abc and xyz
    }        
}

You can avoid this nesting with guard statements:

guard let abc = abc else {
    // Handle failure and then exit this code block
    return
}

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    return
}

// Do something with abc and xyz

as mentioned above, you can do this too to get rid of nested optional statmenets. if let abc = abc?.xyz?.something { }
@Suhaib Ah, true: You can also use optional chaining (developer.apple.com/library/prerelease/content/documentation/…) to quickly access nested properties. I didn't mention it here because I thought it might be too much of a tangent from the original question.
@turingtested Thanks! Actually, I think this is programmer friendly in the sense that it guarantees you won't accidentally try to use a nil value, but I agree that the learning curve is a bit steep. :)
B
Brody Robertson

Swift 5 Protocol Extension

Here is an approach using protocol extension so that you can easily inline an optional nil check:

import Foundation

public extension Optional {

    var isNil: Bool {

        guard case Optional.none = self else {
            return false
        }

        return true

    }

    var isSome: Bool {

        return !self.isNil

    }

}

Usage

var myValue: String?

if myValue.isNil {
    // do something
}

if myValue.isSome {
    // do something
}

It's probably the swift analog of the pythonistas that try to keep the language in some form of pythonic purity . My take? I just lifted your code into my Utils mixin.
B
Ben Lachman

One option that hasn't specifically been covered is using Swift's ignored value syntax:

if let _ = xyz {
    // something that should only happen if xyz is not nil
}

I like this since checking for nil feels out of place in a modern language like Swift. I think the reason it feels out of place is that nil is basically a sentinel value. We've done away with sentinels pretty much everywhere else in modern programming so nil feels like it should go too.


q
qed

Instead of if, ternary operator might come handy when you want to get a value based on whether something is nil:

func f(x: String?) -> String {
    return x == nil ? "empty" : "non-empty"
}

xyz == nil expression doesn't work, but x != nil works. Don't know why.
T
Tiago

Another approach besides using if or guard statements to do the optional binding is to extend Optional with:

extension Optional {

    func ifValue(_ valueHandler: (Wrapped) -> Void) {
        switch self {
        case .some(let wrapped): valueHandler(wrapped)
        default: break
        }
    }

}

ifValue receives a closure and calls it with the value as an argument when the optional is not nil. It is used this way:

var helloString: String? = "Hello, World!"

helloString.ifValue {
    print($0) // prints "Hello, World!"
}

helloString = nil

helloString.ifValue {
    print($0) // This code never runs
}

You should probably use an if or guard however as those are the most conventional (thus familiar) approaches used by Swift programmers.


y
yoAlex5

Optional

Also you can use Nil-Coalescing Operator

The nil-coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a.

let value = optionalValue ?? defaultValue

If optionalValue is nil, it automatically assigns value to defaultValue


I like that, because it gives the easy option to specify a default value.
N
Nicolas Manzini

Now you can do in swift the following thing which allows you to regain a little bit of the objective-c if nil else

if textfieldDate.text?.isEmpty ?? true {

}

M
Mundi
var xyz : NSDictionary?

// case 1:
xyz = ["1":"one"]
// case 2: (empty dictionary)
xyz = NSDictionary() 
// case 3: do nothing

if xyz { NSLog("xyz is not nil.") }
else   { NSLog("xyz is nil.")     }

This test worked as expected in all cases. BTW, you do not need the brackets ().


The case the OP is concerned about is: if (xyz != nil).
R
RT Denver

If you have conditional and would like to unwrap and compare, how about taking advantage of the short-circuit evaluation of compound boolean expression as in

if xyz != nil && xyz! == "some non-nil value" {

}

Granted, this is not as readable as some of the other suggested posts, but gets the job done and somewhat succinct than the other suggested solutions.


T
Tob

If someone is also try to find to work with dictionaries and try to work with Optional(nil).

 let example : [Int:Double?] = [2: 0.5]
 let test = example[0]

You will end up with the type Double??.

To continue on your code, just use coalescing to get around it.

 let example : [Int:Double?] = [2: 0.5]
 let test = example[0] ?? nil

Now you just have Double?

This is totally logical, but I searched the wrong thing, maybe it helps someone else.