Optionals in SWIFT

In SWIFT, an interesting and a powerful feature that i liked mostly is "Optionals".

Can you imagine, how many times you have checked a variable in Objective-C for its Validness, (If it is nil or not) ?
How many times do you think, you might have written the below code in Objective-C:

NSString *string = someFun()

if (string != nil){

//do some stuff with the returned string here

}


I can’t even count on this, since i do this often throughout my classes.


Apple have done a great job to solve this issue with Optional variables.


Let’s look into this feature more deeper with an example to understand its use and purpose.


Consider, you have an array of strings of Phone Numbers as below:
let numbers = [“9876543219”, “9898878890”, “9876556789”]


We’ll write a simple function to see if we can find a phone number within the array.



   func findNumber (number: String, numbers: [String]) -> String {

       for tempNumber in numbers {

           if ( tempNumber == number) {

               return number

           }

       }

   }



The above function takes in two parameters. A string and an array of strings.
The first parameter is the phone number we are looking for and the second parameter is an array of all the phone numbers. Then within the function we iterate over our array storing each phone number in a temporary variable called tempNumber. We then 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 findNumber (number: String, numbers: [String]) -> String {

       for tempNumber in numbers {

           if ( tempNumber == number) {

               return number

           }

       }

       return ""

   }



The above solution is a failure because we should be able to use an if statement such as:

       if findNumber("9890878909", numbers: numbers) {

           numberFound()

       }



However, that if statement will never get executed 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 phone 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 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 findNumber (number: String, numbers: [String]) -> String? {

       for tempNumber in numbers {

           if ( tempNumber == number) {

               return number

           }

       }

       return nil

   }



Now we are well equipped to use an if statement:

       let foundNumber = findNumber("9890878909", numbers: numbers)

       if foundNumber {

           numberFound()

       }



Now, let’s assume we needed to send the phone number to the “numberFound()” fun as “numberFound(number: Int)”. In that case, we cannot simply pass the constant “foundNumber”.

       if foundNumber {

           numberFound(foundNumber)

       }


Firstly, because foundNumber 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 foundNumber, it was implicitly defined as an optional too since that is the return type of our function.

       let foundNumber: String? = findNumber("9890878909", numbers: numbers)//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 foundNumber: String? = findNumber("9890878909", numbers: numbers)//Inferred type String?

       if foundNumber {

           numberFound(foundNumber!)//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 foundNumber = findNumber("9890878909", numbers: numbers) {

           numberFound(foundNumber.toInt())//Note the use of the ! operator to unwrap the value to String

       }



When used as an if let statement the constant foundNumber is assigned the actual value of type String, which is the same as unwrapping the value using a bang ! operator. Now that foundNumber 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 foundNumber = findNumber("9890878909", numbers: numbers) {

           if let foundPhoneNumber = foundNumber.toInt() {

               numberFound(foundPhoneNumber)//Note the use of the ! operator to unwrap the value to String

           }

       }


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


           if let foundPhoneNumber = findNumber("9890878909", numbers: numbers)?.toInt() {

               numberFound(foundPhoneNumber)//Note the use of the ! operator to unwrap the value to String

           }


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 phone number and then call the function “numberFound” else nothing will happen.

Comments

Popular posts from this blog

Free Hand Drawing on Google's Map View

Property Wrapper - Simplified!

Free Hand Drawing on Maps with MapBox SDK