Skip to content

Instantly share code, notes, and snippets.

@alexshafran
Last active December 17, 2017 21:12
Show Gist options
  • Save alexshafran/62243a7d0e24dded7b53 to your computer and use it in GitHub Desktop.
Save alexshafran/62243a7d0e24dded7b53 to your computer and use it in GitHub Desktop.
Collections in Swift

#Collections in Swift

Arrays and dictionaries are the most common data stores in many applications. Swift has brought us a new generation of these collections, so let's take a look at how they compare to the collections found in Objective-C.

##Greatest Differences

###Typing One of the most obvious differences between collections in Objective-C and Swift is that Swift collections are explicitly typed. Typed collections are useful for many reasons, primarily because the system requires you to be precise with the content type you are working with. Any type mismatches will throw errors, which will help you avoid common mistakes.

###Mutability Perhaps the most convenient addition in Swift is that all collections are mutable by default. Whereas Objective-C uses custom subclasses for mutability, Swift determines mutability by the way a variable is declared. If you want to create a mutable collection, use the var introducer. Conversely, you use the let introducer to declare a constant. This is advantageous because both mutable and immutable collections can be created using the same set of initializers. For example:

    var mutableArray = ["a", "b", "c"] // mutable
    let constArray = ["d", "e", "f"] // immutable

###Generics Generics deserve a post of their own, but it is important to discuss their affect on Swift collections. Both the Array and Dictionary collections in Swift are generic, meaning their implementations can be used to work with any data type. Additionally, many of the higher-order collection operators exist as global generics, such as contains, enumerate, equal and join. The distinction is important because these functions are not invoked on the collection itself; instead, the collection is passed as one of the parameters, for example:

    var anArray = ["a", "b", "c"]
    var result = contains(anArray, "a")
    // true

This is definitely a new way of thinking about our code, especially for developers coming from Objective-C.

##Arrays

Just like Objective-C, there are many ways to make an Array in Swift. The code below shows the most basic method:

    var letters : String[] =  ["a", "b"]

This example declares the variable letters as an array of Strings, meaning it can only contain String types. This example also initializes letters to store two values, "a" and "b".

Using type inference, the same example can be written in a shorter form:

    var letters = ["a", "b”]

Since the elements provided in the array literal above are both of type String, Swift can infer that letters should be of type String [ ].

One of the neatest ways to create an Array is by pre-populating it with default values.

    var letters = Array(count: 3, repeatedValue: "a")
    // [a, a, a]

###Interacting with an Array The easiest way to read or modify elements in an Array is with subscripting, e.g. letters[0] = 2. However, Swift adds several new convenience methods for working with arrays:

Appending elements:

    letters.append("c")

or

    letters += "c"

Appending multiple elements:

    letters += ["c", "d"]

Changing multiple elements:

    letters[1...2] = ["e", "f”]

Additionally, when using the subscript syntax, the number of replacement values does not have to match the range provided.

###Enumeration A standard for-in loop is the easiest way to iterate over an Array:

    for letter in letters
    {
        print(letter)
    }
   // prints “abcdef"

If you need the index when enumerating, you can use the enumerate function. It returns a tuple containing the index and value for each item in the collection.

    for (index, letter) in enumerate(letters)
    {
        print("\(index)\(letter)")
    }
    // prints "0a1b2c3e4f" 

##Dictionaries

Just like arrays, there are many ways to declare and initialize a Dictionary in Swift. Below is the simplest example:

    var inventory: Dictionary<String, Int> = ["Cars" : 1, "Trucks" : 2]

This example declares the variable inventory as a Dictionary that contains String keys and Int values. This example also initializes inventory with two key-value pairs.

Once again, we can rely on Swift’s type inference when declaring a Dictionary:

    var inventory = ["Cars" : 1, "Trucks" : 2]

Since both keys are strings and both values are integers, Swift knows that inventory should be of type Dictionary<String, Int>.

###Interacting with a Dictionary The easiest way to read or modify elements in a Dictionary is with index subscripting, e.g.

    inventory["SUVs"] = 5

Since collections are mutable by default, the code above will insert the key “SUVs” (if necessary), and assign its value to 5.

Just like Objective-C, a Dictionary will return nil if it does not contain the requested key. A common way to check for nil is:

    if let num = inventory["SUVs"] {
        println("The number of SUVs in \(num)")
    }
    else {
        println("There are no SUVs")
    } 

Unlike a Swift Array, a Dictionary do not support the += operator for adding new elements.

###Enumeration An example for-in loop for Swift dictionaries is shown below. Note the (key, value) tuple format provided by the loop.

    for (key, value) in inventory {

        println("key: \(key) value: \(value)")
    }

If you need the index while enumerating a Dictionary , you can use the enumerate function, just like we did with arrays.

    for (index, (key, value)) in enumerate(inventory) {

        println("index: \(index) key: \(key) value: \(value)")
    }

In the example above, each item in the Dictionary is returned as a nested tuple in the form of (index, (key, value)).

##Assignment and Copy Behavior

In Objective-C, the NSArray and NSDictionary collection classes are passed by reference. Swift's corresponding collections, Array and Dictionary, are both implemented as structures. Because of this, Swift collections are pass-by-value, meaning they will be copied whenever they are assigned or passed as a parameter. This might cause performance concerns at first glance, but Swift documentation notes that while your code will behave as if a copy took place, a true copy is only performed when absolutely necessary.

###Dictionaries Whenever you assign or pass a Dictionary, it is immediately copied. Any keys/values that are value types are copied, and all keys/values that are objects will have their references copied. Since Strings are values in Swift, it is worth noting that strings will be copies as well.

The code below is an example of how dictionaries are copied during assignment.

    var inventory = ["Cars" : 1, "Trucks" : 2]
    var inventoryCopy = inventory
    inventoryCopy["Cars"] = 3
    println(inventory["Cars"])
    // prints "1"

Notice that changing the number of cars in the copied Dictionary had no effect on the number of cars in the original Dictionary . Similar code in Objective-C would return 3 cars in both dictionaries.

###Arrays Swift has complex rules for copying its Array type. Even though Swift will always appear to copy an Array when it is passed or assigned, this is not always the case. Swift will avoid making a true copy of the Array until you perform an action that can change the array’s length. These actions include inserting, deleting, appending, or replacement by ranged subscripts. When this copy is required, it follows the same rules as a Swift Dictionary -- values are copied and objects are copied by reference. Of course, an array can always be force copied by calling the copy method.

The code below shows that arrays are not copied during simple manipulations, such as a single replacement.

    var letters = ["a", "b", "c”]
    var lettersCopy = letters
    lettersCopy[0] = "b"
    println(letters)
    // prints "[b, b, c]”

However, operations that change the length of the array will trigger a copy. For example:

    var letters = ["a", "b", "c"]
    var lettersCopy = letters
    lettersCopy.removeLast()
    lettersCopy[0] = "b"
    println(letters)
    // prints "[a, b, c]"

While the above examples show that arrays are not always copied, the takeaway is that we should treat all Swift collections as pass-by-value. Expecting the system to not copy a collection could get you into trouble.

@carlware
Copy link

carlware commented Jun 8, 2014

do you know how to iterate over the following array?
var arr = ["one",34,89.90]

@monyschuk
Copy link

In your playground - try the following

// Playground - noun: a place where people can play

import Cocoa

var str = "Hello, playground"

var foo :Array<Any> = ["Hi", 34, 3.14]

func describeFoo() -> Array<String> {
    var description : Array<String> = []
    for val in foo {
        if let str = val as? String {
            description += "the string is " + str
        } else if let num = val as? Int {
            description += "the int is \(num)"
        } else if let num = val as? Double {
            description += "the double is \(num)"
        }
    }
    return description
}

let desc = describeFoo()

@masters3d
Copy link

let constArray = ["d", "e", "f"] // immutable

The only immutable part of the "let" list is the length. Not the contents.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment