Mutable/immutable collections
One rather important discussion that we've left out is how to subtract, edit, or add to arrays, sets, and dictionaries. However, before we do this, you should understand the concept of mutable and immutable data/collections.
A mutable collection is simple data that can be changed, added to, or subtracted from, whereas an immutable collection cannot be changed, added to, or subtracted from.
To work with mutable and immutable collections efficiently in Objective-C, we had to explicitly state the mutability of the collection beforehand. For example, an array of the type NSArray
in Objective-C is always immutable. There are methods we can call on NSArray
that would edit the collection, but behind the scenes, this would be creating brand new NSArray
objects, thus would be rather inefficient to do this often in the life of our game. Objective-C has solved this issue with the class type, NSMutableArray
.
Thanks to the flexibility of Swift's type inference, we already know how to make a collection mutable or immutable! The concept of constants and variables has us covered when it comes to data mutability in Swift. Using the keyword let
when creating a collection will make that collection immutable, while using var
will initialize it as a mutable collection.
//mutable Array var unlockedLevels : [Int] = [1, 2, 5, 8] //immutable Dictionary let playersForThisRound : [PlayerNumber:PlayerUserName] = [453:"userName3344xx5", 233:"princeTrunks", 6567: "noScopeMan98", 211: "egoDino"]
The array of integers, unlockedLevels
, can be edited simply because it's a variable. The immutable dictionary playersForThisRound
can't be changed since it's already been declared as a constant. There is no additional layer of ambiguity concerning additional class types.
Editing/accessing collection data
As long as a collection type is a variable, using the var
keyword, we can do various edits to the data. Let's go back to our unlockedLevels
array. Many games have the functionality of unlocking levels as the player progresses. Let's say that the player has reached the high score needed to unlock the previously locked level 3 (as 3
isn't a member of the array). We can add 3
to the array using the append
function:
unlockedLevels.append(3)
Another neat attribute of Swift is that we can add data to an array using the +=
assignment operator:
unlockedLevels += [3]
Doing it this way however will simply add 3
to the end of the array. So, our previous array [1, 2, 5, 8]
is now [1, 2, 5, 8, 3]
. This probably isn't a desirable order, so to insert the number 3
in the third spot, unlockedLevels[2]
, we can use the following method:
unlockedLevels.insert(3, atIndex: 2)
Now, our array of unlocked levels is ordered to [1, 2, 3, 5, 8]
.
This is assuming though that we know a member of the array prior to 3
is sorted already. There are various sorting functionalities provided by Swift that could help keeping an array sorted. We will leave the details of sorting to our discussions of loops and control flow later in this chapter.
Removing items from an array is just simple. Let's again use our unlockedLevels
array. Imagine that our game has an overworld for the player to travel to and from and the player has just unlocked a secret that triggered an event that blocked off access to level 1. Level 1 would now have to be removed from the unlocked levels. We can do it like this:
unlockedLevels.removeAtIndex(0) // array is now [2, 3, 5, 8]
Alternately, imagine that the player has lost all of their lives and got a Game Over message. A penalty for this could be to lock the furthest level. Though probably a rather infuriating method and us knowing that level 8 is the furthest level in our array, we can remove it using the .removeLast()
function of array types.
unlockedLevels.removeLast() // array is now [2,3,5]
Note
This is assuming that we know the exact order of the collection. Sets or dictionaries might be better at controlling certain aspects of your game.
Here are some ways to edit a set or a dictionary as a quick guide.
Set
inventory.insert("Power Ring") //.insert() adds items to a set inventory.remove("Magic Potion") //.remove() removes a specific item inventory.count //counts # of items in the Set inventory.union(EnemyLoot) //combines two Sets inventory.removeAll() //removes everything from the Set inventory.isEmpty //returns true
Dictionary
var inventory = [Float : String]() //creates a mutable dictionary /* one way to set an equipped weapon in a game; where 1.0 could represent the first "item slot" that would be placeholder for the player's "current weapon" */ inventory.updateValue("Broadsword", forKey: 1.0) //removes an item from a Dictionary based on the key value inventory.removeValueForKey("StatusBooster") inventory.count //counts items in the Dictionary inventory.removeAll(keepCapacity: false) //deletes the Dictionary inventory.isEmpty //returns false //creates an array of the Dictionary's values let inventoryNames = [String](inventory.values) //creates an array of the Dictionary's keys let inventoryKeys = [String](inventory.keys)
Iterating through collection types
We can't discuss collection types without mentioning how to iterate through them en masse.
Here's some way we'd iterate though an array, a set, or a dictionary in Swift:
//(a) outputs every item through the entire collection //works for Arrays, Sets and Dictionaries but output will vary for item in inventory { print(item) } //(b) outputs sorted item list using Swift's sorted() function //works for Sets for item in sorted(inventory) { print("\(item)") } //(c) outputs every item as well as its current index //works for Arrays, Sets and Dictionaries for (index, value) in enumerate(inventory) { print("Item \(index + 1): \(value)") } //(d) //Iterate through and through the keys of a Dictionary for itemCode in inventory.keys { print("Item code: \(itemCode)") } //(e) //Iterate through and through the values of a Dictionary for itemName in inventory.values { print("Item name: \(itemName)") }
As stated previously, this is done with what's known as a for-loop; with these examples, we show how Swift utilizes the for-in variation using the in
keyword. The code will repeat until it reaches the end of the collection in all of these examples. In example (c)
, we also see the use of the Swift function, enumerate()
. This function returns a compound value, (index,value)
, for each item. This compound value is known as a tuple, and Swift's use of tuples makes for a wide variety of functionalities for functions, loops, as well as code blocks.
We will delve more into tuples, loops, and blocks later on.