There is an updated edition of this book available! View Latest Edition
Home iOS & Swift Books Swift Apprentice

6
Optionals Written by Matt Galloway

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

All the variables and constants you’ve dealt with so far have had concrete values. When you had a string variable, like var name, it had a string value associated with it, like "Matt Galloway". It could have been an empty string, like "", but nevertheless, there was a value to which you could refer.

That’s one of the built-in safety features of Swift: If the type says Int or String, then there’s an actual integer or string there, guaranteed.

This chapter will introduce you to the concept of optionals, a special Swift type that can represent not just a value, but also the absence of a value. By the end of this chapter, you’ll know why you need optionals and how to use them safely.

Introducing nil

Sometimes, it’s useful to represent the absence of a value. Imagine a scenario where you need to refer to a person’s identifying information; you want to store the person’s name, age and occupation. Name and age are both things that must have a value — everyone has them. But not everyone is employed, so the absence of a value for occupation is something you need to be able to handle.

Without knowing about optionals, this is how you might represent the person’s name, age and occupation:

var name = "Matt Galloway"
var age = 30
var occupation = "Software Developer & Author"

But what if I become unemployed? Maybe I’ve won the lottery and want to give up work altogether (I wish!). This is when it would be useful to be able to refer to the absence of a value.

Why couldn’t you just use an empty string? You could, but optionals are a much better solution. Read on to see why.

Sentinel values

A value that represents a special condition such as the absence of a value is known as a sentinel value, or simply, special value. That’s what your empty string would be in the previous example.

var errorCode = 0

Introducing optionals

Optionals are Swift’s solution to the problem of representing both a value and the absence of a value. An optional is allowed to hold either a value or nil.

var errorCode: Int?
errorCode = 100
errorCode = nil

Mini-exercises

  1. Make an optional String called myFavoriteSong. If you have a favorite song, set it to a string representing that song. If you have more than one favorite song or no favorite, set the optional to nil.
  2. Create a constant called parsedInt and set it equal to Int("10") which tries to parse the string 10 and convert it to an Int. Check the type of parsedInt using Option-Click. Why is it an optional?
  3. Change the string being parsed in the above exercise to a non-integer (try dog for example). What does parsedInt equal now?

Unwrapping optionals

It’s all well and good that optionals exist, but you may be wondering how you can look inside the box and manipulate the value it contains.

var result: Int? = 30
print(result)
print(result + 1)

Force unwrapping

The error message gives an indication of the solution: It tells you that the optional must be unwrapped. You need to unwrap the value from its box. It’s like Christmas!

var authorName: String? = "Matt Galloway"
var authorAge: Int? = 30
var unwrappedAuthorName = authorName!
print("Author is \(unwrappedAuthorName)")
authorName = nil
print("Author is \(authorName!)")
if authorName != nil {
  print("Author is \(authorName!)")
} else {
  print("No author.")
}

Optional binding

Swift includes a feature known as optional binding, which lets you safely access the value inside an optional. You use it like so:

if let unwrappedAuthorName = authorName {
  print("Author is \(unwrappedAuthorName)")
} else {
  print("No author.")
}
if let authorName = authorName {
  print("Author is \(authorName)")
} else {
  print("No author.")
}
if let authorName = authorName,
   let authorAge = authorAge {
  print("The author is \(authorName) who is \(authorAge) years old.")
} else {
  print("No author or no age.")
}
if let authorName = authorName,
   let authorAge = authorAge,
   authorAge >= 40 {
  print("The author is \(authorName) who is \(authorAge) years old.")
} else {
  print("No author or no age or age less than 40.")
}

Mini-exercises

  1. Using your myFavoriteSong variable from earlier, use optional binding to check if it contains a value. If it does, print out the value. If it doesn’t, print "I don’t have a favorite song."
  2. Change myFavoriteSong to the opposite of what it is now. If it’s nil, set it to a string; if it’s a string, set it to nil. Observe how your printed result changes.

Introducing guard

Sometimes you want to check a condition and only continue executing a function if the condition is true, such as when you use optionals. Imagine a function that fetches some data from the network. That fetch might fail if the network is down. The usual way to encapsulate this behavior is using an optional, which has a value if the fetch succeeds, and nil otherwise.

func guardMyCastle(name: String?) {
  guard let castleName = name else {
    print("No castle!")
    return 
  }
  
  // At this point, `castleName` is a non-optional String
  
  print("Your castle called \(castleName) was guarded!")
}
func calculateNumberOfSides(shape: String) -> Int? {
  switch shape {
  case "Triangle":
    return 3
  case "Square":
    return 4
  case "Rectangle":
    return 4
  case "Pentagon":
    return 5
  case "Hexagon":
    return 6
  default:
    return nil
  }
}
func maybePrintSides(shape: String) {
  let sides = calculateNumberOfSides(shape: shape)

  if let sides = sides {
    print("A \(shape) has \(sides) sides.")
  } else {
    print("I don’t know the number of sides for \(shape).")
  }
}
func maybePrintSides(shape: String) {
  guard let sides = calculateNumberOfSides(shape: shape) else {
    print("I don’t know the number of sides for \(shape).")
    return
  }

  print("A \(shape) has \(sides) sides.")
}

Nil coalescing

There’s a rather handy alternative way to unwrap an optional. You use it when you want to get a value out of the optional no matter what — and in the case of nil, you’ll use a default value. This is called nil coalescing. Here’s how it works:

var optionalInt: Int? = 10
var mustHaveResult = optionalInt ?? 0
var optionalInt: Int? = 10
var mustHaveResult: Int
if let unwrapped = optionalInt {
  mustHaveResult = unwrapped
} else {
  mustHaveResult = 0
}
optionalInt = nil
mustHaveResult = optionalInt ?? 0

Challenges

Before moving on, here are some challenges to test your knowledge of optionals. It is best if you try to solve them yourself, but solutions are available if you get stuck. These came with the download or are available at the printed book’s source code link listed in the introduction.

Challenge 1: You be the compiler

Which of the following are valid statements?

var name: String? = "Ray"
var age: Int = nil
let distance: Float = 26.7
var middleName: String? = nil

Challenge 2: Divide and conquer

First, create a function that returns the number of times an integer can be divided by another integer without a remainder. The function should return nil if the division doesn’t produce a whole number. Name the function divideIfWhole.

func divideIfWhole(_ value: Int, by divisor: Int)

Challenge 3: Refactor and reduce

The code you wrote in the last challenge used if statements. In this challenge, refactor that code to use nil coalescing instead. This time, make it print "It divides X times" in all cases, but if the division doesn’t result in a whole number, then X should be 0.

Challenge 4: Nested optionals

Consider the following nested optional — it corresponds to a number inside a box inside a box inside a box.

let number: Int??? = 10
print(number)
// Optional(Optional(Optional(10)))

print(number!)
// Optional(Optional(10))

Key points

  • nil represents the absence of a value.
  • Non-optional variables and constants are never nil.
  • Optional variables and constants are like boxes that can contain a value or be empty (nil).
  • To work with the value inside an optional, you must first unwrap it from the optional.
  • The safest ways to unwrap an optional’s value is by using optional binding or nil coalescing. Use forced unwrapping only when appropriate, as it could produce a runtime error.
  • You can guard let to bind an optional. If the binding fails, the compiler forces you to exit the current function (or halt execution). This guarantees that your program never execute with uninitialized value.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

© 2022 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.