Empty or Optional Arrays

I’ve briefly mentioned Swift’s requirement that non-optional variables have a non-nil value in a previous post. I wanted to briefly share what I find to be a best practice: don’t use optional arrays.

So, what is the big difference between these the following declarations?

var heroes: [String] = []
var villains: [String]? = nil

 
What happens when we try to append?

// "Ned Stark" is added to our list of heroes
// exactly what we wanted
heroes.append("Ned Stark")

// nothing happens; the variable is nil
// it is like this line doesn't exist
villains?.append("Petyr Baelish")

 
So, how do we make it so we can add villains?

// add a nil check and create the array if it is nil
if villains == nil {
    villains = []
}

// now we can add a villain
villains?.append("Gregor Clegane")

 
This is just one example of many that requires checking for nil when an optional is in play. In most cases, however, being able to assume that our array is never nil can reduce the amount of checking that we have to perform. This makes our code cleaner and more robust.

What about a function that returns an empty array versus an optional array?

func getVillains() -> [String]? {
    return ["Cersei Lannister", "The Others"]
}

// Unwrapping an array works fine
for villain: String in getVillains()! {
    println(villain)
}

// What if we return nil?
func getVillains() -> [String]? {
    return nil //["Cersei Lannister", "The Others"]
}

// Unwrapping a nil will cause a crash
for villain: String in getVillains()! {
    println(villain)
}

// The correct way to handle the optional array is
// to add a check. This, of course, increases 
// complexity and decreases readability
if let checkedVillains = getVillains() {
    for villain: String in checkedVillains {
        println(villain)
    }
}

 
How would this look if we used an empty array instead?

func getHeroes() -> [String] {
    return ["Tyrion Lannister", "Jon Snow"]
}

// works fine if the array has contents
for hero: String in getHeroes() {
    println(hero)
}

func getHeroes() -> [String] {
    return []//["Tyrion Lannister", "Jon Snow"]
}

// no problems or extra checks required 
// for empty array
for hero: String in getHeroes() {
    println(hero)
}

 
There may be cases where you need to distinguish between a nil and an empty array, requiring the use of an optional. Most of the time, I have found that it just adds extra work.

  • Greg Walker

    I argued at a previous job that methods that returned lists or dictionaries should never return null. An empty last tells me more than null does (i.e., that explicitly nothing was returned rather than some kind of implied error state).

    I like Swift’s optionals, even if they are cumbersome at times, but I agree they shouldn’t generally be used for arrays.

  • Victor Santos

    Please edit:

    // add a nil check and create the array if it is nil

    if villains != nil { //<—– you should check whether is nil so (if villains == nil) { villains = [] }
    villains = []
    }

    // now we can add a villain

    villains?.append("Gregor Clegane")

    • You’re absolutely correct. Thanks for pointing this out!