Before we go on, here's some tips for users of other programming languages this year:
Optional type since version 8. However, much of the standard library only returns null rather than Optional, so in many cases you would need to use Optional.ofNullable. Scala and Kotlin make more comprehensive use of Optionalstd::optional. Like Java, many built-in functions and the standard library simply return null. You may need to handle null in a different way, though you could still use std::optional in the design of your own codeBecause optional values may have been added to your language's standard library later in its lifecycle, you may need to handle null references in the traditional manner. For example, you might need to use try/catch to handle a lot of potential errors in Java and C#.
In Swift, optionals allow variables to have either a value or nil (no value). This is useful when:
String to Int)Example:
var name: String? = "John"
In the above example, name is an optional string, meaning it may contain a String or be nil. Since the value is optional, Swift safely wraps the data inside an Optional container.
Technically speaking, the value is Optional.some("John") — users of Rust and other languages with functional programming facilities may find this familiar.
var name: String? = "John"
name = nil
Optional variables can be set to nil, as in line 2 above. Note: this is similar to setting a variable to None in Python — except that Swift guarantees safer handling of these kinds of values by actually wrapping it in a value called Optional.none.
Finally, Swift may give you an optional value when casting between data types. For example, let's create an integer (Int) and a number with a decimal point (Double):
let age: Int? = Int("17")
let highestScore: Double? = Double("GAME OVER")
Use ? after the type hint to define an optional type:
let age: Int? = 25 // Can be 25 or nil, equivalent to Optional.some(25)
let username: String? = nil // No value assigned, equivalent to Optional.none
Types inside types can also be optional. For example, a list of optional integers is [Int?]. Furthermore, an optional list of optional integers is [Int?]?.
To safely use an optional, it must be unwrapped. This means that we carefully check if the value exists or not; if it does, we can use it. If there is no value (Optional.none), the program safely handles the absence of the value and moves on. The unwrapped value is a concrete value.
In Python, it is possible to write code that does not handle when a value is None, causing the program to crash when you run it. Swift detects when an optional value hasn't been unwrapped and refuses to compile the code, forcing you to write safer code that is easier to test.
Use if let to check if an optional contains a value:
let city: String? = "Te Whanganui-a-Tara"
if let unwrappedCity = city {
print("City: \(unwrappedCity)")
} else {
print("No city supplied")
}
The unwrapped value is only usable inside the brackets. This means that the scope of that unwrapped value is limited to that part of the program only.
If you were to unwrap a value and then need to cast it to a different type, that cast would need to occur inside the brackets.
let userInput: String? = readLine()
if let ageString = userInput {
if let age = Int(ageString) { // Int() returns an Int?, so we unwrap it here.
print("In ten years, you will be \(age) years old!")
}
}
print to ask for the user's age (Enter your age:)print() againreadLine() to wait for the user to type something. (This is similar to input() in Python).if let to unwrap the value provided by readLine() into a new constant called ageString
if blockreadLine returns an optional String in case something unusual happens with the program that causes the function to return before any text is given; this is exceedingly unlikely to happen but you still need to do safe unwrapping, just in case!
ageString to a new integer using Int()if let to safely unwrap the value provided by Int() into a new constant called age
If you know an optional contains a value, use ! when accessing the value to force unwrap it:
let language: String? = "Swift"
print(language!) // Works if language is not nil, crashes otherwise.
Warning: Force unwrapping an optional that contains nil causes your program to crash.
if let, just force unwrap the value returned by readLine and Int
As you will have seen in the notes on scope, unwrapping values successive times requires staying within the brackets. This can become very unwieldy if you have multiple unwraps indented like so:
if let b = a {
if let c = Int(b) {
if let d = Bool(c) {
if let // ... etc.
}
}
}
This construct is called the pyramid of doom.
Instead of nesting these if let statements multiple times, you can join them into a single conditional statement. Join the let statements together using commas.
if let b = a, let c = Int(b), let d = Bool(c) {
// Here, you can use b, c, and d safely
}
A very useful construct is guard let. It does largely the same job as if let. However, it allows unwrapped values to have the same scope as they have. To illustrate this, let's look at an if let with limited scope:
if let b = a {
// b can only be used here. It has limited scope.
}
// b is NOT usable here. b's scope is limited to inside the if let.
However, with guard let, b can be used at the same level of 'indentation' (so to speak) as the statement itself. To do this, guard let contains an else block that specifies what happens if the value could not be unwrapped.
guard let b = a else {
print("Unable to unwrap the specified value. Exiting...")
exit(1) // This completely closes the program with exit code 1.
}
print(b) // b is usable here and anywhere else in the program (beyond this point).
Using guard let ... else is very good for avoiding the pyramid of doom we saw above. It also requires you to handle errors explicitly, leading to safer code that is easier to test.
guard let's else block must always exit the current scope. This means that:
exit(1), signifying that the program has exited with an error.for or while loop, you can use break or continue:
break completely ends the loop, regardless of whether the condition has been metcontinue stops the current iteration of the loop but continues with the next; this is useful for allowing for loops to completely finish.Note: these also apply to .forEach
return to prematurely exit the functionreturn a constantnilmap must always return a value. However, if you wish to return nil, you can use compactMap instead (this results in a new collection with fewer items than the original — or even none at all!)
You work for an airline and need to create a simple Swift program that lets users book a seat and select a meal preference. Since users may not always enter valid data, you must safely unwrap all user input.
readLine() to accept inputInt? using Int()
Int?
String?.lowercased() (i.e. myString.lowercased())
Double)
Double?
Enter your seat number: 12
You have booked seat number 12
Select your meal preference:
1. Vegetarian
2. Standard
3. Kosher
Enter the number: 2
You have selected the Standard meal.
Do you want extra legroom? (yes/no): yes
Extra legroom added.
Enter your weight (kg): 75.5
Passenger weight recorded: 75.5 kg
Enter your seat number: A12
Invalid seat number. Please enter a number.
Select your meal preference:
1. Vegetarian
2. Standard
3. Kosher
Enter the number: 5
Invalid meal choice.
Do you want extra legroom? (yes/no): maybe
Invalid choice.
Enter your weight (kg): seventy
Invalid weight entry. Please enter a number.
lets in a single condition, guard let, or (ideally) both