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 two collection types, arrays and dictionaries. Let's start with arrays.
1. Arrays
If you're familiar with Objective-C, JavaScript, or PHP, then you won't find it difficult to wrap your head around the concept of arrays. An array is an ordered, zero-indexed collection of values. There are, however, a few important differences.
Type
The first important difference with 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 with Objective-C arrays 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 as well as 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 creating an array can be done several ways, 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 second line is shorthand for the first line. 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." Note that I italicized of type since that is what the colon signifies.
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](count: 5, repeatedValue: "Test")
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.
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 Swift's implementation of mutability is a perfect example of this 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"
.
array3[1]
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"
.
array3[1] = "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 a
and b
are of the same type. The reason should be obvious by now. The values stored in an array need to be of the same type. The following example will result 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 4
.
var a = [1, 2, 3] a += [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
.
array3.append("Cherry")
Let's see how many items array3
contains by invoking its count
method. This outputs 4
in the results pane on the right.
array3.count
It's also possible to insert an item at a specific index by invoking the array's insert
method as shown below. The insert
method accepts more than one parameter and it may look a bit odd at first.
array3.insert("Prune", atIndex: 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
method inserts an element at index 2
.
While Swift is more concise and less verbose than Objective-C, it does support named parameters. If you're coming from PHP, Ruby, or JavaScript, then this is certainly something that will take 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.
array3.isEmpty
2. Dictionaries
Dictionaries behave very similar 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.
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 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 typeDictionary
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.
let value = dictionary3["Apple"] println(value)
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.
var dictionary4 = [0: "Apple", 1: "Pear", 2: "Orange"] let fruit = dictionary4[0]
Operations
As with arrays, the Swift standard library defines a wide range of operations you can perform on dictionaries. A dictionary returns its number of key/value pairs by calling count
on it. 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.
dictionary4.removeValueForKey(0)
When you start learning Swift, you'll occasionally 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 declare 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. Tuples
You are going to love tuples. While tuples aren't 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.
var currency = ("EUR", 0.81) var time = (NSDate(), "This is my message.") var email = ("Bart Jacobs", "bart@example.com")
The first example declares a tuple named currency
that is of type (String, Int)
. The second tuple, time, contains an NSDate
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.
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.
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.
var currency = (name: "EUR", rate: 0.81) let currencyName = currency.name let currencyRate = currency.rate
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
.
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 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.
let (currencyName, _) = currency
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.