Why would I use if and let together, instead of just checking if the original variable is nil? (Swift)
Because it also unwraps the optional value, so this code:
if let name = optionalName {
greeting = "Hello, \(name)"
}
is equivalent to:
if optionalName != nil {
let name:String = optionalName!
greeting = "Hello, \(name)"
}
This language sugar is known as Optional Binding in Swift.
Optional Types
In Swift T
and T?
are not the same types, but the underlying value of an optional T?
type can easily be realized by using the !
postfix operator, e.g:
let name:String = optionalName!
Which now can be used where a String
is expected, e.g:
func greet(name:String) -> String {
return "Hello, \(name)"
}
greet(name)
Although as its safe to do so, Swift does let you implicitly cast to an optional type:
let name = "World"
let optionalName: String? = name
func greet(optionalName:String?) -> String? {
if optionalName != nil {
return "Hello, \(optionalName)"
}
return nil
}
//Can call with either String or String?
greet(optionalName)
greet(name)
Check for nil with guard instead of if?
Like some people already answered, you can use let
guard let preview = post["preview"] else { /* Handle nil case */ return }
If you are not using the variable, you can use an underscore to not declare the variable and avoid the warning.
guard let _ = post["preview"] else { /* Handle nil case */ return }
You can also do a regular boolean check instead of using let
guard post["preview"] != nil else { /* Handle nil case */ return }
A more general case for a boolean check on a guard
guard conditionYouExpectToBeTrue else { /* Handle nil case */ return }
If you want to be able to modify the variable, you can use var
instead of let
guard var preview = post["preview"] else { /* Handle nil case */ return }
Swift 3.0
You can combine var/let
with a boolean check by using commas between the statements.
guard let preview = post["preview"], preview != "No Preview" else { /* Handle nil case */ return }
Swift 2.x
You can combine var/let
with the boolean check by using where where
guard let preview = post["preview"] where preview != "No Preview" else { /* Handle nil case */ return }
Why is Apple using this if let code?
If you use
let existingImage = cache.objectForKey(documentIdentifier) as? UIImage
if let existingImage = existingImage where cleanThumbnailDocumentIDs.contains(documentIdentifier) {
return existingImage
}
- This will make sure that if
existingImage == nil
,it will not
executereturn existingImage
. - Besides,
if let
also unwrapexistingImage
fromUIImage?
toUIImage
How is Swift `if let` evaluated?
Essentially the line is saying, "if you can let the new variable name
equal the non-optional version of optionalName
, do the following with it". As Martin pointed out, this is called Optional Binding.
The sole purpose of it is to test if an optional variable contains an actual value and bind the non-optional form to a temporary variable. This is the safe way to "unwrap" an optional or in other words, access the value contained in the optional. It is in no way testing for equality of any kind. It is only testing for the existence of a value within an optional.
Swift if value is nil set default value instead
you can do like this but your strValue should be optional type
let strValue:String?
textfield.stringValue = strValue ?? "your default value here"
Swift: guard let vs if let
if let
and guard let
serve similar, but distinct purposes.
The "else" case of guard
must exit the current scope. Generally that means it must call return
or abort the program. guard
is used to provide early return without requiring nesting of the rest of the function.
if let
nests its scope, and does not require anything special of it. It can return
or not.
In general, if the if-let
block was going to be the rest of the function, or its else
clause would have a return
or abort in it, then you should be using guard
instead. This often means (at least in my experience), when in doubt, guard
is usually the better answer. But there are plenty of situations where if let
still is appropriate.
Multiple if let and OR condition in swift
if let value1 = profile.value1, value1 != “” {}
else if value2 = profile.value2, value2 != “” {}
else if value3 = profile.value3, value3 != “” {}
here ,
acts like &&
Pay attention:
For Swift 2 replace ,
with where
Using the Swift if let with logical AND operator &&
As of Swift 1.2, this is now possible. The Swift 1.2 and Xcode 6.3 beta release notes state:
More powerful optional unwrapping with if let — The if let construct
can now unwrap multiple optionals at once, as well as include
intervening boolean conditions. This lets you express conditional
control flow without unnecessary nesting.
With the statement above, the syntax would then be:
if let tabBarController = window!.rootViewController as? UITabBarController where tabBarController.viewControllers.count > 0 {
println("do stuff")
}
This uses the where
clause.
Another example, this time casting AnyObject
to Int
, unwrapping the optional, and checking that the unwrapped optional meets the condition:
if let w = width as? Int where w < 500
{
println("success!")
}
For those now using Swift 3, "where" has been replaced by a comma. The equivalent would therefore be:
if let w = width as? Int, w < 500
{
println("success!")
}
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
Related Topics
How to Set Exit Code Value for a Command Line Utility in Swift
Is There a Daylight Savings Check in Swift
How to Rotate Sprites Around a Joint
How to Encode an Unmanaged<Seckey> to Base64 to Send to Another Server
How to Access Firebase Variable Outside Firebase Function
How to Say "If X == a or B or C" as Succinctly in Swift as Possible
How to Cast an Any Value with Nil in It to a Any
Swift 3 Optional Trouble. Can't Unwrap Url with Passed in String
How to Find Max Value for Double and Float in Swift
Codable Class Does Not Conform to Protocol Decodable
How to Get Iobluetoothdevice's Battery Level, Using Swift and Appkit (Xcode for MACos)
How to Make a Extension for Array of Specific Type in Swift
Swift: Double Conversion Inconsistency. How to Correctly Compare Doubles
Executing Text-To-Speech in Order
How to Disable a Constraint Programmatically
What's the Best Way to Iterate Over Results from an API, and Know When It's Finished