Skip to content

onemonth/iOS-Blog-Posts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 

Repository files navigation

##Swift Optionals

Optionals are a powerful feature of the Swift programming language, but they can be difficult to understand and use effectively. Let's review the essentials.

###At a high level, what are Optionals?

Let's say a mutual friend is throwing a Halloween party. The invitation outlines the pertinent details (e.g. time and location) and among other things it says, "bring a beverage, costumes optional." Now if we wanted to represent this party and its guests in code, we might create a Party object and a Guest object, and we might translate the above details into properties on these classes. However, in order to accurately represent this Halloween party we want to express the fact that costumes are optional. We do this like so:

class Party
{
    var time: NSDate
    var location: CGPoint
    var guests: Array<Guest>
    
    // ...
}

class Guest 
{
    var beverage: String // Each guest must have a beverage
    var costume: String? // Each guest can choose to come in costume or plain clothes (i.e. costumes optional)
    
    // ...
}

In Swift, the ? is how we designate a variable as Optional. And this just means that it's okay for the variable to have a value (costume = "Frankenstein") or to not have a value (costume = nil). In contrast, every guest must have a beverage, this is expressed by the absence of a ?, and the compiler will enforce this for us.

###A little background

Optionals work in tandem with variables. A variable is a construct that we use to hold onto a piece of data. At a minimum we usually give a variable a name, type and value.

// Here we explicitly specify the type
var beverage: String = "Leffe"

// But the type can also be inferred from the fact that we're assigning it a String value
var name = "Leffe"

####vars and lets

Swift offers us two distinctly different kinds of variables: vars and lets. In order to understand Optionals we must first understand vars and lets.

The primary distinction between vars and lets is that vars are mutable and lets are immutable. In other words, we can change the value of a var as many times as we want, but once we set the value of a let, we can never change it. Let's look at some examples.

// vars

var mutableBeverage = "Leffe"
println(mutableBeverage) // "Leffe"

mutableBeverage = "Bud"
println(mutableBeverage) // "Bud"

mutableBeverage = mutableBeverage + "weiser"
println(mutableBeverage) // "Budweiser"

// lets

let immutableBeverage = "Bud"
println(immutableBeverage) // "Bud"

immutableBeverage = "Leffe" // Compiler error

immutableBeverage = immutableBeverage + "weiser" // Compiler error

Test these out in a Swift Playground and you'll see that the compiler enforces these mutability rules for us. And in doing so it forces us to be explicit about our intentions. If we used vars alone we'd be wandering into a lawless land. Mad Max territory. Variables that we intend to be immutable might unintentionally be mutated. But in this day and age we can elect to use vars and lets where appropriate, and proceed with confidence.

####Something and Nothing

Optionals add a layer of complexity to vars and lets. They modify vars and lets to make a distinction between variables whose value can be either something or nothing, and variables whose value can be something but never nothing. In the context of Swift, nothing is expressed with nil, the absence of a value. Let's look at some examples.

// Non-optional vars

var mutableBeverage = "Leffe"
println(mutableBeverage) // "Leffe"

mutableBeverage = "Bud"
println(mutableBeverage) // "Bud"

mutableBeverage = mutableBeverage + "weiser"
println(mutableBeverage) // "Budweiser"

mutableBeverage = nil // Compiler error

// Optional vars

var mutableCostume: String? = "Dracula"
println(mutableCostume) // Optional("Dracula")

mutableCostume = "Frankenstein"
println(mutableCostume) // Optional("Frankenstein")

mutableCostume = "Bride of " + mutableCostume!
println(mutableCostume) // Optional("Bride of Frankenstein")

mutableCostume = nil // nil

// Non-optional lets

let immutableBeverage = "Bud"
println(immutableBeverage) // "Bud"

immutableBeverage = "Leffe" // Compiler error

immutableBeverage = immutableBeverage + "weiser" // Compiler error

immutableBeverage = nil // Compiler error

// Optional lets

let immutableCostume: String? = "Dracula"
println(immutableCostume) // Optional("Dracula")

immutableCostume = "Frankenstein" // Compiler error

immutableCostume = "Bride of " + immutableName! // Compiler error

immutableCostume = nil // Compiler error

let anotherCostume: String? = nil 
println(anotherCostume) // nil

Note the addition of a question mark to our variable declaration. The question mark signifies that the variable is an Optional, a variable that can be something (a value) or nothing (nil). Without the question mark these variables can never be nil. Note that declaring a variable as Optional does not effect its mutability. vars remain mutable and lets remain immutable.

Also note the use of an exclamation mark in select locations. The exclamation mark is a heavy-handed way to unwrap i.e. gain access to the value that an optional variable holds.

###Unwrapping Optionals

Allowing variables to be either something or nothing injects risk into our programs. If we assume a variable is something and it turns out to be nothing, our program might behave unpredictably or worse yet crash. To mitigate this risk and avoid having to make assumptions, we want to know definitively whether a variable is something or nothing before working with it. Anyone who has worked with Objective-c is familiar with this upfront runtime check.

NSString *costume = @"Dracula";

// ...

if (costume == nil) {
    // Do something for this plain clothes guest
} else {
    // Do something else for this costumed guest
}

Swift Optionals allow this check to happen at compile-time. If we attempt to use an optional variable without unwrapping it, the compiler will throw an error instructing us to first unwrap the value. Which means that the compiler will never let us use an optional value without first being certain that it is something or nothing.

We can safely unwrap optionals like this:

var costume: String? = "Werewolf"
println(costume) // Optional("Werewolf")

costume = costume + " dressed as Michael J Fox" // Compiler Error

if let something = costume {
    // The value is not nil, use it with confidence
    something = something + " dressed as Michael J Fox"
    println(something) // "Werewolf dressed as Michael J Fox"
} else {
    // The value is nil
}

A heavy-handed way to unwrap an optional is to make use of the exclamation mark. This is called force unwrapping:

var costume: String? = "Werewolf"
println(costume) // Optional("Werewolf")

costume = costume! + " dressed as Michael J Fox" 
println(costume) // Optional("Werewolf dressed as Michael J Fox")

The former is greatly preferred to the latter. By force unwrapping the optional we're asserting that we're positive the variable is not nil. But if we're wrong our program will crash. Force unwrapping puts the onus back on us humans to keep track of what is nil and what is not, and in doing so brings all of the risk and assumptions back into our code.

###Optionals in Practice

So when do we use Optionals? We use Optionals when a nil value is meaningful.

Consider the case of a model object in our iOS app that represents a video on our server. If the video's image_url property is non-nil we know it has a thumbnail image that we can use. And if it's nil we know it doesn't.

Or consider the case of an NSError object passed as an argument to a network call's completion handler. Not every network call will return an error, so it makes sense for this error object to be Optional.

On the other hand, our program might make use of a Person object. And it might require that every Person have an age. In this scenario age would be non-optional, passed into the Person constructor and never allowed to be nil.

###In Summary

In Swift, we have four different kinds of variables at our disposal:

  • vars (mutable, never nil)
  • Optional vars (mutable, nilable)
  • lets (immutable, never nil)
  • Optional lets (immutable, nilable)

This simple toolset empowers us to write clearer code and more stable programs. And it allows the compiler to be a better partner in helping us achieve these goals.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published