172

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?

tng
  • 4,286
  • 5
  • 21
  • 30

16 Answers16

207

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`.
}
paulmelnikow
  • 16,895
  • 8
  • 63
  • 114
ktzhang
  • 4,069
  • 2
  • 19
  • 18
  • 9
    Can someone elaborate on why xyz == nil is not working? – Christoph May 20 '16 at 15:01
  • The second example should read: // Do something using xy (which is the main difference in use cases between the two variants) – Alper Aug 29 '17 at 11:22
  • @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. – user3207158 Aug 23 '18 at 17:39
  • 1
    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 Jun 10 '19 at 01:05
  • @Omar That's the beauty of the second form, you use xy inside the block without having to unwrap it. – Erik Doernenburg Nov 27 '19 at 07:54
33

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.

Flimm
  • 136,138
  • 45
  • 251
  • 267
zevij
  • 2,416
  • 1
  • 23
  • 32
22

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
Fangming
  • 24,551
  • 6
  • 100
  • 90
  • Man, Swift does have some unique, funky syntax for sho! Good summary though, thanks! – Timo Ernst May 10 '23 at 14:38
  • @TimoErnst If you like these syntax, Golang has similar stuff to calculate and check for conditions in one line. It's also more general and can be used in many places (not mobile of course) and runs very efficiently in general. – Fangming May 22 '23 at 19:43
20

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.

Isaac Drachman
  • 984
  • 6
  • 8
  • 1
    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. – Lombas Jul 19 '16 at 17:27
  • 3
    It's the standard method and recommended method to unwrap an optional variable. "'if let " is seriously powerful. – gnasher729 Apr 11 '17 at 06:16
  • @gnasher729 but what if you only want to use the else part – Bradley Thomas Feb 22 '19 at 12:09
15

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.

Iulian Onofrei
  • 9,188
  • 10
  • 67
  • 113
codester
  • 36,891
  • 10
  • 74
  • 72
  • 2
    In Swift 3 you have to do `if xyz != nil {...}`. – psmythirl Aug 10 '16 at 23:59
  • 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. – gnasher729 Apr 11 '17 at 06:17
9

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
Chris Frederick
  • 5,482
  • 3
  • 36
  • 44
  • 1
    as mentioned above, you can do this too to get rid of nested optional statmenets. if let abc = abc?.xyz?.something { } – Suhaib Sep 23 '16 at 00:18
  • 1
    @Suhaib Ah, true: You can also use optional chaining (https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html) 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. – Chris Frederick Sep 24 '16 at 01:42
  • @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. :) – Chris Frederick Apr 11 '19 at 13:46
9

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
}
Brody Robertson
  • 8,506
  • 2
  • 47
  • 42
  • 2
    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. – WestCoastProjects Jun 17 '20 at 03:48
7

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.

Ben Lachman
  • 3,083
  • 1
  • 28
  • 44
6

Swift Optional

Except if let, guard let constructions 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

yoAlex5
  • 29,217
  • 8
  • 193
  • 205
4

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"
}
qed
  • 22,298
  • 21
  • 125
  • 196
4

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.

Tiago
  • 3,113
  • 2
  • 31
  • 45
2

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 {

}
Nicolas Manzini
  • 8,379
  • 6
  • 63
  • 81
2

Since Swift 5.7:

if let xyz {
    // Do something using `xyz` (`xyz` is not optional here) 
} else {
    // `xyz` was nil
}
SuperGlenn
  • 572
  • 6
  • 9
0
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 ().

Mundi
  • 79,884
  • 17
  • 117
  • 140
0

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.

RT Denver
  • 51
  • 3
0

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.

Tob
  • 627
  • 6
  • 9