Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Swift Cookbook

You're reading from   Swift Cookbook Over 60 proven recipes for developing better iOS applications with Swift 5.3

Arrow left icon
Product type Paperback
Published in Feb 2021
Publisher Packt
ISBN-13 9781839211195
Length 500 pages
Edition 2nd Edition
Languages
Tools
Arrow right icon
Authors (3):
Arrow left icon
Chris Barker Chris Barker
Author Profile Icon Chris Barker
Chris Barker
Keith D. Moon Keith D. Moon
Author Profile Icon Keith D. Moon
Keith D. Moon
Keith Moon Keith Moon
Author Profile Icon Keith Moon
Keith Moon
Arrow right icon
View More author details
Toc

Table of Contents (14) Chapters Close

Preface 1. Swift Building Blocks 2. Mastering the Building Blocks FREE CHAPTER 3. Data Wrangling with Swift Control Flow 4. Generics, Operators, and Nested Types 5. Beyond the Standard Library 6. Building iOS Apps with Swift 7. Swift Playgrounds 8. Server-Side Swift 9. Performance and Responsiveness in Swift 10. SwiftUI and Combine Framework 11. Using CoreML and Vision in Swift 12. About Packt 13. Other Books You May Enjoy

Ordering your data with arrays

So far in this book, we have learned about many different Swift constructs: classes, structs, enums, closures, protocols, and tuples. However, it is rare to deal with just one instance of these on their own. Often, we will have many of these constructs, and we need a way to collect multiple instances and place them in useful data structures. Over the next few recipes, we will examine three collection data structures provided by Swift; that is, arrays, sets, and dictionaries (dictionaries are often called hash tables in other programming languages):

Figure 2.1 – Collection of data structures

While doing this, we will look at how to use them to store and access information, and then examine their relative characteristics.

Getting started

First, let's investigate arrays, which are ordered lists of elements. We won't be using any components from the previous recipes, so you can create a new playground for this recipe.

How to do it...

Let's use an array to organize a list of movies to watch:

  1. Create an array called moviesToWatch. This will hold our strings:
var moviesToWatch: Array<String> = Array()
  1. Append three movies to the end of our movie list array:
moviesToWatch.append("The Shawshank Redemption") 
moviesToWatch.append("Ghostbusters")
moviesToWatch.append("Terminator 2")
  1. Print the names of each movie in the list, in turn:
print(moviesToWatch[0]) // "The Shawshank Redemption"
print(moviesToWatch[1]) // "Ghostbusters"
print(moviesToWatch[2]) // "Terminator 2"
  1. Print a count of the number of movies in the list so far:
print(moviesToWatch.count) // 3
  1. Insert a new movie into the list so that it's the third one in it. Since arrays are zero-based, this is done at index 2:
moviesToWatch.insert("The Matrix", at: 2)
  1. Print the list count to check it has increased by one and print the newly updated list:
print(moviesToWatch.count) // 4
print(moviesToWatch)
// The Shawshank Redemption
// Ghostbusters
// The Matrix
// Terminator 2
  1. Use the first and last array properties to access their respective values and print them:
let firstMovieToWatch = moviesToWatch.first 
print(firstMovieToWatch as Any) // Optional("The Shawshank
Redemption")
let lastMovieToWatch = moviesToWatch.last
print(lastMovieToWatch as Any) // Optional("Terminator 2")
  1. Use an index subscript to access the second movie in the list and print it. Then, set a new value to that same subscript. Once you've done that, print the list count to check the number of movies that haven't changed and print the list to check that the second array element has changed:
let secondMovieToWatch = moviesToWatch[1] 
print(secondMovieToWatch) // "Ghostbusters"
moviesToWatch[1] = "Ghostbusters (1984)"
print(moviesToWatch.count) // 4
print(moviesToWatch)
// The Shawshank Redemption
// Ghostbusters (1984)
// The Matrix
// Terminator 2
  1. Create a new array of spy movies by initializing it with some movies using the array literal syntax:
let spyMovieSuggestions: [String] = ["The Bourne Identity", 
"Casino Royale",
"Mission Impossible"]
  1. Combine the two arrays we have created using the addition operator (+) and assign them back to the moviesToWatch variable. Then, print the array count so that it reflects the two lists combined and print the new list:
moviesToWatch = moviesToWatch + spyMovieSuggestions 
print(moviesToWatch.count) // 7
print(moviesToWatch)
// The Shawshank Redemption
// Ghostbusters (1984)
// The Matrix
// Terminator 2
// The Bourne Identity
// Casino Royale
// Mission Impossible
  1. Next, use an array convenience initializer to create an array that contains three entries that are the same. Then, update each array element so that the rest of their movie titles are shown:
var starWarsTrilogy = Array<String>(repeating: "Star Wars: ", 
count: 3)
starWarsTrilogy[0] = starWarsTrilogy[0] + "A New Hope"
starWarsTrilogy[1] = starWarsTrilogy[1] + "Empire Strikes Back"
starWarsTrilogy[2] = starWarsTrilogy[2] + "Return of the Jedi"
print(starWarsTrilogy)
// Star Wars: A New Hope
// Star Wars: Empire Strikes Back
// Star Wars: Return of the Jedi
  1. Let's replace part of our existing movie list with our starWarsTrilogy list, and then print the count and list:
moviesToWatch.replaceSubrange(2...4, with: starWarsTrilogy) 
print(moviesToWatch.count) // 7
print(moviesToWatch)
// The Shawshank Redemption
// Ghostbusters (1984)
// Star Wars: A New Hope
// Star Wars: Empire Strikes Back
// Star Wars: Return of the Jedi
// Casino Royale
// Mission Impossible
  1. Lastly, remove the last movie in the list and check that the array count has reduced by one:
moviesToWatch.remove(at: 6) 
print(moviesToWatch.count) // 6
print(moviesToWatch)
// The Shawshank Redemption
// Ghostbusters (1984)
// Star Wars: A New Hope
// Star Wars: Empire Strikes Back
// Star Wars: Return of the Jedi
// Casino Royale

With that, we've looked at many ways we can create and manipulate arrays.

How it works...

When creating an array, we need to specify the type of elements that will be stored in the array. The array element type is declared in angular brackets as part of the array's type declaration. In our case, we are storing strings:

var moviesToWatch: Array<String> = Array() 
moviesToWatch.append("The Shawshank Redemption")
moviesToWatch.append("Ghostbusters")
moviesToWatch.append("Terminator 2")

The preceding code uses a Swift language feature called generics, which can be found in many programming languages, and will be covered in detail in Chapter 4, Generics, Operators, and Nested Types.

The append method of Array will add a new element to the end of the array. Now that we have put some elements in the array, we can retrieve and print those elements:

print(moviesToWatch[0]) // "The Shawshank Redemption" 
print(moviesToWatch[1]) // "Ghostbusters"
print(moviesToWatch[2]) // "Terminator 2"

Elements in an array are numbered with a zero-based index, so the first element in the array is at index 0, the second is at index 1, the third is at index 2, and so on. We can access the elements in the array using a subscript, in which we provide the index of the element we want to access. A subscript is specified in square brackets, after the array instance's name.

When an element is accessed using the index subscript, no check is done to ensure you have provided a valid index. In fact, if an index is provided that the array doesn't contain, this will cause a crash. Instead, we can use some index helper methods on Array to ensure that we have an index that is valid for this array. Let's use one of these helper methods to check an index that we know is valid for our array, and then another that we know is not valid:

let index5 = moviesToWatch.index(moviesToWatch.startIndex,
offsetBy: 5,
limitedBy: moviesToWatch.endIndex)
print(index5 as Any) // Optional(5)

let index10 = moviesToWatch.index(moviesToWatch.startIndex,
offsetBy: 10,
limitedBy: moviesToWatch.endIndex)
print(index10 as Any) // nil

The index method lets us specify the index we want as an offset of the first index parameter, but as something that's limited by the last index parameter. This will return the valid index if it is within the bounds, or nil if it is not. By the end of the playground, the moviesToWatch array contains six elements, in which case retrieving index 5 is successful but index 10 returns nil.

In the next chapter, we will cover how to make decisions based on whether this index exists, but for now, it's just useful to know that this method is available.

Arrays have a count property that tells us how many elements they store. So, when we add an element, this value will change:

print(moviesToWatch.count) // 3 

Elements can be inserted anywhere in the array using the same zero-based index that we used in the preceding code:

moviesToWatch.insert("The Matrix", at: 2) 

So, by inserting "The Matrix" at index 2, it will be placed at the third position in our array, and all the elements at position 2 or greater will be moved down by 1.

This increases the array's count:

print(moviesToWatch.count) // 4

The array also provides some helpful computed properties for accessing elements at either end of the array:

let firstMovieToWatch = moviesToWatch.first 
print(firstMovieToWatch as Any) // Optional("The Shawshank Redemption")
let lastMovieToWatch = moviesToWatch.last
print(firstMovieToWatch as Any) // Optional("Terminator 2")
let secondMovieToWatch = moviesToWatch[1]
print(secondMovieToWatch) // "Ghostbusters"

These properties are optional values as the array may be empty, and if it is, these will be nil. However, accessing an array element via an index subscript returns a non-optional value.

In addition to retrieving values via the subscript, we can also assign values to an array subscript:

moviesToWatch[1] = "Ghostbusters (1984)" 

This will replace the element at the given index with the new value.

When we created our first array, we created an empty array and then appended values to it. Additionally, an array literal can be used to create an array that already contains values:

let spyMovieSuggestions: [String] = ["The Bourne Identity", 
"Casino Royale",
"Mission Impossible"]

An array type can be specified with the element type enclosed by square brackets, and the array literal can be defined by comma-separated elements within square brackets. So, we can define an array of integers like this:

let fibonacci: [Int] = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 

As we learned in the previous chapter, the compiler can often infer the type from the value we assign, and when the type is inferred, we don't need to specify it. In both the preceding arrays, spyMovieSuggestions and fibonacci, all the elements in the array are of the same type; that is, String and Int, respectively. Since these types can be inferred, we don't need to define them:

let spyMovieSuggestions = ["The Bourne Identity", "Casino Royale", 
"Mission Impossible"]
let fibonacci = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

Arrays can be combined using the + operator:

moviesToWatch = moviesToWatch + spyMovieSuggestions

This will create a new array by appending the elements in the second array to the first.

The array provides a convenience initializer that will fill an array with repeating elements. We can use this initializer to create an array with the name of a well-known movie trilogy:

var starWarsTrilogy = Array<String>(repeating: "Star Wars: ", count: 3)

We can then combine subscript access, string appending, and subscript assignment to add the full movie name to our trilogy array:

starWarsTrilogy[0] = starWarsTrilogy[0] + "A New Hope" 
starWarsTrilogy[1] = starWarsTrilogy[1] + "Empire Strikes Back"
starWarsTrilogy[2] = starWarsTrilogy[2] + "Return of the Jedi"

The array also provides a helper for replacing a range of values with the values contained in another array:

moviesToWatch.replaceSubrange(2...4, with: starWarsTrilogy)

Here, we have specified a range using ... to indicate a range between two integer values, inclusive of those values. So, this range contains the integers 2, 3, and 4.

We will specify ranges in this way in subsequent chapters. Alternatively, you can specify a range that goes up to, but not including, the top of the range. This is known as a half-open range:

moviesToWatch.replaceSubrange(2..<5, with: starWarsTrilogy)  

For our arrays, we've added elements, accessed them, and replaced them, so we need to know how to remove elements from an array:

moviesToWatch.remove(at: 6) 

Provide the index of the element to the remove method. By doing this, the element at that index will be removed from the array, and all the subsequent elements will move up one place to fill the empty space. This will reduce the array's count by 1:

print(moviesToWatch.count) // 6 

There's more...

If you are familiar with Objective-C, you will have used NSArray, which provides similar functionalities to a Swift array. You may also remember that NSArray is immutable, which means its contents can't be changed once it's been created. If you need to change its contents, then an NSMutableArray should be used instead. Due to this, you may be wondering if Swift has similar concepts of mutable and immutable arrays. It does but rather than using separate mutable and immutable types, you create a mutable array by declaring it as a variable and an immutable array by declaring it as a constant:

let evenNumbersTo10 = [2, 4, 6, 8, 10] 
evenNumbersTo10.append(12) // Doesn't compile

var evenNumbersTo12 = evenNumbersTo10
evenNumbersTo12.append(12) // Does compile

To understand why this is the case, it's important to know that an array is a value type, as are the other collection types in Swift.

As we saw in the previous chapter, a value type is immutable in nature and creates a changed copy whenever it is mutated. Therefore, by assigning the array to a constant using let, we prevent any new value from being assigned, making mutating the array impossible.

See also

Further information about arrays can be found in Apple's documentation on the Swift language at https://developer.apple.com/documentation/swift/array.

Arrays use generics to define the element type they contain. Generics will be discussed in detail in Chapter 4, Generics, Operators, and Nested Types.

You have been reading a chapter from
Swift Cookbook - Second Edition
Published in: Feb 2021
Publisher: Packt
ISBN-13: 9781839211195
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image