Swift: Testing optionals for nil

IosSwiftOptional

Ios Problem Overview


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?

Ios Solutions


Solution 1 - Ios

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`.
}

Solution 2 - Ios

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.

Solution 3 - Ios

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.

Solution 4 - Ios

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

Solution 5 - Ios

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.

Solution 6 - Ios

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

Solution 7 - Ios

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
}

Solution 8 - Ios

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.

Solution 9 - Ios

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"
}

Solution 10 - Ios

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.

Solution 11 - Ios

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

Solution 12 - Ios

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 {
    
}

Solution 13 - Ios

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 ().

Solution 14 - Ios

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontngView Question on Stackoverflow
Solution 1 - IosktzhangView Answer on Stackoverflow
Solution 2 - IoszevijView Answer on Stackoverflow
Solution 3 - IosIsaac DrachmanView Answer on Stackoverflow
Solution 4 - IosFangmingView Answer on Stackoverflow
Solution 5 - IoscodesterView Answer on Stackoverflow
Solution 6 - IosChris FrederickView Answer on Stackoverflow
Solution 7 - IosBrody RobertsonView Answer on Stackoverflow
Solution 8 - IosBen LachmanView Answer on Stackoverflow
Solution 9 - IosqedView Answer on Stackoverflow
Solution 10 - IosTiagoView Answer on Stackoverflow
Solution 11 - IosyoAlex5View Answer on Stackoverflow
Solution 12 - IosNicolas ManziniView Answer on Stackoverflow
Solution 13 - IosMundiView Answer on Stackoverflow
Solution 14 - IosRT DenverView Answer on Stackoverflow