guard In Swift

guard

guard keyword, as the name suggests, guards the execution in Swift. Either you meet the condition, or just get out of here.

The syntax for guard goes like this

guard condition else {
    statements
}

If you know swift you would be knowing that the condition here should be Bool.
The else clause is compulsory with guard.
If the condition in guard turns out to be false the else code block should make sure that the execution is taken out side the scope in which guard is defined or call a function that never returns.

Uses of guard

You might argue that whatever we can achieve with guard can be achieved with if . Thats right. But keep patience, in coming sections we will see the advantages of using guard over if. Let us see the uses of guard statement first.

Early Exit

There would be situations where you would want your function to return if certain conditions are not met. For example you might want to make sure that some value is not negative, some value is not nil etc. Generally these will be situations where executing code any further does not make sense. Lets see an example

func calculateAge(dob : Date) -> Int? {
    let currentDate = Date()
    guard  dob < currentDate else {
        return nil
    }
    let calendar = Calendar.current
    let years = calendar.dateComponents(
        [.year], from: dob, to: currentDate)
    return years.year
}

In the above example we are trying to calculate the age based on passed Date of Birth. We are making sure that the passed date of birth is less than the current date otherwise we will return nil.

Lets call this function with following date

var dateString = "1991-06-17"
var dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
var dateOfBirth = dateFormatter.date(from: dateString)
var age = calculateAge(dob : date!)

As expected this would return the correctly calculated age for the given Date of Birth.

Screen Shot 2017-07-18 at 3.57.02 PM

lets change the date of birth to some future date and try calling the function

dateString = "2087-06-17"
date = dateFormatter.date(from: dateString)
age = calculateAge(dob : date!)

As can be seen, now the guard comes to our rescue and returns nil as soon as it finds any future date.

Screen Shot 2017-07-18 at 4.06.45 PM

Checking for nil and Unwrapping optionals

You will many time find yourself in situations where you need to make sure that the variable you are dealing with is not nil. . If the value is nil you simply want to return. guard can be used to do both. Lets change our calculateAge function to accept optional values.

func calculateAge(dob : Date?) -> Int? {
    let currentDate = Date()
    guard  let dateOfBirth = dob else{
        return nil
    }
    let calendar = Calendar.current
    let years = calendar.dateComponents(
        [.year], from: dateOfBirth, to: currentDate)
    return years.year
}

In the above case the guard would make sure that the date passed to the function is not nil . Also it would safely unwrap the value into dateOfBirth local variable.(We have removed the condition to check if the date passed to above function is less than current date to keep things simple)

Differences between if and guard

There are following differences between if and guard

  1. Values unwrapped via guard persists throughout the block in which guard is defined.
    This can be seen from our above example of function calculateAge. The variable dateOfBirth is available even after the guard block.
    If we unwrap the optional using if, the dateOfBirth variable would not be available outside if block.

    func calculateAge(dob : Date?) -> Int? {
        let currentDate = Date()
        if let dateOfBirth = dob else{
            return nil
        }
        let calendar = Calendar.current
        let years = calendar.dateComponents(
            [.year], from: dateOfBirth, to: currentDate) // Error :: Use of unresolved indentifier dateOfBirth
        return years.year
    }
    
  2. else block is mandatory in guard , which is optional in if .
    else is mandatory in guard statement. If you miss else with guard you will get compile time error.
  3. else block should take the execution out of the block in which guard is defined.
    It can do this with a control transfer statement such as return , break , continue , or throw or it can call a function or method that doesn’t return. This is not the requirement for else block defined with if . For example if we will not return from the else block of our guard statement in calculateAge function, we will get compile time error.

    func calculateAge(dob : Date?) -> Int? {
        let currentDate = Date()
        guard  let dateOfBirth = dob else{
            print("I not returning") //Error :: 'guard' body may not fall through, consider using a 'return' or 'throw' to exit the scope
        }
        let calendar = Calendar.current
        let years = calendar.dateComponents(
            [.year], from: dateOfBirth, to: currentDate)
        return years.year
    }
    

Advantages of using guard over if

  1. Using a guard statement for requirements improves the readability of your code, compared to doing the same check with an if statement. It lets you write the code that’s typically executed without wrapping it in an else block, and it lets you keep the code that handles a violated requirement next to the requirement. For example if we want to write our function calculateAge with only if , it would look like something like
    func calculateAge(dob : Date?) -> Int? {
        let currentDate = Date()
        if  (dob != nil) {
            if dob! < currentDate {
                let calendar = Calendar.current
                let years = calendar.dateComponents(
                    [.year], from: dob!, to: currentDate)
                return years.year
            } else {
                return nil
            }
        }else {
            return nil
        }
    }
    

    As you can see the readability of the code is certainly hampered. We are actually writing the main logic of our function in the entire if block. Using guard you make your intention clear at the beginning itself. If some conditions are not met simply return. This makes your code more maintainable. Though technically you can always achieve the same thing with if , always use guard if you need to return if certain conditions are not met.

  2. As mentioned earlier optionals unwrapped with guard remain available for block in which guard is defined. Which is not the case with if .
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s