Optionals in Swift, Part 1
Sunday, February 25, 2024 1:40 PM
Swift optionals are a powerful addition to your coding toolbox. Let’s explore what they are and how to use them.
Level: Novice
In the Swift programming language, variables are declared like this:
var someString: String = ""
var someInt: Int = 0
var someDouble: Double = 0.0
We’ve declared three variables, a string, an integer and a double. We’ve also initialized them (given them initial values).
In more detail, we’ve:
• Used the word var to indicate that we want to create a new variable.
• Described what name we want to use to refer to that variable, followed by a colon (e.g. someString:).
• Specified what kind of value this variable will store (e.g. String meaning a string of characters, Int meaning a number without a decimal point and Double meaning a number with a decimal point).
• Specified the initial values to store in these variables.
Swift can use the initial value of a variable to infer what type of value it will store. Because Swift supports type inference, we can save some typing and just write this:
var someString = ""
var someInt = 0
var someDouble = 0.0
The Great Banking App dilemma
Suppose we’re creating an application for a bank that allows customers to see the current balances in their accounts. In this bank, customers may have a checking account, a savings account or both.
When creating our banking application, we have no way of knowing what accounts each customer will have. Let’s create some variables to hold their account balances. Because we don’t know if these accounts actually exist, we’ll omit the initial values like this:
var userCheckingBalance: Double
var userSavingsBalance: Double
So far, so good.
But customers are going to want to see the total amount of money the have in the bank, so let’s also give them the sum of their account balances by creating a computed variable like this:
var userTotalBalance = userSavingsBalance + userCheckingBalance
Xcode complains that
Variable 'userCheckingBalance' used before being initialized
Variable 'userSavingsBalance' used before being initialized
because it can’t add values without knowing what they are.
There’s an easy way to fix this. We need to initialize our variables.
var userCheckingBalance = 0.0
var userSavingsBalance = 0.0
var userTotalBalance = userSavingsBalance + userCheckingBalance
We just assume that the values are 0 when we start. It doesn’t matter because our application is going to get actual values from the bank before the customer sees any balances. But customers without a savings account will see a savings account balance of 0. That could mislead them into thinking that they actually have a savings account. That’s not so good, but our code works, so we’ll stick with this approach for now.
Now suppose that this bank charges users monthly fee of $3.99 to have a checking account. Because this is a fixed amount, we can represent it with a constant. In Swift, constants are declared with the word let, so we can do this:
var userCheckingBalance = 0.0
var userSavingsBalance = 0.0
let monthlyFee = 3.99
var userTotalBalance = userSavingsBalance + userCheckingBalance - monthlyFee
But what if the bank decides that they’re going to waive the monthly fee for customers who also have a savings account? Even if they let their savings account balance fall to $0, they won’t be charged a monthly fee.
Our app is going need some logic in order to handle this. If a customer has a savings account, there’s no monthly fee, otherwise, there is a monthly fee.
Think about how we might add this logic to our banking app. Let’s try this:
if userSavingsBalance >= 0 {
userTotalBalance = userSavingsBalance + userCheckingBalance
} else {
userTotalBalance = userSavingsBalance + userCheckingBalance - monthlyFee
}
We’re using an if…else statement to decide whether or not to charge a monthly fee. If a customer has a savings balance, there’s no fee, even if the balance is 0. Otherwise (else), we deduct the monthly fee.
Do you see a problem? We have to initialize our variables in order to use them, so we set userSavingsBalance to 0. We have no way of knowing whether or not a customer actually has a savings account. With this approach, no one will be charged a monthly fee, because everyone will have a savings balance of at least 0.
We don’t have enough information to make our logic work. We need to know which customers don’t have a savings account.
This is a dilemma. We don’t know if userSavingsBalance will exist until a customer starts using our app, but we have to give it some initial value in order for our code to work.
Give me some options
There are several ways to solve this problem, but Swift provides a kind of variable that is perfect for this situation. It’s called an “Optional”. Regular variables like Int must have some value, before we can use them. In contrast, optional variables may or may not have a value. This is perfect for our banking app. We can make userSavingsBalance an optional double variable instead of a double variable. An optional double variable may have a value like 0.0 or no value. In Swift, no value is represented by the word “nil”. Here’s how we declare userSavingsBalance as an optional double:
var userSavingsBalance: Optional<Double>
When declared as an optional double, userSavingsBalance may or may not have a value. We can declare other types as optionals using the same syntax.
var someInt: Optional<Int>
var someString: Optional<String>
And just like regular variables, we can initialize optional variables if we want to.
var someString: Optional<String> = "myString"
But unlike regular variables, we can initialize optional variables with no value.
var someString: Optional<String> = nil
However, there’s a catch. Unlike regular variables, the value stored in an optional variable can’t be accessed directly.
Notice how the variable type is wrapped inside Optional<>. Think of an optional variable as a wrapped package.
🎁
If you want to see what’s inside this package, you have to unwrap it.
The concept of optional variables being wrapped and invisible is essential to remember. Forgetting this is what leads to confusion about how to use them. The fact that they’re declared with Optional<> provides a clue, but it’s easy to forget because you won’t see optionals declared using Optional<>. It’s much more common to use a shorthand that looks like this:
var someOptionalInt: Int?
We just put a “?” after the type of variable we want to declare as an optional. This wraps the type inside an optional and gives it an initial value of nil.
Int and Int? look almost the same, but they’re not!
var someInt: Int
var someOptionalInt: Int?
someInt must be an integer, but someOptionalInt can be an integer or nil. We don’t know until we unwrap it. We’ll see how in Part 2.