Blog / June 18, 2022 / 4 mins read / By Suneet Agrawal

Safe calls(?.) vs Nil checks(!.) in Swift

In Swift, the type system distinguishes between references that can hold nil (nil references) and those that can not (non-nil references).

For example, a normal property can’t hold a nil value and will show a compile error.

var variable : CustomClass = CustomClass()
variable = nil //compilation error 
//'nil' cannot be assigned to type 'CustomClass'

Instead, we can add a ? after the data type of that property which declares that variable as a nillable property.

var nillableVariable : CustomClass? = CustomClass()
nillableVariable = nil //works fine

In any case, the nillable property requires a nil check every time before accessing that property or requires a confirmation from the developer that the estimation of that property won’t be nil while accessing it.

variable.someMethodCall() //works fine as the compiler is sure that
                          //the variable can’t be nil
nillableVariable.someMethodCall() //will highlight compilation error
                                  //as compiler is not sure as
                                  //nilVariable can be nil.

There are still few techniques for using or accessing the nillable variable. One of them is safe call ?. and another one is nil check !. but before figuring out the difference among both, let’s understand what they are in detail first.

If Let and Guard Let

This is the old pattern that we use in every other language, checking the variable if its nil or not.

If let checks if the nillable variable is nil or not.

If the variable is nil, this will go to else block.

If not, it will copy the non-nil reference into another variable within its scope where we can use it without any nil checks.

if let nillableVariable = nillableVariable {
    nillableVariable.someMethodCall()
} else {
    // fallback flow
}

Guard let separates the failure case first. It is designed to exit the current function, loop or condition if the check fails.

It is used to run a code block and return if the variable is nil otherwise below else condition, the variable is non-nil and we can use it without any nil checks.

guard let nillableVariable = nillableVariable else {
    //do something in nil case
    return
}
        
nillableVariable.someMethodCall()

You can read the difference between if-let and guard-let on if vs if let vs guard let in Swift.

Safe Calls (?.)

Another way of using a nillable property is safe call operator ?.

This calls the method if the property is not nil or returns nil if that property is nil without throwing an NPE (nil pointer exception).

nillableVariable?.someMethodCall()

Safe calls are useful in chains. For example, if Bob, an Employee, may be assigned to a Department (or not), that in turn may have another Employee as a department head, then to obtain the name of Bob’s department head (if any), we write the following

let departmentHead = bob?.department?.head?.name

Such a chain returns nil if any of the properties in it is nil.

Nil-Coalescing Operator (??)

This one is similar to safe calls except the fact that it can return a non-nil value if the calling property is nil even

val result = nillableVariable?.someMethodCall()
                       ?? fallbackIfnilMethodCall()

The Nil-Coalescing operator will evaluate the left expression and will return it if it’s not nil else will evaluate the right side expression. Please note that the right side expression will only be called if the left side expression is nil.

The !. Operator

This operator is used to explicitly tell the compiler that the property is not nil and if it’s nil, please throw a nil pointer exception (NPE)

nillableVariable!.someMethodCall()

This code will work fine if ‘nillableVariable’ is not nil else it will throw an NPE.

Difference between ?. and !.

  • The basic difference while using ?. and !. is if you want to separate a normal flow of var or let property having a ‘non-nil’ value with ‘nil’ value flow use ?.
  • But if you are sure that the property value is not nil use !. instead of ?.
  • Also, ?. can be used to call default flow using ?? at the end of the expression but !! will only throw an NPE.
Comments