Arrays, dictionaries and sets make up the three main collection types in Swift. We’ve already talked about Arrays in a previous post, and in this article, we’re going to move on and look at the second of these collection types: Dictionaries.
Table of Contents
What is a Dictionary?
In Swift, dictionaries are unordered collections that store multiple key-value pairs with each value being accessed via a corresponding unique key.
As with the other Swift collection types, dictionaries in Swift are strongly typed. This means that we have to specify individual types for both the keys and the values. All the keys in a dictionary must be of the same type and similarly, all the values must also be of the same type (though not necessarily the same type as the keys).
Beyond this rule, the only caveat we have when declaring dictionaries is that the keys in a dictionary must be unique and must conform to Swifts Hashable protocol. We’ve not covered protocols yet but essentially this means that any type you select for the key, must provide an Int
property called hashValue
that can be used to represent the key.
Dictionary Mutability
As with most other constants and variables in Swift, the mutability of a dictionary (the ability to change or mutate it’s contents) is governed by whether you assign the dictionary to a constant or variable.
If you assign the dictionary to a constant (i.e. declare it using the let
keyword), the dictionary is immutable and both its size and contents cannot be changed. If however you assign the dictionary to a variable (i.e. declare it with using the var
keyword), you can mutate the dictionary; adding, removing or modifying the contents of the dictionary to your hearts content.
Given this, let’s look at how we actually declare one.
How to Declare a Dictionary
Before we start, we’re going to need some sort of example. For this I’ve chosen to dive into the land of Star Wars and initially create a dictionary that holds the heights of each of the characters in the original Star Wars films (yeah I know, slightly strange but go with it for now).
For each character, we’ll look up the characters height using their name as the key (a String
) and store their height as the corresponding value (a Double
).
As with a lot of things in Swift, there are a number of ways we could declare this dictionary. Firstly, if you’re feeling particularly formal, there is the full declaration:
var filmCast1 : Dictionary
Let’s look at it piece by piece.
Like any other variable we use the var
keyword to declare the variable (as a result, the dictionary will be mutable) followed by the name of the variable, a colon and then the variables type.
In the case of dictionaries the variable type is the interesting bit.
Under the hood dictionaries in Swift are actually an example of a generic. I don’t want to go into generics just now but if you’ve never come across them, just think of them as a type that accepts one or more other types as parameters.
We can see this here. In this case, the Dictionary
type (our generic), accepts two other types (String
and Double
) as parameters separated by a comma and specified between a pair of angled brackets. Collectively, the Dictionary
type, the angled brackets and the type parameters make up the complete type so in this case the filmCast1
variable is actually of type Dictionary<String, Double>
.
Now, if all these angled brackets and talk of generics is a bit much for you there is some good news. In Swift, we have an alternative, short-hand syntax that we can use to specify the types of keys and values a dictionary will hold.
In this short-hand syntax, we use a pair of square brackets and write the two types, (one for the keys and one for the values) between the square brackets separating them with a colon. For example, the following is semantically identical to the declaration above but it uses this short-hand syntax instead:
var filmCast2 : [String : Double]
Most of the time it’s this latter form that you’re going to see when you read other peoples Swift code. It’s shorter, more concise and clearer to read so although it’s important to recognise and understand the longer syntax above, my recommendation would be to adopt this latter in our own code.
So, with that covered, let’s look at initialising the dictionary itself. We’re going to start by looking at how to create the simplest dictionary possible, an empty one.
Initialising an Empty Dictionary
As with a declaring dictionaries there are a couple of ways in which we can initialise dictionaries in Swift. The first is something called initializer syntax.
Initialising an Empty Dictionary Using Initialiser Syntax
I know this sounds fancy but all initialiser syntax really means is that we make use of an initialization function (which is is built into the Dictionary type) to construct and return a Dictionary instance.
When it comes to initiation syntax, there are a couple of choices with regard to the exact syntax we use.
The first option is to use the full type specification syntax. It’s similar to the syntax we saw just now:
let empty1 : Dictionary<String, Double> = Dictionary<String, Double>() // Full syntax
But its far more common to see the short-hand syntax being used:
let empty2 : [String : Double] = [String : Double]() // Shorthand syntax
In both cases, we use the parentheses on the right of the assignment operator to call the initialisation function for the specified type.
Now, although shorter, this is still relatively verbose and I’ve included them on purpose so you know what a full declaration looks like. However, we can (and 9 times out of 10 should) simplify things even further.
As you may know from my previous posts, Swift has an inbuilt type inference mechanism that can infer the type for a variable or constant from the values that are initially assign to them. This same mechanism also works with dictionaries in Swift and we can use it to shorten our declarations even further:
let empty3 = Dictionary<String, Double>()
let empty4 = [String: Double]()
Better but we’re not done. In addition to using the initialiser syntax and type inference to initialise dictionaries we can also have the option of using dictionary literals.
Initialising an Empty Dictionary Using Dictionary Literals
Dictionary literals are lists of key-value pairs surrounded by square brackets, where each pair is separated from the next by a comma and each key is separated from it’s corresponding value with a colon.
The simplest dictionary literal we can use is the empty dictionary literal. The empty dictionary literal is written as a pair of square brackets with a colon between them ([:]
) and being an empty dictionary obviously contains no keys or values:
var empty5 : [String: Double] = [:]
As a side note, notice here that I’ve had to include a type annotation for the empty5
constant. This might seem strange (given that I just mentioned in the previous section that we can make use of type inference to usually drop them) but when we use the empty dictionary literal ([:]
) Swift has no other way of inferring the types for the keys and values that will be stored in the dictionary so in this specific case we have to include the annotation in order to give the compiler a hint of what’s going to be stored. Anyway, lets keep going.
Initialising Dictionaries with Initial Items
So far, we’ve looked at how to declare and initialise empty dictionaries and it’s about time we looked at how to initialise dictionaries that actually contain some values. In reality, it’s not a great leap from what we’ve already seen.
First, we have the initialiser syntax we looked at. In addition to dictionaries having an initialisation function that accepts no parameters, it also has an initialisation function that accepts a list of tuples. We’ve looked at tuples in a previous post. In this case, each tuple has two elements, one representing the key and one representing the value. For example, in the following example, I’m declaring a dictionary to hold the details of the Star Wars robots:
var rebelDroids = Dictionary<String, Double>(dictionaryLiteral: ("C-3PO", 2.03), ("R2-D2", 1.06))
// Results in: ["C-3PO": 2.03, "R2-D2": 1.06]
However, as we’ve seen this full syntax is a chunk of typing and it’s much more common to type inference and let Swift infer the the types:
var galacticEmpire = ["Darth Vader" : 2.03, "Darth Maul" : 1.75]
// Results in: ["Darth Vader": 2.03, "Darth Maul": 1.75]
And that’s it. That’s pretty much all there is to initialising dictionaries. Now we’ve got the hang of that, let’s take a look at some of the things we can actually do with dictionaries. We’ll start by looking at how to determine the number of elements that are stored in a dictionary.
Counting the Number of Items in a Dictionary
Checking If a Dictionary Is Empty
Ok. Let’s kick things off by looking at empty dictionaries. We’ve already talked about how to initialise an empty dictionary but how do we check whether a dictionary is empty? In Swift, this is super easy. Inbuilt to the Dictionary type in Swift is the isEmpty
property. This property returns a boolean value to indicate whether the dictionary is empty or not:
// galacticEmpire contains: ["Darth Vader" : 2.03, "Darth Maul" : 1.75]
galacticEmpire.isEmpty // returns false
empty1.isEmpty //returns true
Finding The Number of Items in a Dictionary
In addition to checking whether a dictionary is empty, we can also check for the exact number of elements that are currently in the dictionary by using the count
property. The count
property returns the number of element in the dictionary and (fairly obviously) returns zero if the dictionary is empty:
galacticEmpire.count // returns 2
empty1.count // returns 0
How to Access an Item in a Dictionary
Now that we know how to check whether our dictionaries actually contain some values, the next logical step is to look at how to access those values.
To access specific elements within a dictionary in Swift we use subscript syntax.
The subscript syntax for dictionaries is almost identical to the syntax we saw with arrays but with dictionaries, instead of using a set of square brackets and putting the index of the element we want to access within the brackets, we instead use a key within the brackets:
var droidHeight = rebelDroids["C-3PO"]
print(droidHeight) // prints 2.03
Also, the interesting thing here is the type of value returned. If you’re following along in a Playground and option+click
on the droidHeight
variable, you’ll see that droidHeight
is inferred to be of type Double?
. If you’ve read my Optionals post you’ll recognise this as an optional. So why is this?
Well, as we’ve seen when we attempt to access a value in a dictionary we use a key to indicate the value we want to access. That all works well if the key is actually in the dictionary but what if it isn’t? What if the dictionary doesn’t recognise the key?
Well, in that case, instead of returning the value we are looking for, Swift actually returns a value of nil
to indicate that it didn’t find anything.
Think about it. If the key doesn’t exist, what else could it return? nil
is a fairly logical response in this case.
Given this, by Swift setting the return type be an optional instead of a normal type it is able to return either nil
if a value doesn’t exist or the actual value if one does.
So given that we could potentially get a value of nil
when accessing a value in a dictionary, we really need to check that an actual value was returned. There are two ways we can do this. The first is a simple boolean check to check whether accessing the dictionary using a given key returns nil
:
rebelDroids["C-3PO"] == nil // Returns false
rebelDroids["BB-8"] == nil // Returns true
We can also make use optional binding to automatically unwrap the returned value and conditionally execute some code if the value is not nil
:
if let droidHeight = rebelDroids["C-3PO"]
{
print(droidHeight) // prints 2.03
}
Checkout my post on optionals if you’re not familiar with optional binding.
Dictionaries Keys That Reference Optional Values
Note: This section is a bit more of an advanced topic so feel free to skip over it if you’re just starting out.
Here is a bit of a puzzler though. What if we took things one step further? What if the values we were storing in the dictionary were themselves optionals?
Consider this. Say I had a dictionary of rebels that mapped the rebel to their weapon of choice:
var rebelWeapons : [String: String?] = ["Luke Skywalker" : "Lightsaber",
"C-3PO" : nil, "Princess Leia" : nil]
Notice that I’ve defined the type for the values in the dictionary to be an optional string (String?
). Luke Skywalker obviously has his Lightsaber but C-3PO doesn’t really use a weapon. By making the type for the dictionary values an optional we can represent the fact that some rebels have a preferred weapon and some don’t.
Now, let’s take a look at what happens if we try to access the dictionary using various keys and then compare the results to nil
. First, Luke Skywalker:
rebelWeapons["Luke Skywalker"] == nil // returns false
OK, all good. Just as we expected, Luke has a preference for a lightsaber which is returned and the boolean expression returns false
as the result isn’t nil
.
Next. What if we tried to access the details of a rebel that wasn’t in the dictionary at all:
rebelWeapons["Lando Calrissian"] == nil // returns true
Two for two. Just as expected, the key wasn’t found in the dictionary therefore we don’t know what their weapon preference is, we get a return value of nil
and the boolean expression evaluates to true
. Great.
Now here is the interesting bit. What if we tried to access the weapons for C-3PO? The key is in the dictionary, but he has no weapon preference so we should get a value of nil
and our test against nil
should return true
right?:
rebelWeapons["C-3PO"] == nil // returns false
What? That wasn’t quite what we were expecting. What’s going on?
Well, under the hood, Optionals are actually a generic enumeration with two possible values:
enum Optional {
case None
case Some(T)
}
Remember generics? In this case, our generic type Optional
accepts a single type as a parameter which it then uses within the body of the enumeration.
In reality then, an optional is actually one of two values. Either it has no value (represented by an enumeration value of .None
) or it has a value (represented by an enumeration value of .Some
).
When set to .Some
it also makes use of the fact that enumerations in Swift can have associated values and stores the actual value (a value of the parameter type (T
)).
For example, say we had an optional constant or variable of type String?
. That optional could have a value that was either .None
or .Some(String)
where the string is the actual string value. We can try it for ourselves:
var stringOptional : String? = .Some("Hello World")
stringOptional == nil // returns false
We can also assign the optional a value of .None
:
stringOptional = .None
stringOptional == nil // returns true
Ok. Relatively straightforward.
Where things get a little more confusing with our dictionary example is that the value returned from the dictionary is also an optional. This results in a return type that is actually an optional optional. See for yourself:
var c3poWeapon = rebelWeapons["C-3PO"]
// c3poWeapon is inferred to be of type String??
An optional optional? What the heck is that? Well there are actually two things going on here.
Firstly, Swift is checking for whether the key exists in the dictionary or not. We’re already seen that this returns an optional in our previous examples and we know that optionals are represented by a data type followed by a question mark. But we’re not done.
Due to the fact that we’re also storing optional strings for the values in the dictionary the value returned from the dictionary is also an optional. So it’s an optional (which we already know is a type of generic enumeration) parameterised with an optional (again, a type of generic enumeration). Essentially then its a kind of an optional wrapped inside an optional. It’s pretty mind bending but let’s work through the example and see if makes things any clearer.
In the case of the key C-3PO
, Swift first checks to see if that particular key exists within the dictionary. In the case it does, so the result of this initial check phase works out to an enumeration value of .Some
.
At this point though, the associated value stored by the .Some
case is itself an optional (String?
). That value can therefore also be either .None
or .Some
.
In the case of C-3PO, he has no associated weapon (represented by nil
) so it is set to the corresponding enumeration value of .None
.
The final result then is a value of .Some(.None)
.
So we’re nearly there.
If you look at the result, you might already be able to work out why our check against nil
was giving us a strange result.
What’s actually happening in our boolean check is that we are comparing the outer value against nil
rather than the inner one (remember that .None
and nil
are essentially interchangeable). In order to get at the inner value we need to first unwrap the outer one:
// Unwrap whether the key existed or not....
if let weapon = rebelWeapons["C-3PO"] {
// Unwrap whether the weapon existed or not...
if let weapon = weapon {
print("Weapon is \(weapon)")
} else {
print("No weapon")
}
} else {
print("This rebel is a new to the Alliance")
}
// prints "No weapon"
Clearer? Don’t worry if you don’t get your head around it. It’s an edge case pretty mind bending. I might try to touch on it again in a future post if I can come up with a clearer explanation. For now though, let’s move on with some simpler topics and look at how we can modify the elements in a dictionary.
How to Modify an Item in a Dictionary
We’ve already seen how to access an element in a dictionary and changing one is also pretty simple.
To change an element in a dictionary, instead of using the dictionary name and subscript to the right of an assignment operator, (such as when assigning to a variable or constant), we actually use it to the left:
rebelWeapons["Princess Leia"] = "Pistol"
// ["C-3PO": nil, "Luke Skywalker": Optional("Light Saber"), "Princess Leia": Optional("Pistol")]
We can also use a specific method available on dictionaries called updateValue(_:forKey:)
to achieve the same thing. When using this method, the outcome is the same but the method also returns the value that was replaced in the dictionary (the subscript form simply returns void
):
// rebelWeapons is currently: ["C-3PO": nil, "Luke Skywalker": Optional("Light Saber"), "Princess Leia": Optional("Pistol")]
var oldWeapon = rebelWeapons.updateValue("Blaster Pistol", forKey: "Princess Leia")
print(oldWeapon) // prints "Pistol"
// rebelWeapons is now: ["C-3PO": nil, "Luke Skywalker": Optional("Light Saber"), "Princess Leia": Optional("Blaster Pistol")]
The main reason for using updateValue(_:forKey:)
method instead of subscripts is if you want to do something with the value that was replaced.
How to Add a Dictionary Item
When it comes to adding new items to a Dictionary, the good news is that there is no new syntax to learn. In the previous section, we learnt about how to update a value that was already in the dictionary and saw how we needed to supply the key for the value we wanted to update. To add a value we use exactly the same syntax except we supply a key that doesn’t already exist:
rebelWeapons["Han Solo"] = "Blaster Pistol"
// ["C-3PO": nil, "Luke Skywalker": Optional("Light Saber"), "Princess Leia": Optional("Blaster Pistol"), "Han Solo" : Optional("Blaster Pistol")]
In this case, Swift will search for the key, find it doesn’t exist and add the new value for us.
The same applies to the updateValue(_:forKey:)
method:
rebelWeapons.updateValue("Bowcaster", forKey: "Chewbacca")
// ["Chewbacca" : Optional("Bowcaster"), "C-3PO": nil, "Luke Skywalker": Optional("Light Saber"), "Princess Leia": Optional("Blaster Pistol"), "Han Solo" : Optional("Blaster Pistol")]
So, we can now update and add elements to a dictionary. The next thing to do is work out how to remove them. Let’s look at that next.
Removing Items from a Dictionary
We have two options when it comes to removing elements from a dictionary and which option we choose depends on whether we want to remove just a single element or the entire contents of the dictionary. Let’s look at each in turn.
How to Remove a Single Dictionary Item
Removing a single element from a dictionary in Swift is relatively easy. We know how to update an element and the syntax to remove an element is identical except for the fact that instead of assigning a new value, we assign a value of nil
. Assigning nil
results in the element being removed from the dictionary:
rebelWeapons["C-3PO"] = nil
// ["Chewbacca" : Optional("Bowcaster"), "Luke Skywalker": Optional("Light Saber"), "Princess Leia": Optional("Blaster Pistol"), "Han Solo" : Optional("Blaster Pistol")]
Ok, pretty simple, but it does throw up a little wrinkle in the case of our advanced example (again, skip the next section if you’re not interested).
Adding nil
Values for Keys That Reference Optional Values
Remember, in the case of our advanced example, the values in our dictionary are of type String?
. Being optionals, it’s perfectly valid to assign them a value of nil
. However, if assigning a value of nil
removes the value from the dictionary what do we do? We want to keep the value in the dictionary but want to assign the value nil
?
Well, remember he optional enumeration we talked about earlier? In the case of the situation where we want to keep the key in the dictionary but assign a value of nil
we have to fall back to using the enumeration values directly rather than assigning nil
:
rebelWeapons["R2-D2"] = .Some(.None)
// ["R2-D2" : nil, "Chewbacca" : Optional("Bowcaster"), "Luke Skywalker": Optional("Light Saber"), "Princess Leia": Optional("Blaster Pistol"), "Han Solo" : Optional("Blaster Pistol")]
As a side note, we can also remove the element from the dictionary using this same technique. All we have to do is assign an outer value of .None
:
rebelWeapons["R2-D2"] = .None
// ["Chewbacca" : Optional("Bowcaster"), "Luke Skywalker": Optional("Light Saber"), "Princess Leia": Optional("Blaster Pistol"), "Han Solo" : Optional("Blaster Pistol")]
Again, both of these are advanced cases but they’re worth knowing about.
Removing All Dictionary Items
Anyway, our dictionary is getting pretty long by now so what if we wanted to remove all the elements? The good news here is that we don’t have to do it one by one, instead we can make use the removeAll()
method. This method (as you might guess) removes all the elements from the dictionary on which it is called and results in an empty dictionary:
rebelWeapons.removeAll()
// rebelWeapons is now [:]
Iterating Over a Dictionary
Right then. We’ve looked at all the basic things we can do with a dictionary; adding, accessing, modifying and removing elements but there is one thing we haven’t touched on yet and that is how we iterate over the contents of a dictionary. You’ll find that this is relatively common, so we’ll go over it now.
As I mentioned way back in the introduction, unlike arrays, dictionaries in Swift are unordered collections. This means that unlike arrays, the values in a Dictionary are not held in a particular order and when we iterate over the contents of a dictionary there is no guarantee about the order in which the values will be returned. There are ways around this (which we’ll touch on shortly), but in the mean time, let’s take a look the basics.
The main mechanism for iterating over the contents of a dictionary in Swift is the for..in
loop. With the for..in
loop, we can iterate over either the dictionaries keys, values, or both keys and values depending on the arguments we supply to loop.
Let’s start by looking at iterating over the dictionaries values.
Iterating Over a Dictionaries Values
In this example, we’re going to re-visit our galacticEmpire
dictionary from earlier which contained various characters and their associated heights.
Say we wanted to come up with a new galacticEmpire clothing line and wanted to iterate over the dictionary and print out the heights of each character. Build into the Swift dictionary type is a property called values. This returns a collection that contains each of the values from the dictionary. We can pass this collection into a for..in
loop which will then make each value available within the body of the loop as a constant:
for value in galacticEmpire.values {
print("Value: \(value)")
}
// Prints:
// Value: 2.03
// Value: 1.75
Ok. Good start. But it would be a bit nicer if we knew which height related to which character. Let’s look at iterating over the keys next.
Iterating Over a Dictionaries Keys
In similar fashion to the values
property, built into the Swift dictionary type is a second property called keys. As with the value
property, the keys
property returns a LazyMapCollection
which we can also pass into the for..in
loop to iterate over its contents. Again, the keys are made available as constants within the body of the for..in
loop (so there’s no changing them) but this time we can use the keys to lookup the corresponding values.
When you’re doing this, remember though that when we use a key to look up a value in a dictionary, the value returned is an optional. In this particular situation though, we can safely use forced unwrapping to get at the value as we know that the key exists (otherwise it would never be iterated over):
for key in galacticEmpire.keys {
print("Key: \(key) Value: \(galacticEmpire[key]!)")
}
// Prints:
// Key: Darth Vader Value: 2.03
// Key: Darth Maul Value: 1.75
Great. Much better. We can now see which character was which height but looking the values up inside the body of the loop is probably more work than we should be really doing. What if we could iterate over both the keys and the values?
Iterating Over a Dictionaries Keys and Values
The final way to iterate over the contents of the dictionary involves iterating over both the keys and values. This time, if we simply supply the dictionary to the for..in
loop, each key value pair is returned as a tuple. If you’ve read my previous post on Tuples you’ll already be aware that we can also name individual elements within a tuple and we can make use of this fact here to directly extract the key and value for each element in the dictionary:
for (key, value) in galacticEmpire {
print("Key: \(key) Value: \(value)")
}
// Prints
// Key: Darth Vader Value: 2.03
// Key: Darth Maul Value: 1.75
The output is exactly the same as the last example, but this time, we pull out each key / value pair directly and don’t need a second lookup within the body of the loop.
Ok. So we’ve got the basics of iterating over a dictionary covered, but I mentioned previously that dictionaries were unordered collections and as such, there was no guarantee about the order in which the values would be returned. But what if we did want to iterate over the contents of the dictionary in a particular order? Let’s look at that next.
Iterating Over a Dictionary in a Particular Order
We’ve already looked at how we can retrieve either the values, the keys or both the values and keys from a dictionary and we’ve seen how we can supply these collections to the for..in
loop to iterate over their contents. The key to iterating over the contents in a particular order is to sort the resulting collections before we supply them to the for..in
loop. The good news is that this is not actually as complicated as you may think.
Built into the Swift collection types is the sort()
method. The sort method can be called on any sequence of elements in Swift and it will return an Array containing the sorted elements. The elements in the dictionary are sorted using the (<
) operator so in the case of numeric values, the smaller values will be listed first and the larger ones will be listed later. For example, say we wanted to iterate over our galacticEmpire
dictionary from the shortest character to the tallest character. We could do that as follows:
for value in galacticEmpire.values.sort() {
print(value)
}
// Prints
// 1.75
// 2.03
We can also sort them in the reverse order by (unsurprisingly) making use of the reverse()
method that is available on arrays:
for value in galacticEmpire.values.sort().reverse() {
print(value)
}
// Prints
// 2.03
// 1.75
If you want to take things a step further and iterate over the elements in the dictionary in some bespoke order, there is also an alternative form of the sort()
method (sort(_:)
) which accepts a closure as a parameter.
Note: Closures are another thing we’ve not covered yet. For now think of them as an un-named function. They accept parameters and have an associated block of code that is executed when the closure is called. We’ll go into closures in more detail some time in the future.
Anyway, in this case the closure itself accepts two parameters of the same type (the types of values you’re sorting) and returns a boolean value to indicate whether the first parameter should be sorted before the second parameter. I’m not doing to go into it now as it’s an advanced case and we’ve not covered closures yet, but it does allow you to iterate over the contents of the dictionary in a custom order. Check out the documentation on the SequenceType protocol for more details.
Dictionaries are Value Types
Ok, we’re on the home stretch but there one more thing I wanted to cover before we wrap up. Primarily about the nature of dictionaries themselves.
If you have any experience in Object-Oriented programming, you may already be familiar with the concept of value types and reference types. As a quick reminder, in Swift, value types are those types that have a unique copy of their contained values where as reference types are those types that share a single copy of their data. These latter reference types, are usually defined as classes in Swift.
The key thing I wanted to point out here, is that dictionaries in Swift fall into the first of these camps and are actually value types. This means that when you assign a dictionary from one variable or constant to another, you are actually making a copy of that dictionary rather than creating a reference to the same dictionary. We can see this in the following example:
var rebels = ["Luke Skywalker" : "Light Saber"]
print(rebels)
var rebelClones = rebels
rebelClones["Yoda"] = "Light Saber"
print(rebels) // ["Luke Skywalker": "Light Saber"]
print(rebelClones) // ["Luke Skywalker": "Light Saber", "Yoda": "Light Saber"]
Notice that after we modify the rebelClones
dictionary, the two dictionaries actually contain different values. In reality they are two separate instances not simply two references to the same instance. Just keep this in mind as it can sometimes catch you out.
Anyway, that about wraps it up for dictionaries in Swift. It’s another long post but hopefully you should have a much better understanding of dictionaries now as well as a bit of behind the scenes knowledge of how optionals work. As ever, if you have any comments, questions or if you notice any mistakes, please get in touch.