You already know about expressions, operators, variables, constants, and types in Swift. It’s time to consolidate and build on that knowledge and explore some more advanced Swift data structures and operators: collections and control flow. In this chapter, we’re going to talk about putting collections of data into variables and constants, and how to structure data, manipulate data, and operate on data using control flow statements. We’ll be looking at other ways to collect and structure data later in the book, but for now let’s get started with arrays, sets, and dictionaries.
Meet the Swift Pizzeria, the fastest pizza place in town.
The Chef, who is pretty grumpy at the best of times, wants to sort his pizzas alphabetically using Swift (he figures that since his pizza shop is named Swift, he should learn the language).
He’s facing some issues though. Can you help Swift Pizzeria sort their pizzas?
Swift has a range of special types that can store collections of things. Unsurprisingly, these are called collection types.
The name says it all, really!
There’s three main collection types you can use to store collections in Swift: arrays, sets, and dictionarys. They all have differences, and knowing when to use the right one is a fundamental skill of Swift programming.
The quickest way to store a list of pizzas in a collection and then sort them alphabetically is an array, but you’ll come back to that. Before you can help the chef, you need to think about each of the collection types and what they can do.
The Swift types you’ve used so far allow you to store individual pieces of data, of different types (for the most part). For example, String lets you store strings, Int lets you store integers, Bool lets you store boolean values, and so on.
Swift’s collection types allow you to store multiple pieces of data.
A collection type makes storing collections of things much easier.
Arrays are ordered, and every element has a value of the same type, is automatically numbered with an index, starting from 0. This means you can access individual values using the indices.
Sets are unordered, and every element has a value of the same type. There no index value for sets, so you need to access them using features of the Set, or by iterating through the whole set.
Each value can only be stored in a Set once.
Dictionaries are an unordered collection of keys and values. Every key is the same type, and every value is the same type. A value can be accessed using its key, just like indices in Arrays. The keys can be different types to the values.
The first collection type we’re going to look at is an array. An array is collection of things, of the same type, that has a specific order. You can create an array simply, like this array of strings representing potential names for a cat:
An array is an ordered collection of values of the same type.
Or you could create an array like this, like this variable array of integers:
Arrays number the values they hold, starting from 0. So the array we just created would look like this:
We can add things to variable arrays in a variety of ways, including appending:
We can add things to variable arrays in a variety of ways, including appending:
You can also remove elements:
numbers.remove(at: 3)
And change the value of specific elements:
numbers[2] = 307
You knew exactly how big the arrays we’ve worked with so far were, but what if you need to work with an array that you don’t know the length of in advance?
Imagine you’ve been asked to do something with an array, named ingredients, that contains the ingredients you need for a specific pizza. It’s been created somewhere else, so you don’t know how many elements are in it.
In this case, we’ll take a peek behind the curtain. Here’s the array being created:
Every collection type in Swift has a property available: count. count
lets you get the number of elements in the collection, like this:
print("There are (ingredients.count) ingredients in this pizza.")
You can also do other useful things, like check if the array is empty:
print(ingredients.isEmpty)
There’s also some convenience methods that let you get the minimum and maximum elements of an array:
print(ingredients.max()) print(ingredients.min())
The second collection type that we’re going to use is the set. Sets are similar to arrays, and must contain solely one type, but sets are unordered, and there can only be one of each distinct value in a set.
You can create a set like this:
var evenNumbers = Set([2, 4, 6, 8])
Or like this:
var oddNumbers: Set = [1,3,5,7]
Sets are useful when you need to make sure each item only appears once.
There are other ways to create sets, and we’ll cover them later in the book.
You can insert items into sets, just like with an array:
And likewise, you can easily remove elements (even if they don’t exist in the set you can still ask for it to be removed):
The final collection type we’re going to use right now is a dictionary. As you might be able to intuit from the name, a dictionary collects things by mapping one thing, to another (just like a real-world dictionary maps a word to a definition). Like sets, dictionairies in Swift are unordered.
If you wanted to create a dictionary that represented some people who played a board game, and the score they got, you could do it like this:
Or like this, where you tell the Swift compiler explicitly which types you’d like the keys and values to be:
We can also create an empty dictionary like this, but we have specify the types in advance, because (obviously) the compiler can’t figure that out without any values present:
var scores: [String: Int] = [:]
Reading a value from a dictionary is a little strange, compared to what we’ve done so far.
To read a value from a dicitionary, you can simply use the key:
print(scores["Paris"]!)
The exclamation mark afterwards is used to directly access the value of the key, and we’ll come back to that syntax a little bit later, in the next chapter, when we use optionals.
You can also add new values or change existing ones in the dictionary, like this:
scores.updateValue(17, forKey: "Bob")
Or like this:
scores["Josh"] = 4
The three main collection types are the array, set, and dictionary.
But Swift has also a few more tricks up its sleeve. There is a concept of a generic collection type, which we’ll get to much later, where you can create your own collection types, but there’s also the humble tuple.
Tuples let you store multiple values in a single variable or constant, but without the overhead of being (for example) an array.
Tuples are perfect for when you need a very specific collection of values where each item has an exact position or name.
A tuple can store as many values as you like, as long as the amount of them and type of them is defined up front. For example, you could use a tuple to represent coordinates, like x
and y
, and store them in one variable:
Tuples can have names for each component or have no names at all. You can update or modify tuples without using the names for the components, as long as the types of the values match:
You can also always access the contents of tuples using integers, starting from 0
:
When you’re working with the various data types in Swift, one of the ways you can make your code self-documenting is by using type aliases. A type alias, basically, lets you give a new name to an existing type.
Let’s take a look at a quick example: a simple, one-way, celcius to fahrenheit temperature convertor, written without any type aliases:
While this is a very small, simple piece of code, it can be made easier to follow using type aliases:
When you’re coding in Swift (and other programming languages, naturally), it’s often useful to be able to do something repeatedly or do something under certain conditions. These two concepts, broadly, are often called control flow.
There’s lots of tools in your control flow toolbox, and they all work a little differently, and all have their place.
There are no hard rules.
You can use all of these control flow statements in a variety of situations. There’s no single rule that tells you when to use what, but it will usually be relatively obvious.
If statements are best for executing some code only if a certain condition is true.
Switch statements are useful in the same context, but if you have lots of possible different case, depending on the conditions.
For loops, for-in loops, while loops, and repeat-while loops allow you to repeatedly execute some code based on certain conditions.
You’ll find uses for all of these control flow statements as you code more.
If statements allow you to do something under certain conditions. They allow you to execute some specific code if certain conditions are met. If statements, broadly, are part of a group of concepts known as conditionals.
If you have a Bool
named userLovesPizza
and you wanted to use it to determine whether you should give the user a pizza, based on whether they love pizza or not, you could use an if statement.
The if statement can be combined with else in order to provide more options.
The conditional in an if statement must be a Boolean expression. This means it must evaluate to either true, or false. If the expression is true, the code inside the if statement (between the two { }) is executed.
You often have to think about a lot of possibilities when you’re creating conditionals with if statements. Going back to the pizza shop we were helping with the if statements, imagine how unwiedly things could get if we also wanted to send a special message with each type of pizza? We’ll check if a string variable contains a certain type of pizza, and display a message based on that pizza. We’ll need to use a new operator, the equal to operator, to check.
We might end up with something that looks like this:
A switch statement often serves you better if you need to execute some different code depending on the value of a variable or constant..
Lots of if statements becomes unreadable, and very inelegant, fast. A switch statement lets you switch
which bits of code get run, based on the values of variables or constants that you want to switch on.
A switch statement can have multiple clauses, and is not simply limited to testing for equality or comparing. We’ll come back to this later.
Going back to our pizza shop, we can rewrite the potential giant series of if statements as a nice, clean switch statement:
So, if we (for some reason) wanted to check if a number was 9, 42, 47, or 317, we could use a switch statement:
A switch statement is easier to maintain in the long term, and makes for more readable code..
We’ll come back to switch statements both later in book (when we look at enumerations). But, in general, if you have more than a handful of things you’re checking for in a large block of if-else statements, then you might want to consider switch statement.
In addition to the mathematical operators you’ve already seen, and the assignment operator, Swift has several other useful operators, including a selection of range operators. Range operators are shortcuts for expressing a range of values. They are particularly useful for loops and other control-flow statements:
These both represent the range 1, 2, 3, 4
:
The closed range operator defines a range that runs from the value on the left side of the operator to the value on the right side.
The half-open range operator defines a range that runs from the value on the left side of the operator to the value immediately before the value on the right side.
There’s also a one-sided range operator that lets you consider a range that continues as far as it can in one direction. For example, the following constants defines a range that starts at 5 and continues infinitely, and a range that goes from an infinitely negative number to 100, respectively:
We can then check if the constanst contain a certain value:
Say you need to help a school write some code that prints out a text representation of a student’s final grade. The school grades on a system of Fail (0 to 49 points), Pass (50 to 59), Credit (60 to 69), Distinction (70 to 79), and High Distinction (80 to 89). You could do this with a bunch of if statements, but it wouldn’t look very nice.
Consider a switch statement:
But the switch statement has more tricks up its its sleeve. We’ll look at one final trick here, before we come back to switches later in the book.
Imagine if you want to switch on a number, and check whether it’s an odd numer or an even number. You can do it like this:
Swift has two major loop statements you can use: for (and for-in) loops, for running some code a certain number of times, or iterating through every item in a collection type, and while (and do-while) loops, for running some code over and over again until something becomes true, or false.
You can also put loops inside other loops if you want.
Let’s unpack this, and write some code that displays the numbers from 1 to 10, using a for loop:
The ...
you see in the range is called the closed-range operator. This operator lets you define a range that runs from the value specified before the operator to the value specified after the operator, including both of those values. So the range 1...5
is 1, 2, 3, 4, 5
.
The for-in loop makes it easy to iterate over the items in an array.
Programming is thirsty work, so let’s pretend we’re running a café, and we’re using an Array of Strings called drinks to keep track of which drinks are still available.
If we want to iterate through that array, and display a message to let people know what they can order, we can use a for-in loop.
Swift doesn’t know what a single drink is called.
We can use a word that makes it easier for a human to understand what’s going on.
If the only purpose of your loop is to simply iterate through some sort of data structure, you might be able to use for each instead:
Let’s unpack this, and write a while loop that, while a number is less than 100, multiplies the number by 2:
A while loop is particularly useful when you’re not sure how many iterations the loop will go through until it’s started.
Let’s write the same loop we wrote over the page, for the while loop, but using repeat-while instead:
var pizzaHawaiian = "Hawaiian" var pizzaCheese = "Cheese" var pizzaMargherita = "Margherita" var pizzaMeatlovers = "Meatlovers" var pizzaVegetarian = "Vegetarian" var pizzaProsciutto = "Prosciutto" var pizzaVegan = "Vegan"
Q: Where are the semicolons? I thought programming was meant to be full of semicolons!
A: Swift doesn’t end lines with semicolons. If it makes you feel more comfortable, you can do it anyway. It’s still valid, it’s just not necessary!.
Q: I thought Swift had something called “protocols” instead of classes? When are we learning those? What about classes?
A: Swift does have something called “protocols”, and we promise that we’ll get to it. We’ll also get to classes very soon. Swift has more than one way to structure a program, and both classes and protocols offer useful paths forward. Be patient, and we’ll get there.
Q: How do I run my code on an iPhone or iPad? I bought this book so I could learn to make iOS apps and get filthy rich.
A: We’ll explain everything you need to know to use your Swift knowledge to make iOS apps much, much later in the book. We make no promises about getting rich, but we do promise that you’ll know how to build iOS apps by the end. Again, be patient.
Q: Why would I ever use a constant when I could just make everything a variable in case it needs to change?
A: Swift places a much greater emphasis on using constants than other programming languages do. It can help, a lot, with making your program safer and more stable by only using variables for values that you expect to change. Additionally, by telling the Swift compiler that your values with be constants, the compiler can make our programs faster by performing certain optimizations for us.
Q: Why can’t I change the type of a value after I’ve assigned it? Other languages can do this.
A: Swift is a strongly typed language. It puts a lot of emphasis on the way type system works, and encourages you to learn it. You cannot change the type of a variable after you’ve created it. You can create a new variable and cast the type to something new if you need to.
Q: I don’t really understand the difference between enumerations and structures and classes. What’s the point of having three similar things?
A: It’s hard to convey without a little more experience under your belt. Trust us to point you in the right direction. In a few more chapters you’ll be equipped with the gut-feeling to pick which one you need. We promise.
Q: Optionals seem really useful. Why don’t other programming languages seem to have those?
A: That’s a great question. We have no idea.
Q: So are variables and constants also types?
A: No, variables and constants are named locations to store data. The data in a variable or constant has a type, so the variable or constant has a type, but it in itself is not a type.
Q: What if I need to store some data that Swift doesn’t have a type for?
A: Great question. We’ll be looking at ways you can create your own types later on in the book.
Q: What about the Collection Types? Can I make my own Collection Type too?
A: Yes, we’ll get back to this in a chapter or three, as it requires some advance Swift features. But the answer is yes: you can make your own Collection types that are just as capable as the provided three (arrays, sets, dictionaries).
Q: What do I do if I need to create a variable, but don’t know what type to set it to in advance?
A: Another great question! You might have noticed that in our code so far, sometimes we specify a type in the expression that creates a variable, and sometimes we don’t. Swift’s type system is able to infer types, or use a provided type annotation.
Q: Switch statements in other languages I’ve looked at don’t seem very powerful, and I never used them. Swift seems to have considerably more powerful switch statements. What’s the deal?
A: You’re right. Swift’s switch statement is considerably more capable than most other language’s. Switch statements are super useful, and Swift has a really good implementation of them.
You’re only three chapters into your Swift journey, but you’ve already done so much. You’ve learned about the building blocks of Swift, and how to create a Swift Playround and build programs out of operators, expressions, variables, constants, types, collections, and control flow. Phew.
Have a break, tackle some of the exercises remaining in this chapter, and get some rest so you’re refreshed to march onwards!
We recommend starting with the a review of your knowledge so far, through the bullet points over the page, and tackling the crossword puzzle on the next page. Don’t skip them!
Repeating your Swift knowledge helps solidify what you’ve learned.
Exercise
A Swift program is all scrambled up on the fridge. Can you reconstruct the code snippets to make a working Swift program that produces the output listed over the page? The code uses concepts you’ve looked at in detail, as well as a few that you haven’t.
Your job is to take lines of code from the pool and place them into the blank lines in the playground. You may not use the same line more than once, and you won’t need to use all the lines Your goal is to make the code that will generate the output shown below, given the starting variables:
each thing from the pool can only be used once!
Exercise
Each of the Swift code snippets on this page represents a complete playground. Your job is to play Swift compiler, and determine whether each of these will run or not. If they will not compile, how would you fix them? What’s wrong with them if they won’t work?.
A
let dogsAge = 10 let dogsName = "Trevor" print("My dog's name is (dogsName) and they are (dogsAge) years old.") dogsAge = dogsAge + 1
B
var number = 10 for i in 1...number { print(number*92.7) }
C
var bestNumbers: Set = [7, 42, 109, 53, 12, 17] bestNumbers.remove(7) bestNumbers.remove(109) bestNumbers.remove(242) bestNumbers.insert(907) bestNumbers.insert(1002) bestNumbers.insert(42)
It’s time to test your brain. Gently.
This is just a normal crossword, but all the solutions are from concepts we covered in this chapter. How much did you pay attention?
Across
4. Related functionality and values
5. An ordered collection of values of the same type
7. A named piece of data that can never change
9. A named piece of repeatable code
13. Mixing values with a string
14. Checking what’s inside an optional
15. This statement helps you run different code depending on the value of a variable (amongst other things)
16. A type that can store positive or negative
17. A way to execute some code over and over again
18. Something to check, combine, or change a value
Down
1. A group of related values
2. A decimal number
3. A place to code Swift without the clutter of an IDE
6. A type that can store some words. Or a word.
7. A way to store groups of values
8. An unordered collection of values of the same type
10. A named piece of data that can change later
11. A collection of values mapped to other values
12. Representing a value, as well as the potential absence of a value
13. A whole number
Exercise Solution
class Dog { var name: String var color: DogColor var age: Int init(name: String, color: DogColor, age: Int) { self.name = name self.color = color self.age = age } } enum DogColor { case red case brown case black } var fido = Dog(name: "Fido", color: .brown, age: 7) var bruce = Dog(name: "Bruce", color: .black, age: 4) var moose = Dog(name: "Moose", color: .red, age: 11) var pack: [Dog] = [fido, bruce] func addDogToPack(dog: Dog) { pack.append(dog) print("(dog.name) (aged (dog.age)) has joined the pack.") } func listDogsInPack() { print("The pack is:") print("--") for dog in pack { print(dog.name) } print("--") } listDogsInPack() addDogToPack(dog: moose) listDogsInPack()
Exercise Solution
var todaysWeather = "Windy" var temperature = 35 var message = "Today's Weather" switch todaysWeather { case "Sunny": message = "It's a lovely sunny day!" case "Windy": message = "Strap your hat on. It's windy!" case "Raining": message = "Pack your umbrella!" case "Snow": message = "Brr! There's snow in the air!" default: message = "It's a day, you know?" } if(temperature > 65) { message += " And it's not cold out there." } else if(temperature < 35) { message += " And it's chilly out there." } else { message += " And it's not cold or hot!" } print(message)
A
The code will error because we’re trying to modify a constant (dogsAge). We need to change the dogsAge constant to a variable for this to work.
B
This works fine, exactly as it is.
C
The code will error when you try and multiply a double (92.7) by an integer (number). Additionally, we’re not using the i that’s counting up inside the loop at all.
Exercise Solution
REQUIRED LEFT-HAND PAGE FOR ENDING CHAPTER. INTENTIONALLY BLANK.
18.220.160.216