Pyramid of Doom in Swift: How to Fix It (Nested if-let)

In Swift, chained optional binding can lead to an “if-let pyramid of doom” that looks like this:

if let a = a {
    if let b = b {
        if let c = c {
            // action
        }
    }
}

This is bad practice. However, there is a way to flatten this structure by:

if let a = a, let b = b, let c = c {
    // action
}

In this guide, you learn how to break the pyramids of doom using neater if-let statements and guard-let statements.

Optional Binding in Swift – A Quick Primer

In Swift, an optional value is something that can be either a value or a nil. Optionals are really common in Swift.

Reading values from optional requires care. This is because if the optional contains nil, the program crashes if you try to do certain actions with it. However, if there is a value, the program works as expected.

To check if an optional consists of a non-nil value, you can use optional binding. This works with the if-let construct.

if let somename = someoptional {
    // actions with somename constant
} else {
    // actions when someoptional is nil
}

The if let somename part reads “If someoptional has a value that is not nil, assign it to a constant called somename.”

For example:

var word: String?

if let word = word {
    print(word)
} else {
    print("Nil found")
}

Output:

Nil found

The “if-let Pyramid of Doom”

Once you have multiple optional variables that all need to be non-nil, you end up with a nested “if-let pyramid of doom”.

For example:

var a: String? = "This "
var b: String? = "is "
var c: String? = "test "

if let a = a {
    if let b = b {
        if let c = c {
            print(a + b + c)
        }
    }
}

Output:

This is test

Even though this works, it is bad practice. You should avoid nested code structures as much as possible because they can be hard to read and infeasible to manage.

How to Fix the “if let Pyramid of Doom”

To fix the nested if-let pyramid, you can comma-separate the nested if-let statements and omit using if + curly braces.

For example, this pyramid structure:

var a: String? = "This "
var b: String? = "is "
var c: String? = "test "

if let a = a {
    if let b = b {
        if let c = c {
            print(a + b + c)
        }
    }
}

Becomes a readable one dimensional if let statement:

var a: String? = "This "
var b: String? = "is "
var c: String? = "test "

if let a = a, let b = b, let c = c {
    print(a + b + c)
}

If the if-let statement becomes too long, you can break it down into multiple lines too:

if let a = a,
    let b = b,
    let c = c {
    print(a + b + c)
}

If you are handling optionals in a function, loop, or condition, you can use the guard to get rid of an if-let pyramid.

The guard-let Statement in Functions

The guard-let statement lets you check if an optional has nil or not. If the optional is nil, the guard exits the block of code without going further.

A guard-let statement can be used inside a function, loop, or condition.

The syntax of the guard-let statement is:

guard let somename = someoptional else { return }

This reads “if someoptional is not nil, assign the value to somename. If it is nil, stop running the current block of code.”

A guard-let statement is useful in many ways. One benefit is you can flatten the if-let pyramids using it.

For example, let’s repeat the previous example of optional binding, this time inside a function.

var a: String? = "This "
var b: String? = "is "
var c: String? = "test "

func printOptionals() {
    if let a = a {
        if let b = b {
            if let c = c {
                print(a + b + c)
            }
        }
    }
}

printOptionals()

Output:

This is a test

Now, let’s use the guard to get rid of the nested if-let pyramid:

var a: String? = "This "
var b: String? = "is "
var c: String? = "test "

func printOptionals() {
    guard let a = a else { return }
    guard let b = b else { return }
    guard let c = c else { return }
    print(a + b + c)
}

printOptionals()

Output:

This is test

As you can see, the code is now one-dimensional and readable. This piece of code works such that if any of the variables a, b, or c is nil, the function stops and never prints the values.

If you do not prefer the repetition there, make the guard-let statement shorter by:

func printOptionals() {
    guard let a = a, let b = b, let c = c else { return }
    print(a + b + c)
}

Output:

This is test

Conclusion

Today you learned how to deal with the “if-let pyramid of doom” in Swift.

To recap, the if-let statements are prone to pyramid-like code structures. These are infeasible to manage and difficult to read. To overcome this, you can shorten the if-let statements by comma-separating the optional bindings into one if-let statement.

if let a = a, let b = b, let c = c { // action }

Alternatively, if you are in a code block, use the guard-let statement to get rid of if-let statements altogether.

guard let a = a, let b = b, let c = c else { return }

Thanks for reading!

Scroll to Top