Comprehension Swift’s Optionals

Comprehension Swift’s Optionals

The day Apple introduced Swift they talked about three main things: safe, modern, and powerful. One such powerful feature is called Optional. Heading forward from the last blog about introduction to Swift, this article will zoom more advance aspect of swift that is Optionals. We will discuss about Functional programming in upcoming blog of this series. Most language features exist to solve a problem and naturally optional was created to solve a problem.

This is the tenth post of our blogger month at Xebia. Every day one of the Xebian post a new blog. You can follow the full series at http://blog.xebia.in/pages/blogger-month-2015/.

Table of content

Optionals

Let’s explore its maneuver and impetus. Assume that you have an array containing Room numbers that they are in trouble. We need to check if room number exists in that array so we can send them a help.

let roomNumbers = ["101","202","303","404"]

We can write a very simple function to see if we can find a room number within the array.

func findRoom (roomNumber : String, roomNumbers: String[]) -> String {
    for tempRoomNumber in roomNumbers {
        if ( tempRoomNumber == roomNumber) {
            return roomNumber
        }
    }
}

The above function takes in two parameters. A string and an array of strings. The first parameter is the Room number we are looking for and the second parameter is an array of all Room numbers. Then within the function we iterate over our array storing each room number in a temporary variable called tempRoomNumber. Then we compare the values and if we find a match we return it.

However, the above function is incomplete because we are only returning a value when one is found. What happens when we don’t find a value? We must return something. Ideally, we should return a nil but we specified String as our return type. One solution would be to return an empty string:

func findRoom (roomNumber : String, roomNumbers: String[]) -> String {
    for tempRoomNumber in roomNumbers {
        if ( tempRoomNumber == roomNumber) {
            return roomNumber
        }
    }
    return ""
}

The above solution is a failure because it always returns a String value. Moreover, it does not evaluate to a boolean expression.

We could compare against an empty string but that defeats the purpose. It would be nice to return a nil when the Room number is not found in the array.

Changing the last line to return nil will give us a compiler error because we set our return value to be a String. This is where optionals come in very significantly & handy.

An optional can have two states, a value or a nil. To declare an optional, simply suffix it with a ?.

Let’s rewrite our function above to use an optional.

func findRoom (roomNumber : String, roomNumbers: String[]) -> String? {
    for tempRoomNumber in roomNumbers {
        if ( tempRoomNumber == roomNumber) {
            return roomNumber
        }
    }
    return nil
}

Now we are well equipped to use an if statement:

let culprit = findRoom("404", roomNumbers)
if culprit {
    sendNotice();
}

Optional Chaining

Instead of sending a generic notice, let’s assume we needed to use a function sendNoticeTo(Room: Int), where we are sending a notice to a specific room number. In that case, we cannot simply pass the constant culprit.

if culprit {
    sendNoticeTo(culprit);
}

Firstly, because culprit is a string and secondly because it is an optional. As I mentioned above that optionals can either contain a value or nil. When we created the constant culprit it was implicitly defined as an optional too since that is the return type of our function.

let culprit: String? = findRoom("404",roomNumbers) // inferred type String?

It is important to understand that String and String? are not the same. The former can only contain a string whereas the later can contain either a string or a nil. So how do we convert the result of our function to the value of type String?. You can instruct the optional to provide you with a value if there is one.

let culprit = findRoom("404",roomNumbers)
if culprit {
    sendNoticeTo(culprit!) // Note the use of the ! operator to unwrap the value to String
}

We are not quite there yet because we need to send an Int to our sendNoticeTo function. Besides, we can also combine the two above statements into one:

if let culprit = findRoom("404",roomNumbers) {
    sendNoticeTo(culprit.toInt())
}

When used as an if let statement the constant culprit is assigned the actual value of type String, which is the same as unwrapping the value using a bang ! operator. Now that culprit is of type String we can call the toInt() function on it. There’s still one issue we haven’t addressed. The function toInt() returns Int? which is also an optional. Now we could nest if statements.

if let culprit = findRoom("404",roomNumbers) {
    if let culpritRoomNumber = culprit.toInt() {
        sendNoticeTo(culpritRoomNumber)
    }
}

Why nest when we can chain function calls. Swift provides what is called optional chaining to make such statements compact:

if let culpritRoomNumber = findRoom("404",roomNumbers) ?.toInt() {
    sendNoticeTo(culpritRoomNumber)
}

The above ties in very nicely because we chain the call to the function toInt by using the ? operator. Since toInt also returns an optional we can check to see if we received a valid room number and then call the function sendNoticeTo else nothing will happen.

Conclusion

Although we covered only the basics of the Swift optional behavior above, it is evident that Swift provides a clean and modern syntax that is quite similar to other popular languages.

Optionals help save a lot of time and make our code readable and efficient. It might take a little getting used to but once you get the handy of it you would hate to have code lying around that explicitly checks for nils or sentinel values.

Leave a Reply

Your email address will not be published. Required fields are marked *