LoginSignup
1
2

More than 1 year has passed since last update.

Swift: "guard" vs "if", and "guard let" vs "if let"

Last updated at Posted at 2021-02-23

Swift: guardif と、 guard letif let (日本語)

It would be more appropriate to see this question as guard vs if and guard let vs if let.

guard vs if

Standalone guard and if are both simple flow control statements and do not do any unwrapping.

guard just exits early if its condition isn't met.

Comparison table

guard if
Function Early exit Flow control
Focus on ... ... the "early exit path": checking conditions are correct before going further. ... the “happy path”: the behavior of our function when everything has gone according to plan.
Readability, Clarity Improved Can lead to pyramids of doom
Nested Scope (adds levels of depth)
Enclosing Scope Necessary Not necessary
Must ... return from the scope if the check fails
Return / Throw Necessary Not necessary
Think of it as ... ... validation. ... simple flow control
Preferred location At the start of the scope

Examples

guard
let array = ["a", "b", "c"]
func at(at index: Int) -> String?{
   // exit early when index is out of bound
   guard index >= 0, index < array.count  else { return nil }
   return array[index]
}
print(at(1))
if
let array = ["a", "b", "c"]
let index = 1
if index >= 0, index < array.count {
    print(array[index])
} else {
    print(nil)
}

guard let vs if let

guard let and if let both unwrap optionals.

guard let, like guard, exits early if its condition isn't met.

Comparison table

guard let if let
Function Unwraps optionals Unwraps optionals
Designed to ... ... exit the current scope if the check fails. ... just unwrap some optionals.
Focus on ... ... the "early exit path": checking conditions are correct before going further. ... the “happy path”: the behavior of our function when everything has gone according to plan.
Enclosing Scope Necessary Not necessary
Must ... return from the scope if the check fails
Return / Throw Necessary Not necessary
Readability, Clarity Improved Can lead to pyramids of doom
Nested Scope (adds levels of depth)
Visibility The unwrapped optional is visible in the scope after the guard let statement. The unwrapped optional is only visible in the if let statement's code block scope
Think of it as ... ... validating then unwrapping some optionals. ... just unwrapping some optionals.
Preferred location At the start of the scope
Unwrap multiple optionals at once
Side Effects Should be avoided Ok

Examples

guard let

All the strings (firstNameString, lastNameString, emailString, passwordString) are accessible in the scope after the guard statements, if all the fields are valid:

guard-let
func validateThenRegisterUser() {
    // validation
    guard let firstNameString = firstName.text where firstNameString.characters.count > 0 else {
            firstName.becomeFirstResponder()
            return
        }
    guard let lastNameString = lastName.text where lastNameString.characters.count > 0 else {
            lastName.becomeFirstResponder()
            return
        }
    guard let emailString = email.text where 
        emailString.characters.count > 3 &&
        emailString.containsString("@") && 
        emailString.containsString(".") else {
            email.becomeFirstResponder()
            return
        }
    guard let passwordString = password.text where passwordString.characters.count > 7 else {
            password.becomeFirstResponder()
            return
        }

    // The validated information can now be used for registration
    let newUser = User()
    newUser.firstName = firstNameString
    newUser.lastName = lastNameString
    newUser.email = emailString
    newUser.password = passwordString
    APIHandler.sharedInstance.registerUser(newUser)
}
if let

All the strings (firstNameString, lastNameString, emailString, passwordString) are accessible only within the scope of the if statement code block.

This creates a "pyramid of doom" which presents many issues like:

  • readability,
  • ease of reordering/refactoring the code. For example, if the fields' validation order was altered, you would have to rewrite most of the code.
if-let
func validateThenRegisterUser() {
    // validation: starts the pyramid of doom
    if let firstNameString = firstName.text where firstNameString.characters.count > 0{
        if let lastNameString = lastName.text where lastNameString.characters.count > 0{
            if let emailString = email.text where emailString.characters.count > 3 && emailString.containsString("@") && emailString.containsString(".") {
                if let passwordString = password.text where passwordString.characters.count > 7{
                    // The validated information can now be used for registration
                    let newUser = User()
                    newUser.firstName = firstNameString
                    newUser.lastName = lastNameString
                    newUser.email = emailString
                    newUser.password = passwordString
                    APIHandler.sharedInstance.registerUser(newUser)
                } else {
                    password.becomeFirstResponder()
                }
            } else {
                email.becomeFirstResponder()
            }
        } else {
            lastName.becomeFirstResponder()
        }
    } else {
        firstName.becomeFirstResponder()
    }
}
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2