Swift 2.0 comes with the excitement of the guard statement. But looks like there’s still confusion as to why guard is AMAZING, especially compared to the simple if statement before Swift 2.0.

@NatashaTheRobot it was nicely explained by you. bt i got stuck in 1 place that why gaurd is still better than plain if condition — Deepak Mishra (@dm7026) July 13, 2015

That is a completely reasonable question. So why is guard better than just a plane if ? Let me count the ways…

The Setup

I’ll use the same example as in my Error Handling blog post – a simple form with a name and age field – so please take a look at it before proceeding.

The part I’ll be focusing on is the viewModel , specifically the createPerson() function:

struct Person { let name: String var age: Int } struct PersonViewModel { var name: String? var age: String? enum InputError: ErrorType { case InputMissing case AgeIncorrect } func createPerson() throws -> Person { guard let age = age, let name = name where name.characters.count > 0 && age.characters.count > 0 else { throw InputError.InputMissing } guard let ageFormatted = Int(age) else { throw InputError.AgeIncorrect } return Person(name: name, age: ageFormatted) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 struct Person { let name : String var age : Int } struct PersonViewModel { var name : String ? var age : String ? enum InputError : ErrorType { case InputMissing case AgeIncorrect } func createPerson ( ) throws -> Person { guard let age = age , let name = name where name . characters . count > 0 && age . characters . count > 0 else { throw InputError . InputMissing } guard let ageFormatted = Int ( age ) else { throw InputError . AgeIncorrect } return Person ( name : name , age : ageFormatted ) } }

Here are the benefits of using the new guard paradigm in the createPerson() function:

Pyramid of Doom

I’ll start with the most obvious – the infamous Swift Pyramid of Doom. Here is what the createPerson() function would look like using the if statement:

func createPersonNoGuard() -> Person? { if let age = age, let name = name where name.characters.count > 0 && age.characters.count > 0 { if let ageFormatted = Int(age) { return Person(name: name, age: ageFormatted) } else { return nil } } else { return nil } } 1 2 3 4 5 6 7 8 9 10 11 12 13 func createPersonNoGuard ( ) -> Person ? { if let age = age , let name = name where name . characters . count > 0 && age . characters . count > 0 { if let ageFormatted = Int ( age ) { return Person ( name : name , age : ageFormatted ) } else { return nil } } else { return nil } }

Sure, the Pyramid of Doom get a little better with the Swift 1.2 unwrapping of multiple optionals on the same line, but you got to admit this is not pretty and hard to decipher the meat of this function (actually creating the Person!) at first glance.

Compare this to the above implementation with guard – it’s so much more readable with the Person being returned at the very end, making it clear that that’s the main goal of this function all along.

Return Options

Notice that when using the if statement version of the createPerson() function (without the guard ), we have very limited options on the return value.

In my case, I’m returning an optional Person – the person either exists or not. This puts an additional Pyramid of Doom responsibility on the caller of this function:

let personViewModel = PersonViewModel(name: "Taylor Swift", age: "25") if let person = personViewModel.createPersonNoGuard() { // DO SOMETHING IN ANOTHER PYRAMID OF DOOM HERE } 1 2 3 4 5 let personViewModel = PersonViewModel ( name : "Taylor Swift" , age : "25" ) if let person = personViewModel . createPersonNoGuard ( ) { // DO SOMETHING IN ANOTHER PYRAMID OF DOOM HERE }

But what if you want to be more specific – if there’s an error, you want to return what the exact error is to show to the user filling out the form. Well, in this case, you would do something like this:

enum PersonResult { case Success(Person) case Failure(errorText: String) } func createPersonNoGuard() -> PersonResult { if let age = age, let name = name where name.characters.count > 0 && age.characters.count > 0 { if let ageFormatted = Int(age) { let person = Person(name: name, age: ageFormatted) return PersonResult.Success(person) } else { return PersonResult.Failure(errorText: "The age is invalid!") } } else { return PersonResult.Failure(errorText: "Information is Missing!") } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 enum PersonResult { case Success ( Person ) case Failure ( errorText : String ) } func createPersonNoGuard ( ) -> PersonResult { if let age = age , let name = name where name . characters . count > 0 && age . characters . count > 0 { if let ageFormatted = Int ( age ) { let person = Person ( name : name , age : ageFormatted ) return PersonResult . Success ( person ) } else { return PersonResult . Failure ( errorText : "The age is invalid!" ) } } else { return PersonResult . Failure ( errorText : "Information is Missing!" ) } }

This Haskell-like Result Enum solution is not bad, except for the fact that you’re now returning a PersonResult instead of a Person like you meant to and you can ignore the failure option if you wanted to.

if case .Success(let person) = personResult { print("Success! Person created") } // Error case not addressed 1 2 3 4 if case . Success ( let person ) = personResult { print ( "Success! Person created" ) } // Error case not addressed

With the new guard syntax, the return value is what it’s meant to be – in this case you’re meaning to return a Person object, and the compiler forces you to handle the error cases when the Person is not returned as expected:

So the caller’s result catching syntax with guard looks like this:

do { let person = try personViewModel.createPerson() print("Success! Person created. \(person)") } catch PersonViewModel.InputError.InputMissing { print("Input missing!") } catch PersonViewModel.InputError.AgeIncorrect { print("Age Incorrect!") } catch { print("Something went wrong, please try again!") } 1 2 3 4 5 6 7 8 9 10 do { let person = try personViewModel . createPerson ( ) print ( "Success! Person created. \ ( person ) " ) } catch PersonViewModel . InputError . InputMissing { print ( "Input missing!" ) } catch PersonViewModel . InputError . AgeIncorrect { print ( "Age Incorrect!" ) } catch { print ( "Something went wrong, please try again!" ) }

Happy-Path Programming

Finally, the most elegant thing about using guard vs other programming paradigms (like the nested if statements) is that you’re coding for the happy path while still being forced to handle early exits caused by possible errors. It reminds me a lot of the famous Railway Oriented Programming talk.

You’re coding for the green, with early exits if things go red at any point. It’s a very elegant way of approaching code without the overwhelmingly complicated functional syntax described in the talk. Reading the code, you can immediately see what the happy path is:

func createPerson() throws -> Person { guard let age = age, let name = name where name.characters.count > 0 && age.characters.count > 0 else { throw InputError.InputMissing } guard let ageFormatted = Int(age) else { throw InputError.AgeIncorrect } return Person(name: name, age: ageFormatted) } 1 2 3 4 5 6 7 8 9 10 11 12 13 func createPerson ( ) throws -> Person { guard let age = age , let name = name where name . characters . count > 0 && age . characters . count > 0 else { throw InputError . InputMissing } guard let ageFormatted = Int ( age ) else { throw InputError . AgeIncorrect } return Person ( name : name , age : ageFormatted ) }

I’m sure I’m missing some other points on what makes guard so awesome, so feel free to contribute in the comments!