When creating your application, there might come a point where you need to create a list, or you might come to a point where you want to associate a bunch of values with a bunch of other values (like a real-life dictionary). To solve this problem, we will walk you through what an array is and what a dictionary is in this chapter. These are considered collection types in Swift and are very powerful.
This chapter will help you understand the following things:
All year, you've been dreaming of finally taking that family vacation to the moon. Marisa (your neighbor) went last year and it's all she ever talks about. After begging your parents to take you and your sister to the moon for a family vacation, you awoke to see this note slipped through your door.
Yes! We're going to the moon. On second thoughts, now that we're finally going, it does sound a little scary to be leaving everything behind for a full month. Also, we need to reply by writing Swift code, that doesn't seem too bad.
In the earlier chapters, we've learned that there are various types built into the Swift language:
let name = "Jim" let age = 21
Here, name
is a constant of the String
type and age
is a constant of the Int
type. They have both been assigned values: name
has been assigned the value Jim
and age
has been assigned the value 21
. Let's get back to the note we received now. It's asking us to create a list. What we need to do is generate a list of values, so how do we do that in Swift?
Before we get into that, let's come up with the list of toys we want to take with us:
Now that we have our list, how can we generate this list in Swift?
An array stores values of the same type in an ordered list. Below is an example of an array:
let list = ["Legos", "Dungeons and Dragons", "Gameboy", "Monopoly", "Rubix Cube"]
All related source code for this chapter can be found here: https://github.com/swift-book-projects/swift-3-programming-for-kids/tree/master/Chapter-7
This is an array (which you can think of as a list). Arrays are an ordered collection of values.
We've created a constant called list
of the [String]
type and assigned it a value that represents our list of toys that we want to take with us. When describing arrays, you surround the type of the values that are being stored in the array by square brackets, [String]
. The following is another array called numbers, which contains four values, being 5, 2, 9 and 22:
let numbers = [5, 2, 9, 22]
You would describe numbers
as being an array which contains Int
values, which can be written as [Int]
. We can confirm this by holding Alt and selecting the numbers
constant to see what its type is in a playground file:
What if we were to go back and confirm that list
is an array of String
values. Let's Alt click that constant to make sure:
Similar to how we created instances of String
and Int
in earlier chapters without providing any type information, we're doing the same thing here when we create list
and numbers
. Both list
and numbers
are created by taking advantage of type inference. In creating our two arrays, we weren't explicit in providing any type information, we just created the array and Swift was able to figure out the type of the array for us.
If we want to, though, we can provide type information, as follows:
let colors: [String] = ["Red", "Orange", "Yellow"]
colors
is a constant of the [String]
type.
Now that we know how to create an array in Swift which can be compared to a list in real life, how can we actually use it? Can we access various items from the array? If so, how? Also, can we add new items to the list in case we forgot to include any items? Yes to all of these questions.
Every element (or item) in an array is indexed. What does that mean? Well, you can think of being indexed as being numbered. Except that there's one big difference between how we humans number things and how arrays number things. Humans start from 1
when they create a list (just like we did when we created our preceding list). An array starts from 0
. So, the first element in an array is considered to be at index 0
:
Always remember that the first item in any array begins at 0
. This means that the index is used as an offset. The first element of the array is exactly contained in a location that the array refers to (being 0 elements away from this location).
If we want to grab the first item from an array, we will do so as shown, using what is referred to as subscript syntax:
That 0
enclosed in two square brackets is what is known as subscript syntax. We are looking to access a certain element in the array at a certain index. In order to do that, we need to use subscript index, including the index of the item we want within square brackets. In doing so, it will return the value at the index. The value at the index in our preceding example is Legos
. The =
sign is also referred to as the assignment operator. So, we are assigning the Legos
value to a new constant, called firstItem
.
If we were to print out firstItem
, Legos
should print to the console:
print(firstItem) // Prints "Legos"
If we want to grab the last item in this array, how do we do it?
Well, there are five items in the array, so the last item should be at index 5
, right? Wrong!
What if we wrote the following code (which would be incorrect!):
let lastItem = list[5]
This would crash our application, which would be bad. When working with arrays, you need to ensure that you don't attempt to grab an item at an index that doesn't exist. There is no item in our array at index 5
, which would make our application crash. When you run your app, you will receive the fatal error: Index out of range
error. This is shown in the following screenshot:
Let's correctly grab the last item in the array:
let lastItem = list[4] print("I'm not as good as my sister, but I love solving the \(lastItem)") // Prints "I'm not as good as my sister, but I love solving the Rubix Cube"
Note that because arrays start with an index of 0, that this means that the index of the last element is lastItem.count - 1
.
All of a sudden, you've now decided that you don't want to take the rubix cube as it's too difficult to play with. You were never able to solve it on Earth, so you start wondering why bringing it to the moon would help solve that problem. Bringing crayons is a much better idea.
Let's swap out the Rubix cube
for Crayons
. But how do we do that?
Using subscript syntax, we should be able to assign a new value to the array. Let's give it a shot:
list[4] = "Crayons"
This will not work! But why? Can you take a guess?
It's telling us that we cannot assign through subscript because list
is a constant (we declared it using the let
keyword). Ah! That's exactly how String
and Int
work. We decide whether or not we can change (mutate) the array based upon the let
or var
keyword just like every other type in Swift. Let's change the list array to a variable using the var
keyword:
var list = ["Legos", "Dungeons and Dragons", "Gameboy", "Monopoly", "Rubix Cube"]
After doing so, we should be able to run this code without any problem:
list[4] = "Crayons"
If we decide to print the entire array, we will see the following print to console:
["Legos", "Dungeons and Dragons", "Gameboy", "Monopoly", "Crayons"]
Note how Rubix Cube
is no longer the value at index 4
(our last index); it has been changed to Crayons
.
That's how we can mutate (or change) elements at certain indexes in our array. What if we want to add a new item to the array. How do we do that? We've just seen that trying to use subscript syntax with an index that doesn't exist in our array crashes our application, so we know we can't use that to add new items to our array.
In the last chapter, you learned how to create functions. Apple (having created Swift) has created hundreds, if not thousands, of functions that are available in all the different types (like String
, Int
, and array). You can consider yourself an instance of a person (person being the name of the type). Being an instance of a person, you can run, eat, sleep, study, and exercise (among other things). These things are considered functions (or methods) that are available to you. Your pet rock doesn't have these functions available to it. Why? This is because it's an instance of a rock and not an instance of a person. An instance of a rock doesn't have the same functions available to it that an instance of a person has.
All that being said, an array can do things that a String
and Int
can't do. No, arrays can't run or eat, but they can append (or add) new items to themselves. An array can do this by calling the append(_:)
method available to it. This method can be called on an instance of an array (like the preceding list), using what is known as dot syntax. In dot syntax, you write the name of the method immediately after the instance name, separated by a period (.
), without any space:
list.append("Play-Doh")
Just as if we were to tell a person to run, we are telling the list to append. However, we can't just tell it to append, we have to pass an argument to the append function so that it can add it to the list.
Our list array now looks like this:
["Legos", "Dungeons and Dragons", "Gameboy", "Monopoly", "Crayons", "Play-Doh"]
We have covered a lot of material important to understanding Swift and writing iOS apps here. Feel free to reread what you've read so far, as well as writing code in a playground file. Create your own arrays, add whatever items you want to them, and change values at certain indexes. Get used to the syntax of working on creating arrays, as well as appending new items. If you feel comfortable up to this point with how arrays work, that's awesome; keep up the great work!
After a long day of figuring out what you wanted to bring with you, you received the greatest gift of all. After first landing on the moon, humans began to create their own language in space, called Moon Speak. After many decades, there were enough words to create a dictionary, which was titled Moon Speak. You now have this dictionary!
Just like dictionaries on Earth, you open the book to find a certain word. After finding that word, next to it is its definition.
If we were to open up a regular dictionary and look for the word coffee, we would be met with its definition, as follows:
Coffee : A drink made from the roasted and ground bean-like seeds of a tropical shrub, served hot or iced.
Let's associate words with these two items here:
In order to retrieve this particular value here that represents the definition, we first need the key. The key here is the word coffee. If we hand you the key Turtle, you will use this key and the dictionary to retrieve the value, the value being the definition. If you plug the key Turtle into a dictionary, you should get the following value:
A slow-moving reptile, enclosed in a scaly or leathery domed shell into which it can retract its head and thick legs.
There's a type in Swift (just like String
, Int
, and array are types) that works just like a dictionary in real life. In fact, it's called a dictionary. A dictionary stores associations between keys of the same type and values of the same type in a collection with no defined ordering.
Hm, well that sounds like a real life dictionary, except for one part. What does it mean to not have defined ordering? One day, you might go through your dictionary and note that the first page has all words that begin with the letter Z. Every other time you open this dictionary, the pages will be different. So the next time you open the dictionary, you might note that the first page has all words that begin with the letter B. We will discuss this in detail later.
Let's create a Dictionary
in Swift now:
var words = ["Coffee" : "A drink made from the roasted and ground beanlike seeds of a tropical shrub, served hot or iced."]
That's it! It kind of looks a bit like creating an array, except for one big difference. Can you spot it? The colon (:
) is the difference. When creating a Dictionary in Swift, you need to separate the key and the value by a colon and then enclose that in square brackets (like an array). We've created an instance of a dictionary where the keys are of the String
type and the values are of the String
type.
You describe the type of words by enclosing the type of the keys and the type of the values with a colon in between within square brackets, like so: [String : String]
. words
is a variable of the [String : String]
type. It's a dictionary where the keys are of the String
type and the values are of the String
type. If we were was to hold Alt and select the words
variable in playground, we would be met with the following screenshot, which confirms the type of our dictionary:
Let's create a new dictionary where the keys are of the String
type and the values are of the Int
type. This dictionary will be about the planets. The keys will represent the names of the planets and the values will represent the number of moons on each of those planets:
let planets = ["Earth" : 1, "Mars" : 2, "Jupiter" : 53]
When creating a dictionary that has multiple key-value pairs (like planets), we separate the various key-value pairs with commas. Don't forget, dictionaries are unordered in Swift, so there's no concept of indexes. "Earth" : 1
is not considered to be at index 0
; again, that's because they are not ordered.
This dictionary has three keys in it: Earth
, Mars
, and Jupiter
. How, though, do we retrieve their values?
We can retrieve their values using the subscript syntax, which is similar to how we retrieve values from arrays (except without the index):
planets["Earth"]
The value we get back is 1
. This is how we use the key (Earth
) with our dictionary (planets
) to retrieve the value (1
) at that particular key (Earth
).
If we want to store the value in a constant, we would do so like this:
let earthMoons = planets["Earth"]
We've created a constant named earthMoons
and we are assigning it a value. The value being whatever is stored at the key Earth
within the planets
dictionary. Plugging this key Earth
into the dictionary, we will get back the value stored for that particular key. The value returned here will be assigned to the earthMoons
constant.
What if we want to create a new key-value pair within our dictionary. How can we do that? Similar to retrieving values at certain keys in a dictionary, we will use subscript syntax to add key-value pairs to a dictionary:
var favoriteColors = ["Neil" : "red", "Carl" : "blue"]
Here, we have a variable, called favoriteColors
. We're assigning it a value that is a dictionary of the [String : String]
type, where the keys represent names and the values represent their favorite colors.
If we want to add another key-value pair to this dictionary, we will do so as illustrated:
favoriteColors["Isaac"] = "green"
Now, our favoriteColors
dictionary contains three key-value pairs, as we've just added a new one using subscript syntax. Creating a new key-value pair looks very similar to how we would retrieve a value from a dictionary; the difference being the (=
) assignment operator, followed by the new value we want to associate with the key preceding the assignment operator:
print(favoriteColors) // Prints "["Carl": "blue", "Isaac": "green", "Neil": "red"]"
What if we want to change a value for a certain key? Carl comes back to us to tell us that his favorite color is white.
Here's how we can change his favorite color to white:
favoriteColors["Carl"] = "white"
It's the exact same syntax that we use to create a new key-value pair. What's happening here is that if there already is a key-value pair within the dictionary that exists for the Carl
key (which there is), then this will override it. This will change the value to white
now, instead of blue
for the Carl
key.
You might have noted something odd if you attempted to retrieve a value for a certain key from the preceding dictionaries and tried to print out the value to console. Let's do that now to see what it looks like.
Sticking to our favoriteColors
dictionary, let's look to print out Carl's favorite color. First, we will store the value in a constant called carlsFavColor
:
let carlsFavColor = favoriteColors["Carl"]
Let's use the print function to print out this constant to console:
print(carlsFavColor) // prints "Optional("white")"
Shouldn't it be printing just the value white
? What is this word Optional
in front of the value "white"
surrounded by parenthesis?
Before we step into that, let's do one more thing. What if I asked you to retrieve Jessica's favorite color from this favoriteColors
dictionary? You might attempt something like this:
let jessFavColor = favoriteColors["Jessica"]
Well, wait a minute. Jessica
doesn't exist as a key in our dictionary. So what happens? What is the value that is retrieved and stored in this jessFavColor
constant?
Let's find out:
print(jessFavColor) // prints "nil"
In retrieving a value from a dictionary, these values we get are represented as optionals. An optional value either contains a value or contains nil
to indicate that a value is missing. We like to think of an optional value as a wrapped present:
In our retrieval of a value from a dictionary, we will always get back a present (or an optional). In order for us to see what value we might have received from the dictionary, we need to open the present! This present is what's referred to as an optional. When we open it, it will either contain a value or it will be an empty box, where it doesn't contain any value (nil
).
Okay, so optionals are like presents that need to be opened. Why is it like that? Why did Swift set it up this way? Well, if you really think about it, there might be scenarios in your code when you look to grab something from a dictionary where there is no such key (like our Jessica example).
A dictionary will always return an Optional
when you're looking to grab a specific value with a key (like our preceding examples). In order to use the value at a certain key in a dictionary, we need to unwrap the optional, because if we don't, we saw what will happen when we attempted to print carlsFavColor
earlier. It prints out the following output:
"Optional("white")"
The next chapter will further explain what a conditional is, but for now, we will show you the bare minimum to see how you unwrap the present (optional).
Think of a conditional statement as asking a question to someone, then getting a response. Based on that response, we perform a certain action. Imagine that you want to ask your sister whether or not her favorite color is blue. She can only answer by stating true
or false
. If she answers true
, then we throw up our hands and say "Hooray!":
if true { "Hooray!" }
We only enter those curly braces in the preceding code if our sister's response is true
. That is what's referred to as an if-statement. If the condition (asking her if her favorite color is blue?) evaluates to true
(meaning that she answered true), then we enter the following curly braces and scream out Hooray!
. Note that, if she said false
, we would not enter those curly braces and we would not be shouting out Hooray!.
We can use this logic to check weather a gift exists in our present! The correct term is looking to see if there's a value in our optional:
let carlsFavColor = favoriteColors["Carl"]
We know how to write this piece of code. Here, we've created a constant called carlsFavColor
and we've assigned it a value; the value being whatever is stored at the Carl
key within the favoriteColors
dictionary. We just learned, though, that no matter what, a dictionary will always return an optional when you try to retrieve a value with a key.
All we have to do is throw in an if
statement and some curly braces and we should be good to go! We'll leave our code the way it is and just add an if statement in front of the let keyword, as shown:
if let carlsFavColor = favoriteColors["Carl"]
Now we need to add one more thing, the curly braces:
if let carlsFavColor = favoriteColors["Carl"] { print("Hooray!") print(carlsFavColor) } // Prints "Hooray!" // Prints "white"
The word used to describe this process using if-let is known as optional binding.
carlsFavColor
is now equal to the value white
. It is not equal to Optional("white")
as it was when we first attempted to retrieve the value using the Carl
key in our preceding examples. The reason for this is that we have unwrapped our gift and stored whatever was in that gift in the carlsFavColor
constant using optional binding.
In using optional binding, if we attempt to retrieve a value for a key that doesn't exist in our dictionary, we will not enter the curly braces and none of that code will be executed. It means that it opened up the gift to find no present (it is nil
):
if let jessicaFavColor = favoriteColors["Jessica"] { print("Hooray!") print(jessicaFavColor) } // Nothing prints!
This is by no means an easy concept. We recommend that you open up Xcode (playground file), attempt to create your own dictionary and try to retrieve values from that dictionary with the keys of that dictionary using optional binding.