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.
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.
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.
var array1: Array<String> var array2: [String] var array3 = ["Apple", "Pear", "Orange"]
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
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.
var a = [String](repeating: "Test", count: 5)
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.
var b = Array<String>() var c = [String]()
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.
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
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
array3 = "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.
let a = [1, 2, 3] let b = [4, 5, 6] let c = a + b
However, it is key that the values stored in
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.
let a = [1, 2, 3] let b = [1.5, 5.2, 6.3] let c = a + b
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
var a = [1, 2, 3] a += 
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
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.
array3.insert("Prune", at: 2)
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
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.
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.
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.
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.
var dictionary1: Dictionary<String, Int> var dictionary2: [String: Int] var dictionary3 = ["Apple": 3, "Pear": 8, "Orange": 11]
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
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
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
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.
let value = dictionary3["Apple"] print(value)
You'll notice that Xcode tells us that the value of
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.
var dictionary4 = [0: "Apple", 1: "Pear", 2: "Orange"] let fruit = dictionary4
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.
var dictionary = [String: Int]() dictionary["Oranges"] = 2 dictionary["Apples"] = 10 dictionary["Pears"] = 5 dictionary = [:]
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.
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.
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
var set1: Set<String> var set2 = Set<String>() var set3: Set<String> = ["Apple", "Pear", "Orange"]
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.
var set3: Set = ["Apple", "Pear", "Orange"]
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
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.
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.
import Foundation var currency = ("EUR", 0.81) var time = (Date(), "This is my message.") var email = ("Bart Jacobs", "email@example.com")
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
String, which means
To access a value stored in a tuple, you use the index that corresponds with the value you're interested in.
var rate = currency.1 var message = time.1 var name = email.0
Xcode shows us the indexes of each value stored in a tuple in the results pane of the playground on the right.
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.
var currency = (name: "EUR", rate: 0.81) let currencyName = currency.name let currencyRate = currency.rate
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
let (currencyName, currencyRate) = 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
currencyRate since Swift infers the type from the values stored in
currency. In other words,
currencyName is of type
currencyRate is of type
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.
let (currencyName, _) = currency
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!