Just An Application

June 28, 2014

So Swift Then: Optionals Or Wot No nil ?

One of the more interesting features of Objective-C is the ability to send messages to non-existent objects, so

    ...
    
    NSString* path      = nil;
    BOOL      isTxtFile = [[path lastPathComponent] hasSuffix:@".txt"];
    
    if (isTxtFile)
    {
        printf("is text file\n");
    }
    else
    {
        printf("is not text file\n");
    }
    
    ...

prints

    is not text file

Depending upon your language of choice this kind of thing usually results in an NPE or the abrupt termination of your program, but in Objective-C nothing happens.

The reason nothing happens in Objective-C is because the semantics of sending a message to nil are well defined.

If you send a message which returns nothing, i.e., void, nothing happens.

if you send a message which returns a reference to object to nil then you get back nil.

If you send a message which returns something other than an object to nil you get back 0 or its equivalent

In the example above the call to lastPathComponent on nil returns nil.

The call to hasSuffix on nil returns false.

The downside to this is that you can, on occasion, spend quite a bit of time wondering why nothing is happening only to eventually discover that some variable had the value nil when it ought not to have.

The designers of Swift have decided to be helpful and eliminate this downside by making it both impossible to forget to initialize variables and impossible to initialize them to nil in the Objective-C style.

1.0 Constants And Variables

You can declare a constant in Swift like this

    let constantCod: String = "a fish"

Swift supports type inference so you can omit the type if you wish

    let constantCod = "a fish"

As you might expect given that it is a constant you cannot assign another value to constantCod once it has been initialized.

In addition if the constant refers to a compound value, an instance of a struct for example, the value itself is immutable.

You declare a variable in Swift in the same way but using var instead of let.

    var cod: String = "a fish"

and without the type.

    var cod = "a fish"

You can assign another value to a variable in the usual way

    cod = "another fish"

If you are declaring a constant or a global variable you must specify a value in the declaration, but if you are declaring a local variable you can omit the initializer if you wish in which case you must specify the type.

If you declare a local variable without an inititializer you cannot access that variable until it has been explicitly initialized.

So doing this

    var cod: String

    ...

    // compiler error here: Variable 'cod' used before being initialized 
    var uppercaseCod = cod.uppercaseString 
    
    
    println("uppercaseCod == \(uppercaseCod)")

gets you a compiler error, whereas, doing this

    var cod: String

    cod = "a fish"

    var uppercaseCod = cod.uppercaseString

    println("uppercaseCod == \(uppercaseCod)")

prints

    uppercaseCod == A FISH

as you might expect.

2.0 The Optional Type

While this is all very helpful, what if you really, really need to declare a variable which has a value except when it doesn’t ?

The answer is to declare the variable to have the type

    Optional<T>

where T is the type of the variable when it does have a value.

For example

    var optionalCod: Optional<String>

The generic type Optional<T> is defined in the Swift library.

Swift provides syntactic sugar for this in the form of question mark (‘?’) suffix to the type of the variable so you can write the above as

    var optionalCod: String?

3.0 Assignment

You can assign three types of thing to a variable of type Optional<T>

  • a value of type T

  • nil which indicates that the variable has no value, or

  • another value of type Optional<T>

Omitting the initializer when declaring an Optional variable

    var optionalCod: String?

is equivalent to explicitly initializing it to nil

    var optionalCod: String? = nil

If you assign one Optional<T> value to another the value will be copied. so

    var cod : String? = "a fish"
    var dab : String? = cod
    
    println("cod == \(cod)")
    
    cod = nil
    
    println("cod == \(cod)")
    println("dab == \(dab)")

prints

    cod == a fish
    cod == nil
    dab == a fish

4.0 Accessing The Value

Now you have succeeded in declaring a variable which may or may not have a value how do you get at the value, assuming there is one ?

In Swift parlance this is termed unwrapping and there are multiple ways of doing it.

4.1 Forced Unwrapping

If you are absolutely certain that there is a value in the variable you can use forced unwrapping which simply involves adding an exclamation mark (!) as a suffix

    optionalCod!

This is definitely the way to go if you like your programs to terminate abruptly when something goes wrong because if there is no value in the variable that is exactly what happens

So doing this

    var optionalCod: String?

    ...
    
    var cod : String = optionalCod!

gets you this at runtime

    fatal error: Can't unwrap Optional.None

and everything stops.

4.2 Look Before You Leap

Fortunately for those of us of a more nervous disposition it is possible to check whether an Optional variable contains a value before potentially exploding it using the bang operator !

For example

    var optionalCod: String?
    
    ...

    if optionalCod
    {
        println("uppercase optionalCod == \(optionalCod!.uppercaseString)")
    }
    else
    {
        println("optionalCod is nil")
    }

4.3 Optional Binding

Alternatively you can combine the checking and the unwrapping like so

    var optionalCod: String?

    if let cod = optionalCod
    {
        println("uppercase cod == \(cod.uppercaseString)")
    }
    else
    {
        println("optionalCod is nil")
    }

4.4 Optional Chaining

If you are invoking a method on the value in an Optional variable you can add a question mark (‘?’) suffix to the variable and then invoke the method, so given

    var path: String?

you can do

    path?.componentsSeparatedByString("/")

If the variable contains a value then the method will be invoked. If it is nil then nothing happens.

The value of this expression will of necessity be of type Optional<T> where T is the type of the value returned by the method being invoked

    var path: String?

    ...
    
    let parts: Optional<String[]> = path?.componentsSeparatedByString("/")

    ...

Given that the type of the expression is of type Optional<T> you might think that if you want to call a method on the result you need to use the ‘?’ suffix again like so

    path?.componentsSeparatedByString("/")?.reverse()

but if you try this you get a compiler error, specifically

    path?.componentsSeparatedByString("/")?.reverse()
    Operand of postfix '?' should have optional type: type is 'String[]'

which is a bit puzzling at first.

In fact while it is true that in isolation the type of

    path?.componentsSeparatedByString("/")

is

    Optional<String[]>

in the context of a chain of method calls if the Optional variable has no value then the ? operator shortcuts the entire chain and returns immediately.

So at runtime at the point the method reverse is being called the ? operator has already opened the box and found a
String[] value in it rather than nil and it has called componentsSeparatedByString on it and the type of

    path?.componentsSeparatedByString("/")

really is

    String[]

Another way of looking at it is that adding a postfix ? is equivalent to doing this

    let path  : String?
    var result: (String[])?
    
    // ...
    
    if let p : String = path
    {
        let parts   : String[] = p.componentsSeparatedByString("/")
        let reversed: String[] = parts.reverse()
    
        result = reversed
    }
    else
    {
        result = nil
    }

from which it is clear that the result of calling componentsSeparatedByString really is a String[].

Which is all a very long and round about way of you do not actually need the second postfix ? at all. You can simply do this

    path?.componentsSeparatedByString("/").reverse()

You can chain as many methods together as you can find and you still only need the initial ‘?’ assuming of course that none of the methods themselves explicitly return Optional values.

If the last method in the chain returns a value of type Optional<T>, for example

    path?.componentsSeparatedByString("/").reverse()[0].toInt()

where the method toInt returns

    Optional<Int>

you might think that the result of the method chain expression would be

    Optional<Optional<T>>

but it is in fact

    Optional<T>

which has the merit of consistency and simplicity since you only ever have to unwrap the resulting value once

4.5 Implicit Unwrapping

If you declare an Optional variable then, as we have seen, you need to unwrap it one way or another each time you want to use the value.

If you are in a position where you are certain that the Optional variable will definitely have a value each time you use it then you can avoid the need to explicitly unwrap it at each point of use
by declaring it like this

    var optionalDab: String!

adding a postfix exclamation mark (!) to the type rather than a question mark (?).

You can then refer to it directly when using without explicitly unwrapping it.

It is still being unwrapped implicitly using forced unwrapping so if your model of the variable’s usage is incorrect and there is in fact some point at which it can be used before it has been initialized you will find out all about it at runtime.

5.0 Optional Constants

You can, if you want, declare constants with Optional types but after the novelty has worn off they turn out not to be very interesting at all.

Since the only thing you can do with a constant is initialize it or use it you can either have a constant which is guaranteed to be nil

    let constantDab: String?

or one that is guaranteed to have a value

    let constantDab: String? = "an optional fish"

and thats it.

6.0 Optional Globals

You can declare a global variable with an Optional value.

This can be useful if you do not want to or cannot initialize it at the point it is declared but it is guaranteed to have been initialized before it is used.

This case also allows you to take advantage of implicit unwrapping as described above but the caveat about runtime exceptions also applies.

    // declaration
    
    var globalOptionalEel: String!
    
    ...
    
    // initialization
    
    globalOptionalEel = "global eel"
    
    ...
    
    // usage with implict unwrapping plus chance of runtime exception
    
    let constantEel: String = globalOptionalEel

Copyright (c) 2014 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

Create a free website or blog at WordPress.com.