Swift
Optional
Default Value
Programming
iOS Development

Providing a default value for an Optional in Swift?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Swift's nil-coalescing operator ?? provides a default value when an optional is nil. The expression optionalValue ?? defaultValue unwraps the optional if it has a value, or returns the default if it is nil. This is the primary and most idiomatic way to provide fallback values for optionals in Swift.

The Nil-Coalescing Operator (??)

swift
1let name: String? = nil
2let displayName = name ?? "Anonymous"
3print(displayName)  // "Anonymous"
4
5let age: Int? = 25
6let displayAge = age ?? 0
7print(displayAge)  // 25 (optional had a value, default not used)

The ?? operator checks if the left side is nil. If not, it unwraps and returns the value. If nil, it returns the right side. The result type is non-optional.

Chaining Multiple Defaults

swift
1let primaryName: String? = nil
2let secondaryName: String? = nil
3let fallbackName: String? = "Guest"
4
5let name = primaryName ?? secondaryName ?? fallbackName ?? "Unknown"
6print(name)  // "Guest"

Swift evaluates left to right, using the first non-nil value. If all are nil, the final non-optional value is used.

With Dictionary Lookups

swift
1let settings: [String: Int] = ["timeout": 30, "retries": 3]
2
3// subscript returns Optional — provide a default
4let timeout = settings["timeout"] ?? 60
5let maxConnections = settings["maxConnections"] ?? 10
6
7print(timeout)         // 30 (found in dictionary)
8print(maxConnections)  // 10 (not found, used default)

With Function Return Values

swift
1func findUser(id: Int) -> String? {
2    let users = [1: "Alice", 2: "Bob"]
3    return users[id]
4}
5
6let userName = findUser(id: 3) ?? "Unknown User"
7print(userName)  // "Unknown User"

Default Value with Lazy Evaluation

The right side of ?? is evaluated lazily — it only executes if the optional is nil:

swift
1func expensiveDefault() -> String {
2    print("Computing default...")  // Only prints if needed
3    return "Computed Value"
4}
5
6let cached: String? = "Cached"
7let value = cached ?? expensiveDefault()
8// "Computing default..." is NOT printed — cached has a value
9print(value)  // "Cached"
10
11let empty: String? = nil
12let value2 = empty ?? expensiveDefault()
13// "Computing default..." IS printed
14print(value2)  // "Computed Value"

This is important for performance — expensive default computations are skipped when unnecessary.

Optional Binding (if let / guard let)

For more complex default logic, use optional binding:

swift
1// if let — use within the block
2let input: String? = "42"
3if let number = Int(input ?? "") {
4    print("Parsed: \(number)")
5} else {
6    print("Invalid input")
7}
8
9// guard let — unwrap or return early
10func processAge(_ age: Int?) {
11    guard let validAge = age else {
12        print("No age provided, using default")
13        return
14    }
15    print("Age: \(validAge)")
16}

Map and Default

swift
1let optionalNumber: Int? = 5
2
3// map transforms the value if present, returns nil if absent
4let doubled = optionalNumber.map { $0 * 2 } ?? 0
5print(doubled)  // 10
6
7let nilNumber: Int? = nil
8let doubledNil = nilNumber.map { $0 * 2 } ?? 0
9print(doubledNil)  // 0 (map returns nil, ?? provides default)

Default Values in Function Parameters

swift
1// Optional parameter with a default
2func greet(name: String? = nil) {
3    let displayName = name ?? "World"
4    print("Hello, \(displayName)!")
5}
6
7greet()            // Hello, World!
8greet(name: "Alice")  // Hello, Alice!
9
10// Non-optional with default (preferred when nil has no meaning)
11func connect(host: String = "localhost", port: Int = 8080) {
12    print("Connecting to \(host):\(port)")
13}

Struct Default Values

swift
1struct Config {
2    var host: String
3    var port: Int
4    var timeout: Int
5
6    init(host: String? = nil, port: Int? = nil, timeout: Int? = nil) {
7        self.host = host ?? "localhost"
8        self.port = port ?? 8080
9        self.timeout = timeout ?? 30
10    }
11}
12
13let config = Config(host: "api.example.com")
14print(config.port)     // 8080 (default)
15print(config.timeout)  // 30 (default)

Comparison with Other Unwrapping Methods

swift
1let value: String? = nil
2
3// 1. Nil-coalescing — returns non-optional with default
4let a = value ?? "default"  // String
5
6// 2. Force unwrap — crashes if nil
7let b = value!  // FATAL ERROR: unexpectedly found nil
8
9// 3. Optional binding — branches on nil/non-nil
10if let c = value {
11    print(c)
12} else {
13    print("was nil")
14}
15
16// 4. Optional chaining — propagates nil
17let length = value?.count  // Int? (nil if value is nil)
18let safeLength = value?.count ?? 0  // Int (0 if value is nil)

Common Pitfalls

  • Force unwrapping instead of ??: Using value! crashes on nil. Use value ?? default for safe unwrapping with a fallback. Reserve ! for cases where nil truly indicates a programming error.
  • Expensive right-hand side: Although ?? is lazy, developers sometimes forget this and avoid it unnecessarily. The default expression is only evaluated when the optional is nil.
  • Nested optionals: let x: Int?? = nil; x ?? 0 returns Optional(0), not 0. Double-optional wrapping requires careful handling — flatten with x.flatMap { $0 } ?? 0.
  • Confusing ?? with ?: value?.method() is optional chaining (propagates nil). value ?? default is nil-coalescing (provides fallback). They solve different problems but combine well: value?.count ?? 0.
  • Mutable default gotcha: var items = optionalArray ?? [] creates a new array if nil. Mutating items does not affect the original optional. If you need to mutate the original, use if var.

Summary

  • Use ?? (nil-coalescing operator) to provide default values for optionals
  • value ?? default returns the unwrapped value or the default — result is non-optional
  • Chain multiple ?? operators for cascading fallbacks
  • The right side is lazily evaluated — expensive defaults are only computed when needed
  • Use if let/guard let for complex unwrapping logic beyond simple defaults
  • Prefer ?? over force unwrapping (!) for safe, crash-free code

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.