Let and guard statements in Swift


Update, 07 Feb 2017
As of Swift 3, some of the code below may not work properly anymore. To see what changed, read Pyramid of Doom Updated (Swift 3).


Original Post

Swift 2 was announced in June, soon to be a year ago. Still, some of the concepts it introduced are new to many iOS developers – especially the ones who keep using ObjC as their #1 language and are only starting to learn Swift.

One of the new things introduced in Swift 2 is guard statement, which I think finally solves Swift’s pyramid of doom (or what I called “let indentation hell” – that’s what you get when you are nesting optional binding statements one inside another).

First, let’s take a look at hypothetical code of setting an author on a book, in Swift < 1.2

func letIndentationHell(foo: Book?, bar: Author?) {
    if let book = foo {
        if let author = bar {
            book.setAuthor(author)
        }
    }
}

That was one of the things that really terrified me when I first started using Swift 1.0 – and when you were also using ObjC-bridged code, this nesting could have been even deeper.

Swift 1.2 allowed us to bind multiple optionals in one statement

func multipleBind(foo: Book?, bar: Author?) {
    if let book = foo, let author = bar {
        book.setAuthor(author)
    }
}

That already looks better, but still, it forces you to move most of your function’s body inside indented blocks.

You could avoid it, but it would require ObjC-style nil checking, and while it works, I just personally don’t like it as much – Swift was supposed to be better than ObjC, right?

func objcStyle(foo: Book?, bar: Author?) {
    if (foo == nil && bar == nil) {
        return
    }
    let book = foo!
    let author = bar!
    book.setAuthor(author)
    // or force unwrap both directly with
    // foo!.setAuthor(bar!)
}

Function above is longer than it could be and it’s also really easy to write wrong condition for the if statement – actually that’s what I did writing it for the first time for this post.

If you try the code above and one of the obj1 or obj2 is equal to nil – you will get an error.
(proper condition is if (obj1 == nil || obj2 == nil))

What Swift 2 introduced is actually a combination of solutions suggested in example #2 and #3 – it’s called guard statement.

func swiftGuardStyle(foo: Book?, bar: Author?) {
    guard let book = foo, author = bar else {
        return
    }
    book.setAuthor(author)
}

Now, all the optionals can be checked at the beginning of your function and you can provide a code, that will be executed if the condition fails (inside the else block).

Actually if you forget to provide the code for when condition fails, compiler will throw an error! Guard must always have an else clause, that needs to contain return or break inside.

This also allows you to name objects passed to your functions and to your closures in a sane way.

I used to name function arguments and local variables differently – so I could clearly see which variables are of optional type and which ones are already unwrapped.

In this case, instead of taking variables named foo and bar as function parameters, you could rewrite your code with book and author

func swiftGuardStyle2(book: Book?, author: Author?) {
    guard let book = book, author = author else {
        return
    }
    book.setAuthor(author)
}

(this could have also been done in previous examples, but it feels much cleaner with guard)

You can also combine guard with where, to check specific condition on your optionals AND – you can use it to check conditions on non-optional values!
(I found that not long ago on Eric Cerney’s post)

func swiftGuardStyleWhere(book: Book?, author: Author?, rating: Int) {
    guard rating > 4, let book = book, author = author where author.name == "Bob" else {
        return
    }
    // will only execute if book and author are non nil
    // and when author's name is "Bob"
    // and when rating is > 4
    book.setAuthor(author)
}

With guard, Swift programming becomes easier and your code becomes cleaner – you can now check for expected conditions, not for error case (like with foo == nil) and as a bonus – you’re getting rid of that ugly indentation.

You can find all code examples from the post on GitHub.

Related Posts

GitHub Pages and Automatic Deployment

Looking for a free website hosting and automatic deployment after source code changes? Try GitHub pages and Wercker!

Credit Card Validation

Credit Card validation isn't as hard as it looks -- and you can add it to your app in a few minutes only!

Swifty function currying

Function currying is something I wanted to dig into for some time now, and finally I found a good time for it, especially now, after some changes introduced to the syntax in Swift 3.

Pyramid of Doom Updated (Swift 3)

Since last posts about using `let` and `guard`, Swift 3 came out and changed a few things here and there. Let's see what's new!

Dealing with Swift's Pyramid of Doom

Today we continue topic of avoiding Swift's "Pyramid of Doom" that we started in previous post about `guard` statement.