Optionals
## Learning Objectives
- Understand what optionals are
- Use optional types and unwrapping
- Master optional binding
- Work with nil coalescing
## What are Optionals?
Optionals represent values that may or may not exist:
```swift
var name: String? = "Alice"
var empty: String? = nil
```
### Why Optionals?
```swift
// Without optionals - unclear if value can be missing
// String name = null; // What does null mean?
// With optionals - explicit about missing values
let name: String? = nil
```
## Optional Type Declaration
### Syntax
```swift
String? // Optional String (may hold String or nil)
Int? // Optional Int
Double? // Optional Double
Bool? // Optional Bool
[ String ]? // Optional Array of Strings
```
### Non-Optional vs Optional
```swift
let certain: String = "Hello" // Must have a value
let maybe: String? = nil // May or may not have a value
```
## Forced Unwrapping
### Using (!)
```swift
let name: String? = "Alice"
print(name!) // "Alice"
// WARNING: Crashes if nil!
let empty: String? = nil
// print(empty!) // RUNTIME CRASH!
```
### When to Avoid
```swift
// BAD - crashes if nil
func getUsername() -> String? { return nil }
let username = getUsername()!
// print(username) // Crash!
```
## Optional Binding
### if let
```swift
let name: String? = "Alice"
if let unwrappedName = name {
print("Hello, \(unwrappedName)")
} else {
print("No name provided")
}
// Output: Hello, Alice
```
### guard let
```swift
func greet(name: String?) {
guard let name = name else {
print("No name")
return
}
print("Hello, \(name)")
}
greet(name: "Alice") // Hello, Alice
greet(name: nil) // No name
```
### Multiple Binding
```swift
let name: String? = "Alice"
let age: Int? = 30
if let unwrappedName = name, let unwrappedAge = age {
print("\(unwrappedName) is \(unwrappedAge)")
}
// Can combine with conditions
if let name = name, let age = age, age >= 18 {
print("\(name) is an adult")
}
```
## Nil Coalescing
### ?? Operator
```swift
let name: String? = nil
let displayName = name ?? "Anonymous"
print(displayName) // "Anonymous"
let actualName: String? = "Alice"
let greeting = "Hello, \(actualName ?? "Guest")"
print(greeting) // "Hello, Alice"
```
### Chaining
```swift
let nested: String?? = nil
let result = nested ?? "default" // "default"
let optionalString: String? = "Hello"
let result2 = optionalString ?? "nil or empty" // "Hello"
```
## Implicitly Unwrapped Optionals
### Declaration
```swift
var implicitlyOptional: String! = "Hello"
print(implicitlyOptional) // No need to unwrap
```
### Use Cases
```swift
class ViewController {
var label: UILabel! // Will be set later
func viewDidLoad() {
label.text = "Hello" // Safe because we know it's set
}
}
```
### Dangers
```swift
var dangerous: String! = nil
print(dangerous) // CRASH!
```
## Optional Chaining
### Optional Chaining Syntax
```swift
let street: String? = address?.city?.street?.name
```
### Example
```swift
class Person {
var residence: Residence?
}
class Residence {
var address: Address?
}
class Address {
var street: String = "123 Main St"
}
let person = Person()
let street = person.residence?.address?.street
// street is nil (person.residence is nil)
// No crash - chain short-circuits
```
### With Methods
```swift
if let length = person.residence?.computeRoomCount?() {
print("House has \(length) rooms")
}
```
## Comparing Optionals
### Equality
```swift
let a: Int? = 5
let b: Int? = 5
let c: Int? = nil
a == b // true
a == c // false
a == 5 // true (compares to non-optional)
c == nil // true
```
### Optional Enum Case
```swift
enum Color {
case red, green, blue
}
let selected: Color? = .red
if selected == .red {
print("Red is selected")
}
```
## Map and FlatMap
### Optional Map
```swift
let number: Int? = 5
let squared = number.map { $0 * $0 } // Optional(25)
let nilNumber: Int? = nil
let nilSquared = nilNumber.map { $0 * $0 } // nil
```
### Optional FlatMap
```swift
let string: String? = "42"
let int = string.flatMap { Int($0) } // Optional(42)
let invalid: String? = "hello"
let notInt = invalid.flatMap { Int($0) } // nil
```
## Pattern Matching
### switch with Optionals
```swift
let value: Int? = 5
switch value {
case nil:
print("Is nil")
case let unwrapped?:
print("Has value: \(unwrapped)")
}
```
### if case
```swift
let age: Int? = 25
if case let unwrapped? = age {
print("Age is \(unwrapped)")
}
```
## Common Errors
### Unexpected Nil
```swift
func getUser() -> String? { return nil }
let user = getUser()
// user.uppercased() // ERROR: Cannot call on Optional
// user!.uppercased() // CRASH: nil value
```
### Wrong Type
```swift
let string: String? = "123"
// let number: Int = string // ERROR: Cannot assign String? to Int
let number: Int? = string.map { Int($0) }
```
## Best Practices
### Do
```swift
// Use optional binding
if let value = optional {
use(value)
}
// Use guard for early exit
func process() {
guard let value = optional else { return }
use(value)
}
// Use nil coalescing for defaults
let result = optional ?? "default"
```
### Don't
```swift
// Don't force unwrap
// let value = optional! // DANGEROUS
// Don't compare to nil explicitly (prefer optional binding)
// if optional != nil { } // Not idiomatic
```
## Summary
- Optionals represent values that may be nil
- Declare with `Type?` syntax
- Force unwrap with `!` (dangerous if nil)
- Optional binding: `if let` and `guard let`
- Nil coalescing: `??` provides default value
- Optional chaining: `?.` safely accesses properties
- `map` and `flatMap` transform optional values
- Implicitly unwrapped optionals (`Type!`) defer unwrapping
- Prefer safe unwrapping over force unwrap
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →