Subscribe now

Basic Flow-Control [11.09.2016]

if

Type this into a fresh playground:

var a:Int = 0
if a == 0 {
  print("a == 0")
}

We'll find a == 0 show up in the debug area.

Although Swift uses keywords, like if that are similar to C-based languages, you'll notice 2 immediate differences: we don't need to place parentheses around the expression being evaluated, and we do have to put curly brackets around the code to be executed, even if it's only one line.

Let's try a second if that you might expect from another language.

if a {
  print("a != 0")
}

The compiler won't let us do that. Swift if only accepts an expression which resolves to a Bool. A Swift Bool is not secretly an Int, so we can't substitute an Int in here.

We can, however, use lots of logical operators you might expect from another language.

var a:Int = 7
if a == 0 && a <= 8 {
  print("a == 0")
}

Here, I've used && and <= to build a larger logical expression. Technically, you can do whatever you want in there, as long as it resolves to a Bool. Some other common logical operators are || for or, and a prefixed ! for not.

if a >= 0 && a <= 8 || a >= 14 {
  print("a is ok")
}

I can also group those operations using parentheses.

if (a >= 0 && a <= 8) || (a >= 14 && a < 21) {
  print("a is ok")
}

Built-in comparison operators includes these, and also >=, and !=.

However, I've noticed that the Swift compiler has a limit for how complex these expressions can actually be. In order to relieve the need of predeclaring functions and types, Swift actually parses the syntax long before it figures out what your types are and whether they can actually participate the functions you're calling. To prevent run-away compile times, the compiler developers put an upper limit on how complex these expressions are, and I've hit it inside an if every now and then. So I've developed a habit of breaking my conditionals into a separate variable.

var a:Int = 0
var aIsOk:Bool = (a >= 0 && a <= 8) || (a >= 14 && a < 21)
if aIsOk {
  print("a is ok")
}

And now we don't have to worry about how complex it is.

else

Let's add an else statement.

if a == 0 {
  print("a == 0")
} else {
  print("a != 0")
}

Then change the value of a

var a:Int = 3  // a != 0

Swift else works exactly like C else, including when we want to make an else if.

if a == 0 {
  print("a == 0")
} else if a < 3 {
  print("a < 3 and != 0")
} else {
  print("a > 3")
}

var a:Int = 2  //  a < 3 and != 0

So aside from the requirements about parentheses, curly braces and types, Swift if and else are very similar to C if and else.

Unwrapping Optionals

I want to show you something that doesn't work.

var aValue:Int? = 5
let someValue:Int = aValue

You know what var is; a keyword which introduces a variable. let is similar, but it introduces a constant. Once its value is assigned, it cannot be changed. This code tries to initialize the value of an Int with an Int?. That's not allowed, because there might not be an Int inside the Optional. In order to determine if there is or not, we must unwrap the Optional. We can do that with if. Here's an example:

var aValueOrNil:Int? = 5
if let theValue:Int = aValueOrNil {
  print("aValueOrNil == \(theValue)")
}
// aValueOrNil == 5

We've created an Optional Int, and set its value to some Int. We've then written an if statement. The expression evaluated by the if statement is our let definition and initialization.

By placing the let inside the if expression, Swift will determine whether the aValueOrNil was nil. If it's not nil, theValue will be populated, and execute the code that uses it in the if block.

We can add an else block to this as well,

var aValueOrNil:Int? = nil
if let theValue:Int = aValueOrNil {
  print("aValueOrNil == \(theValue)")
} else {
  print("aValueOrNil is nil")
}
// aValueOrNil is nil

So our code in the else block only gets executed if aValueOrNil is nil.

Here's something that will feel like a gotcha the first time you try it.

var username:String?

if username.isEmpty {
  print("user name is not acceptible")
} else {
  print("username is acceptible")
}

This code focusses on determining if the name the user has provided is even worth being sent up to a server. If the user hasn't provided a value, it's nil. But it's possible that the user input a zero-length name, and thus the username .isEmpty.

But this doesn't compile, because we can't call isEmpty on an Optional, it's not one of its properties, it's property of a String. We have to unwrap the Optional String. I know what you're thinking, unwrap the user name with an if let then check it. But there's a shorter way. swift if username?.isEmpty { print("user name is not acceptible") } else { print("username is acceptible") }

Using the ? post-fix operator unwraps the optional and calls .isEmpty only if it is non-nil. We're on the right path, but it may not feel like it, since the compiler won't allow the Bool? as an expression. It needs a Bool. By using the chaining operator, we've determined that we might not get to call .isEmpty when the ? operator returns nil. So while .isEmpty returns a Bool, calling it on an optional returns a Bool?.

So here's the solution: The == operator not only compares two Equatable things, it will also check an Optional thing for equality with a thing.

if username?.isEmpty == false {
print("user name is acceptible")
} else {
  print("username is not acceptible")
}

If the optional term is non-optional and matches the other operand, it will return true, which is a Bool. This is called optional chaining. You can build up long chains of these, but I recommend you don't, since it violates the Law of Demeter. (Look it up on your own time.) However, doing so with Swift is much safer than doing so in Obj-C.

In Obj-C, a call to nil would return nil as long as the return type was pointer-sized or smaller. That's because the functionality actually relied on leaving the nil pointer in place for the return value. When a method wasn't executed because there was no object in which to call it, the return value would come back all 0's. But in Swift, the succeeding methods or properties are never actually even called.

Summary

Today we covered the ins and outs of if, and else, and how these statements differ from other C-based languages.

We also learned the basics of unwrapping an optional using if let.

Conditional code execution depending on whether a value is nil or not; now that's Swift!