When should I compare an optional value to nil?
It is almost always unnecessary to check if an optional is not nil
. Pretty much the only time you need to do this is if its nil
-ness is the only thing you want to know about – you don’t care what’s in the value, just that it’s not nil
.
Under most other circumstances, there is a bit of Swift shorthand that can more safely and concisely do the task inside the if
for you.
Using the value if it isn’t nil
Instead of:
let s = "1"
let i = Int(s)
if i != nil {
print(i! + 1)
}
you can use if let
:
if let i = Int(s) {
print(i + 1)
}
You can also use var
:
if var i = Int(s) {
print(++i) // prints 2
}
but note that i
will be a local copy - any changes to i
will not affect the value inside the original optional.
You can unwrap multiple optionals within a single if let
, and later ones can depend on earlier ones:
if let url = NSURL(string: urlString),
data = NSData(contentsOfURL: url),
image = UIImage(data: data)
{
let view = UIImageView(image: image)
// etc.
}
You can also add where
clauses to the unwrapped values:
if let url = NSURL(string: urlString) where url.pathExtension == "png",
let data = NSData(contentsOfURL: url), image = UIImage(data: data)
{ etc. }
Replacing nil
with a default
Instead of:
let j: Int
if i != nil {
j = i
}
else {
j = 0
}
or:
let j = i != nil ? i! : 0
you can use the nil-coalescing operator, ??
:
// j will be the unwrapped value of i,
// or 0 if i is nil
let j = i ?? 0
Equating an optional with a non-optional
Instead of:
if i != nil && i! == 2 {
print("i is two and not nil")
}
you can check if optionals are equal to non-optional values:
if i == 2 {
print("i is two and not nil")
}
This also works with comparisons:
if i < 5 { }
nil
is always equal to other nil
s, and is less than any non-nil
value.
Be careful! There can be gotchas here:
let a: Any = "hello"
let b: Any = "goodbye"
if (a as? Double) == (b as? Double) {
print("these will be equal because both nil...")
}
Calling a method (or reading a property) on an optional
Instead of:
let j: Int
if i != nil {
j = i.successor()
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
you can use optional chaining, ?.
:
let j = i?.successor()
Note, j
will also now be optional, to account for the fatalError
scenario. Later, you can use one of the other techniques in this answer to handle j
’s optionality, but you can often defer actually unwrapping your optionals until much later, or sometimes not at all.
As the name implies, you can chain them, so you can write:
let j = s.toInt()?.successor()?.successor()
Optional chaining also works with subscripts:
let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]]
let sevenOfNine = dictOfArrays["nine"]?[7] // returns {Some 7}
and functions:
let dictOfFuncs: [String:(Int,Int)->Int] = [
"add":(+),
"subtract":(-)
]
dictOfFuncs["add"]?(1,1) // returns {Some 2}
Assigning to a property on an optional
Instead of:
if splitViewController != nil {
splitViewController!.delegate = self
}
you can assign through an optional chain:
splitViewController?.delegate = self
Only if splitViewController
is non-nil
will the assignment happen.
Using the value if it isn’t nil
, or bailing (new in Swift 2.0)
Sometimes in a function, there’s a short bit of code you want to write to check an optional, and if it’s nil
, exit the function early, otherwise keep going.
You might write this like this:
func f(s: String) {
let i = Int(s)
if i == nil { fatalError("Input must be a number") }
print(i! + 1)
}
or to avoid the force unwrap, like this:
func f(s: String) {
if let i = Int(s) {
print(i! + 1)
}
else {
fatalErrr("Input must be a number")
}
}
but it’s much nicer to keep the error-handling code at the top by the check. This can also lead to unpleasant nesting (the "pyramid of doom").
Instead you can use guard
, which is like an if not let
:
func f(s: String) {
guard let i = Int(s)
else { fatalError("Input must be a number") }
// i will be an non-optional Int
print(i+1)
}
The else
part must exit the scope of the guarded value, e.g. a return
or fatalError
, to guarantee that the guarded value will be valid for the remainder of the scope.
guard
isn’t limited to function scope. For example the following:
var a = ["0","1","foo","2"]
while !a.isEmpty {
guard let i = Int(a.removeLast())
else { continue }
print(i+1, appendNewline: false)
}
prints 321
.
Looping over non-nil items in a sequence (new in Swift 2.0)
If you have a sequence of optionals, you can use for case let _?
to iterate over all the non-optional elements:
let a = ["0","1","foo","2"]
for case let i? in a.map({ Int($0)}) {
print(i+1, appendNewline: false)
}
prints 321
. This is using the pattern-matching syntax for an optional, which is a variable name followed by ?
.
You can also use this pattern matching in switch
statements:
func add(i: Int?, _ j: Int?) -> Int? {
switch (i,j) {
case (nil,nil), (_?,nil), (nil,_?):
return nil
case let (x?,y?):
return x + y
}
}
add(1,2) // 3
add(nil, 1) // nil
Looping until a function returns nil
Much like if let
, you can also write while let
and loop until nil
:
while let line = readLine() {
print(line)
}
You can also write while var
(similar caveats to if var
apply).
where
clauses also work here (and terminate the loop, rather than skipping):
while let line = readLine()
where !line.isEmpty {
print(line)
}
Passing an optional into a function that takes a non-optional and returns a result
Instead of:
let j: Int
if i != nil {
j = abs(i!)
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
you can use optional’s map
operator:
let j = i.map { abs($0) }
This is very similar to optional chaining, but for when you need to pass the non-optional value into the function as an argument. As with optional chaining, the result will be optional.
This is nice when you want an optional anyway. For example, reduce1
is like reduce
, but uses the first value as the seed, returning an optional in case the array is empty. You might write it like this (using the guard
keyword from earlier):
extension Array {
func reduce1(combine: (T,T)->T)->T? {
guard let head = self.first
else { return nil }
return dropFirst(self).reduce(head, combine: combine)
}
}
[1,2,3].reduce1(+) // returns 6
But instead you could map
the .first
property, and return that:
extension Array {
func reduce1(combine: (T,T)->T)->T? {
return self.first.map {
dropFirst(self).reduce($0, combine: combine)
}
}
}
Passing an optional into a function that takes an optional and returns a result, avoiding annoying double-optionals
Sometimes, you want something similar to map
, but the function you want to call itself returns an optional. For example:
// an array of arrays
let arr = [[1,2,3],[4,5,6]]
// .first returns an optional of the first element of the array
// (optional because the array could be empty, in which case it's nil)
let fst = arr.first // fst is now [Int]?, an optional array of ints
// now, if we want to find the index of the value 2, we could use map and find
let idx = fst.map { find($0, 2) }
But now idx
is of type Int??
, a double-optional. Instead, you can use flatMap
, which “flattens” the result into a single optional:
let idx = fst.flatMap { find($0, 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used
I am getting a warning comparing my OPTIONAL value to not-nil will always return true
Just remove the entry != nil
clause and it will work as you require. The if let
statement that proceeds it performs the not-nil check already.
guard let entry = entryForEdit else {
return
}
Confused about optional type in swift (Nil can compare to Int)
(Edit update: after additional question added by OP)
I'll add an answer to you question
"Does it make sense "The nil can compare to Int" in Swift?"
You can consider Optional
a type just like Int
or double, Double
, with a (simplified) enum implementation
enum Optional<T> {
case None
case Some(T)
init(_ value: T) {
self = .Some(value)
}
init() {
self = .None
}
}
The generic T
in the Optional
type is never used for case .None
, which, in this discussion, would be nil
. Hence, any type that is also optional (e.g. Int?
, String?
and so on) can be compared to nil
. For the sake of this discussion, you could almost think of nil
as a single literal that can be used to initialize or give value to any type that is defined as optional. With this latter statement, it's obvious that we can also compare the values of our optional type to nil
.
Hence: yes, we can compare the value of any optional variable to nil
, but there are usually better options, e.g. optional chaining or the nil coalescing operator (??
).
(From before edit, still relevant)
I'll add an example to help to show you what is going on in loops constructed as if a.b?.c != d { ...
.
Consider the following structures:
struct MyStruct {
var myRow : Int
init (row: Int) {
myRow = row
}
}
struct MyTopStruct {
var myStruct : MyStruct? = nil
mutating func setValueToMyStruct(row: Int) {
myStruct = MyStruct(row: row)
}
}
Let a
be an instance of MyTopStruct
, and lets have some integer and optional integer properties in our example:
var a = MyTopStruct()
let myInt = 10
var myOptionalInt : Int? = nil
Then, we have a few different outcomes for if a.b?.c != d { ...
clauses, depending on whether optionals in this example are nil
(as initialized) or not.
/* "a.myStruct?" returns nil, and this clause will hence
compare "nil != myInt"?, which will always be true, since
myInt is a non-optional integer */
if a.myStruct?.myRow != myInt {
print("Entered 1") // prints
}
/* "a.myStruct?" still nil, and myOptionalInt not yet initialised
so also nil. Hence, "nil != nil"? => false */
if a.myStruct?.myRow != myOptionalInt {
print("Entered 2") // doesnt print
}
/* now initialise a.myStruct */
a.setValueToMyStruct(myInt)
/* "a.myStruct?" now returs value of myInt (10), which != nil,
the latter being the current value of myOptionalInt
=> always enters */
if a.myStruct?.myRow != myOptionalInt {
print("Entered 3") // prints
}
/* finally initialize myOptionalInt */
myOptionalInt = 9
/* Now this is the first comparison that can actually behave
in "different ways" w.r.t. how we usually think of if clauses:
comparing _values_ of same-type objects.
Hence, comparison now depends on the two non-nil values of
a.myStruct?.myRow and myOptionalInt */
if a.myStruct?.myRow != myOptionalInt {
print("Entered 4") // prints, since 10 != 9
}
Comparing non optional value leads to error
It sounds like you are confusing yourself between the local variable snusPortions
and the Product property snusPortions
.
In your Product definition, the property snusPortions
is an Int. It can never be nil
. Hence, in this code:
if products[indexPath.row].snusPortions == nil
... this Product's snusPortions
will never be nil
, and we will never set the text to "N/A".
Now let's look at your other code:
let snusPortions = thisProductDict["PortionsCan"] as? Int
This is a completely different snusPortions
. It can be nil
, namely, if thisProductDict
lacks a "PortionsCan"
key or if its value is not castable to Int.
Related Topics
Iphone 6 Plus Resolution Confusion: Xcode or Apple'S Website? For Development
How to Change the Background Color of a Uibutton While It's Highlighted
Set Padding For Uitextfield With Uitextborderstylenone
How to Record a Conversation/Phone Call on Ios
How to Download Multiple Files Sequentially Using Nsurlsession Downloadtask in Swift
Passing Data Between View Controllers
Xcode Error "Could Not Find Developer Disk Image"
First and Last Day of the Current Month in Swift
How to Programmatically Get the MAC Address of an Iphone
How to Create Dispatch Queue in Swift 3
Submit to App Store Issues: Unsupported Architecture X86
Swift Compiler Error: "Expression Too Complex" on a String Concatenation
How to Convert Uiview to Pdf Within Ios
What Is Silent Push Notification? When Does the Device Receive It
How to Deploy an Iphone Application from Xcode to a Real Iphone Device