Swift From Scratch: Collections and Tuples

In the previous article, you learned about variables, constants, and some of the common data types, such as integers, floats, and strings. In this article, we zoom in on collections. Swift's standard library defines three collection types: sets, arrays, and dictionaries. Let's start with arrays.

1. Arrays

If you're familiar with Objective-C, JavaScript, or PHP, then it won't be difficult to understand arrays. An array is an ordered, zero-indexed collection of values. But there are a few important differences between arrays for Swift and other programming languages.

Type

The first important difference compared to arrays in Objective-C is that the values stored in an array are always of the same type. At first, this may seem like a significant limitation, but it actually isn't. In fact, this limitation has an important advantage. We know exactly what type we get back when we ask the array for one of its values.

Another key difference is the type of values an array can store. In Objective-C, an array can only store values of a class type. Swift doesn't have this limitation. An array in Swift can store strings, integers, floats, and class instances. How this works and why this is possible in Swift will become clear later in this series when we cover classes and structures.

Declaration

While there are several ways to create an array, you need to keep in mind that Swift needs to know what type of values you plan to store in the array. Create a new playground in Xcode, like we did in the previous article, and add the following lines to your playground.

The first and second lines mean the same thing. The second line is just shorthand. The square brackets wrapping the String keyword tell Swift that we're declaring an array that can only contain String objects.

You could read the first line of code as: "We declare a variable named array1 of type Array that can only contain String objects." The colon signifies of type.

The third line shows us how to initialize an array using an array literal. Array literals look very similar to array literals in Objective-C. The main difference is the absence of the @ symbol preceding the square brackets and the string literals.

There's also a fancy way to initialize an array with a predefined number of default values. The syntax may be confusing at first, but take a moment to let it sink in.

The resulting array contains five strings, with each string being equal to "Test". To better understand the above initialization, take a look at the following two lines of code in which we initialize an empty array of strings.

Don't worry if you're still confused. We'll explore the syntax in more detail once we start dealing with classes and functions. In this article, we're only focusing on collections.

Mutability

One aspect of Swift that you'll quickly come to appreciate is how to declare mutable collections. The above code snippet, for example, declares three mutable arrays. A mutable array is defined by using the var keyword. It's that simple.

If you don't want an array to be mutable, then use the let keyword instead. Swift aims to be intuitive and easy to use, and its implementation of mutability is a perfect example of that goal.

Getting and Setting Values

To access the values stored in an array, we use the same subscript syntax as in Objective-C. In the following example, we ask array3 for its second element, the string "Pear".

Replacing the value stored at index 1 is as simple as assigning a new value using the same subscript syntax. In the following example, we replace "Pear" at index 1 with "Peach".

This is only possible because the array is mutable, that is, we used the var keyword to declare the array. Mutating a constant array isn't possible. There are more advanced techniques for manipulating the contents of an array, but the underlying concept is the same.

Merging two arrays is as simple as adding them together. In the following example, we declare and merge two immutable arrays. Note that the resulting array, c, doesn't need to be mutable for this to work.

However, it is key that the values stored in a and b are of the same type. The reason should be obvious by now. As I mentioned earlier, the values stored in an array need to be of the same type. The following example results in an error.

To append an array to a mutable array, we use the += operator. Note that the operand on the right is an array. This operation wouldn't work if we removed the square brackets surrounding 4.

Operations

Arrays are objects on which you can perform a wide range of operations. Arrays expose a number of functions or methods. To invoke a method on an object, you use the dot notation. Add the following line to your playground to add an item to array3.

Let's see how many items array3 contains by inspecting the value of its count property. This outputs 4 to the results pane.

It's also possible to insert an item at a specific index by invoking the array's insert(_:at:) method as shown below. The insert(_:at:) method accepts more than one parameter, and the syntax may look a bit odd at first.

Like Objective-C, Swift supports named parameters to improve readability. The result is that code is easier to read and understand, and functions or methods don't need much explaining in terms of what they do. It is clear, for example, that the insert(_:at:) method inserts an element at index 2.

While Swift is more concise and less verbose than Objective-C, it supports named parameters. If you're coming from PHP, Ruby, or JavaScript, then this is certainly something that takes some getting used to.

Convenience Methods

What I really enjoy about Swift are the Ruby-like convenience properties and methods of Swift's standard library. An array, for example, has an isEmpty property that tells you if the array contains any elements. This is nothing more than shorthand for checking the array's count property. The result, however, is code that is more concise and easier to read.

2. Dictionaries

Dictionaries behave very similarly to dictionaries in Objective-C. A dictionary stores an unordered collection of values. Each value in the dictionary is associated with a key. In other words, a dictionary stores a number of key/value pairs.

Type

As with arrays, the keys and values stored in a dictionary need to be of the same type. This means that if you ask a dictionary for the value of a particular key, you know what type the dictionary will return.

Declaration

Declaring a dictionary is similar to declaring an array. The difference is that you need to specify the type for both keys and values. The following example shows three ways to declare a dictionary.

The second line is shorthand for the first line. The keys of these dictionaries need to be of type String while the values are expected to be of type Int. The var keyword indicates that the dictionaries are mutable.

You could read the first line of code as: "We declare a variable named dictionary1 of type Dictionary that can only contain keys of type String and values of type Int."

The third line illustrates how we can initialize a dictionary using a dictionary literal. This is similar to the syntax we use in Objective-C, but note that the curly braces are replaced by square brackets and the literal isn't prefixed with an @ symbol.

Getting and Setting Values

Accessing values is similar to accessing values of an array. The only difference is that you use the key instead of the index of the value you need to access. The following example illustrates this.

You'll notice that Xcode tells us that the value of value isn't 3, but Optional(3). What does this mean? Swift uses optionals to wrap values that can be one of two things, a value or nil. Don't worry about optionals at this point. We're going to focus on optionals in the next article of this series. Let me just tell you that optionals are another key concept of the Swift programming language.

It's interesting to point out that the syntax to access a value of a dictionary is identical to that of arrays if the keys of the dictionary are of type Int. Take a look at the following example to see what I mean.

Operations

As with arrays, the Swift standard library defines a wide range of operations you can perform on dictionaries. You can ask a dictionary for its number of key/value pairs through its count property. Removing a key/value pair is easy and intuitive, as the next example illustrates. Of course, this is only possible if the dictionary is mutable.

When you start learning Swift, you'll might run into code snippets that look odd or confusing. Take a look at the following line, in which we first declare a dictionary and then remove its key/value pairs.

You have to admit that the last line looks a bit odd. Because Swift knows the types of the keys and values that can be stored in dictionary, emptying the dictionary is as simple as assigning an empty dictionary to it.

There's no need to specify the types for the keys and values in this case, because we already did when we declared the dictionary in the first line. This points out another important detail, that is, the type of values you can store in arrays and dictionaries cannot change once the collection is declared.

3. Sets

Sets are very similar to arrays in that they store a collection of values of the same type. But there are several important differences. The elements stored in a set are unordered, and each item can only appear once in a set.

Declaration

Working with sets is a bit different from working with arrays. Take a look at the next examples in which we declare three sets. The set1 variable is of type Set<String>, a set that can only contain values of type String.

The set2 variable is an empty set, and we use an array literal in the third example to create and populate a mutable set that contains three values. Thanks to Swift's type inference, we can omit the type of the set.

Manipulating Sets

Working with sets is similar to working with arrays. We can ask for the number of elements stored in a set by inspecting the set's count property.

Inserting an element is easy. Because the elements of a set are unordered, we don't need to specify the location of the new element.

And the same applies to removing an element from a set.

You can also ask a set if it contains a particular element.

Arrays or Sets

I often refer to sets as lightweight versions of arrays. If the order of the elements is not important or you want to make sure each element can only appear once in a collection, then sets are the way to go.

4. Tuples

You are going to love tuples. Tuples aren't collections, but like collections, they also group multiple values. Similar to arrays and dictionaries, tuples can contain values of any type. The key difference, however, is that the values stored in a tuple don't need to be of the same type. Let's look at an example to explain this in more detail.

The first example declares a tuple named currency that is of type (String, Int). The second tuple, time, contains a Date instance and a string literal. The values stored in email are both of type String, which means email is of type (String, String).

Accessing Values

Indexes

To access a value stored in a tuple, you use the index that corresponds with the value you're interested in.

Xcode shows us the indexes of each value stored in a tuple in the results pane of the playground on the right.

Tuples in a Playground

Names

To improve readability, you can name the values stored in a tuple. The result is that you can access the values of the tuple through their names instead of their indexes. Declaring a tuple is slightly different in that case.

Decomposition

There's a second, more elegant way to work with the values stored in a tuple. Take a look at the following example in which we decompose the contents of currency.

The value of currency at index 0 is stored in currencyName, and the value at index 1 is stored in currencyRate. There's no need to specify the type for currencyName and currencyRate since Swift infers the type from the values stored in currency. In other words, currencyName is of type String, and currencyRate is of type Float.

If you're only interested in specific values of a tuple, you can use an underscore to tell Swift which values you're not interested in.

Conclusion

Arrays and dictionaries are fundamental components of almost every programming language, and Swift is no different. While collections behave a little differently in Swift, it doesn't take long to become familiar with Swift's collection types if you've worked with arrays and dictionaries in other programming languages. In the next tutorial, we explore optionals and control flow.

If you want to get up and running with the Swift language quickly, check out our course on creating iOS apps with Swift.

Or check out some of our other tutorials and courses on Swift and iOS development!

Tags:

Comments

Related Articles