UIKit For Masterminds 4page
UIKit For Masterminds 4page
Apple™, iPhone™, iPad™, Mac™, among others mentioned in this work, are
trademarks of Apple Inc.
LISTS
Conventions
CHAPTER 22 - MAC CATALYST
22.1 MAC CATALYST This book explores basic and advanced topics required to develop
MAC APPS professional applications. Depending on your current level of knowledge,
CONDITIONAL CODE you may find some of these topics easy or difficult to learn. To help you
MENU navigate the book, we have identified each section with a label. The
TOOLBAR following is a description of what these labels represent.
GESTURES
VIEWS

22.2 MULTIPLE WINDOWS
The Basic label represents topics you can ignore if you already know the
CHAPTER 23 - APP STORE basics of Swift and app development. If you are learning how to develop
23.1 PUBLISHING applications for Apple devices for the first time, these sections are
APPLE DEVELOPER PROGRAM required.
CERTIFICATES, PROVISIONING PROFILES, AND IDENTIFIERS
APP STORE CONNECT 
SUBMITTING THE APPLICATION The Medium label represents topics that are not required for every app.
You can ignore the information presented in these sections until they are
applied in practical situations later or when you need them in your own
applications.

The Advanced label represents topics that are only required in advanced
applications or API development. The information presented in these
sections is not required for the development of most applications, but it
can be helpful if you want to improve your understanding of how Apple
technologies work.
If you are new to app development, read all the Basic sections first
and only read the Medium sections when you need it later to understand
how the examples work.
Examples CHAPTER 1 - APP DEVELOPMENT
Every single topic presented in this book is explained through examples
that you can try yourself. We recommend that you open Xcode and try the
examples as you learn, but you can download the codes and projects from
our website to save time (www.formasterminds.com).
The examples in this book only apply the technologies you already know,
so they not always represent best practices. There are several
programming patterns and best practices you can follow. What applies to
you depends on the characteristics of your application and what you want
to achieve with it. We recommend you explore all the possibilities
presented in this book but also experiment and try your own.
After the introduction of the iPhone in 2007 and its massive success, Apple Apple requires developers to use the software provided by the company to
decided to provide the tools for developers to create applications for it. create apps for its devices, and this software only works on Apple
The tools were already available for Mac computers, but the iPhone's small computers. For this reason, the options are limited, but the good news is
screen and unique characteristics called for a different approach. So, along that the tools and accounts we need are provided by the company for free.
with those already available, Apple introduced a specific set of tools to
Mac Computer—This in theory could be any Mac computer, but the
develop apps for these devices called UIKit.
development software always requires the latest operative system
UIKit (User Interface Kit) includes everything a developer needs to manage
(currently macOS Monterey), so in practice we need a relatively new
a device and create the elements of the graphic interface. It was designed computer with a recommended 16GB of memory.
to develop apps for iPhones and iPads, but thanks to a new system called
Xcode—This is the software provided by Apple for development. The
Mac Catalyst, we can write applications for Mac computers, as well.
latest version is number 13. It’s free and the package comes with
everything we need to create our apps, including an editor, the SDK
(Software Development Kit), and a simulator to test the applications.
Apple Developer Account—This is a basic account we can get for
free. From this account, we can manage our membership, create
certificates, app identifiers and other information we need to test and
publish our apps.
Apple Membership—This is the membership required to publish
our apps in the App Store. As of this writing, the cost of this
membership is $99 US dollars per year.
Mobile Device— This could be any of the devices available in the
market that support the current versions of Apple’s mobile operative
systems (currently iOS 15, tvOS 15, watchOS 8, iPadOS 15, and macOS
Monterey). Testing our applications on a real device is highly
recommended and necessary before publishing.
1.3 Development

The welcome screen offers a list of the recent projects on the right and
buttons on the left to create a new project, open a project on our
computer, or clone one stored in a repository.
Programming Languages Frameworks and APIs
 
Several years ago, Apple adopted and implemented a language called Programming languages by themselves cannot do much. They provide all
Objective-C to allow developers to create applications for its devices. Due the elements to interact with the system but are basic tools for data
to the technical level required to work with this language, the spectacular management. Because of the complexity of the information required to
success of Apple's mobile devices did not impress developers the same control sophisticated technologies and access every part of a system, it
way as consumers. The demand for more and better applications was could take years to develop an application from scratch working with just
growing fast, but the complicated nature of the system did not appeal to the instructions of a programming language. Doing simple things like
most developers who were used to working with more traditional tools. To printing graphics on the screen or storing data in files would become a
solve this problem, in 2014 the company introduced a new programming nightmare if programmers had to depend on the tools provided by
language called Swift. Swift presents a simpler syntax that developers find programming languages alone. For this reason, languages are always
familiar, while at the same time preserves that low-level nature necessary accompanied by pre-programmed routines grouped in libraries and
to take advantage of every aspect of Apple's devices. Swift was designed to frameworks that through a simple interface called API (Application
replace Objective-C and, therefore, it is the language recommended to new programming interface) allow programmers to incorporate to their apps
developers. amazing functionality with just a few lines of code. Apple provides all this
functionality, including frameworks and their APIs, in a set of tools called
SDK (Software Development Kit) that comes with Xcode.
Frameworks and APIs are critical for app development. As developers, we
must learn and apply these tools if we want to create useful applications
and, therefore, they will become the main subject of study in following
chapters.
Compiler

Playground

This opens a window with a list of icons to select the template we want to
use. Templates are files with pre-programmed code to help us get started 
with our project. The ones available at this time are called Blank (with just
a few lines of code to start from scratch), Game (with basic code to
Playground presents a simple interface with a toolbar at the top and four
program a video game), Map (with the code to display a map), and Single
View (with the same code required to create a view for an application). areas. The Navigator Area where we can see the resources included in our
Playground project, the Editor Area where we write our code, the Results
Figure 2-2: Playground's templates Side Bar on the right where the results produced by our code are
displayed, and the Console at the bottom where we can read the errors
produced by the code and print our own messages. The interface includes var greeting = "Hello, playground"

buttons to open or remove some of these panels. The button on the top-
left corner removes the Navigator Area (number 1), the one on the top-
right corner removes a panel called Utilities Area with information about A computer program is just text written in a specific format. Each line of
the selected resource, and the button at the top-left corner of the Console text represents an instruction. Sometimes a single line includes several
area removes the console. instructions, and therefore each line is usually called statement. Every
As illustrated in Figure 2-3, the Editor Area includes a button at the bottom statement is an order, or a group of orders, required for the computer to
of the panel to run and stop the code (Play Button). There is also a play perform a task. In the code of Listing 2-1, the first statement uses the
button on the left side of the Editor Area that we can press if we want to instruction import to include in our code the pre-programmed codes from
execute parts of the code instead (number 4). When this button is pressed, the UIKit framework, and the second statement uses the instruction var to
the code is executed up to the line in which the button is located. store the text "Hello, playground" in memory.
Playground can run the code automatically or wait until we press the Play If we press the Play Button to execute the code, we see what the code
button. By default, the mode is set to Automatically Run, but we can press
does inside the Results Side Bar (in this case, it shows the text stored in
and hold the Play button to access the menu to modify this behavior.
memory by the var instruction). When we move the mouse over this
Figure 2-4: Playground's running mode indication, two small buttons show up, as illustrated below.
 
In the Editor Area, we can see the code we have programmed so far. When The button on the left is called Quick Look, and it shows a popup window
a new Playground file is created, Xcode offers a template that includes a with a visual representation of the result of the execution of the code, such
few basic lines of code to start with. Listing 2-1, below, is the code
as the formatted text or an image. In this case, no visual effect is generated
currently generated for the Blank template.
by the code, so we only see the plain text.
Listing 2-1: Playground template

Figure 2-6: Quick Look window
import UIKit
2.2 Variables

The code provided by Xcode for the Blank template is useless, but it shows
the basic syntax of the Swift language and how to do elemental things in a
program such as importing frameworks to add functionality and storing
data in memory. The reason why one of the statements is storing data in
memory is because this is the most important task of a program. A
program’s main functions are storing, retrieving, and processing data.
Working with data in the computer’s memory is a delicate process that
demands meticulous organization. If we are not careful, data may be
accidentally deleted, corrupted, or completely overwritten. To make sure
this does not happen, programming languages introduce the concept of
variables.
Memory An on switch represents the value 1 and an off switch represents the value
 0. Basic units were determined with the purpose of identifying parts of this
endless series of digits. One cell was called a bit and a group of 8 bits was
The computer’s memory is like a huge honeycomb, with consecutive cells called a byte. Figure 2-10 shows how a byte looks like in memory, with
that can be in two possible states: activated or deactivated. They are some of its switches on representing the binary number 00011101.
electronic switches with on and off positions established by low and high
energy levels. Figure 2-10: Representation of one byte in memory
 
Because of their two possible states, each cell is a small unit of The possible combinations of 8 bits are 256, therefore, a byte can
information. One cell may represent two possible states (switch on or off), represent binary numbers of 8 digits, which in turn can be converted to
but by combining a sequence of cells we can represent more states. For numbers that humans can understand, such as decimal numbers. With its
example, if we combine two cells, we have four possible states. 256 possible combinations, a byte can represent decimal numbers from 0
to 255. For instance, when the value of the byte in the example of Figure 2-
Figure 2-9: Combining two cells 10 is converted to the decimal system, we get the number 29 (00011101 =
29).
represent decimal numbers from 0 to 65535 (a total of 65536 possible Primitive Data Types
combinations). To establish clearly defined data structures, each 
programming language declares its own units of data of a predetermined
size. These units are usually called primitive types. Primitive data types are units of data defined by the language. They are
always the same size, so when we store a value of one of these data types,
the computer knows exactly how much memory to use. The following is
probably the most useful primitive data type provided by Swift.
Int—This data type defines integer numbers, which are numbers with
no fractional component. In 64 bits systems, the size of this data type
is 8 bytes and therefore it can store values from
-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.
If we check the size of each type presented so far and calculate the
possible combinations of bits, we will discover that the maximum values
don't match. For example, an Int8 uses 1 byte, which means it is composed Doublewhen performing calculations and use Float for minor tasks such as
of 8 bits, and for this reason it should be able to store numbers from 0 to storing coordinates to position graphics on the screen.
255 (256 possible combinations). The reason why an Int8 has a positive
limit of 127 is because it only uses 7 bits to store the value, the first bit on
the left is reserved to indicate the sign (positive or negative). Although
these limits are not restrictive, the language also provides the unsigned
versions of these types in case we need to store larger positive values.
UInt—This is the same as Int but for unsigned values. Because it does
not reserve a bit for the sign, in 64-bit systems it can store values from
0 to 18,446,744,073,709,551,615.
The specific data types for UInt are UInt8, UInt16, UInt32, and UInt64. These
data types work exactly like the equivalents for Int, but they are intended
to store only positive numbers.
Although all these data types are very useful, they are only good for storing
binary values that can be used to represent integer numbers. Arithmetic
operations also require the use of real numbers (e.g., 3.14 or 10.543).
Computers cannot reproduce these types of values, but they can work with
an approximation called floating-point numbers. The following are the
most frequently used floating-point data types defined in the Swift
language.
Floating-point types can handle large numbers using scientific notation, but
because of their precision, it is recommended to declare a variable of type
Declaration and Initialization variable we must always store a value in it to clear the space. This action is
 called Initialization.
The second statement in Listing 2-8 reads the value of the mynumber Arithmetic Operators
variable and assigns it to the myfavorite variable. The type of myfavorite is 
inferred to be Int (the same of mynumber). After this code is executed, we
have two integer variables, each with its own space in memory and the Storing values in memory is what variables allow us to do, but those values
value 5 stored in them. do not have to be declared explicitly, they can also be the result of
arithmetic operations. Swift supports the operations: + (addition), -
(subtraction), * (multiplication), / (division) and % (remainder).
When the system reads the statement in Listing 2-9, it adds 10 to 5 and
assigns the result to mynumber (15). Of course, we can perform not only
addition but any operation we want.
Listing 2-13: Adding numbers to variables -= is a shorthand for variable = variable - number, where number is the
 value we want to subtract from the variable’s current value.
var mynumber = 5
var total = mynumber + 10 // 15 With these operators, we can easily add or subtract a value to the current
 value of the variable and assign the result back to the same variable.
This example declares the mynumber variable and initializes it with the value Listing 2-15: Modifying the variable’s value using incremental operators
5. In the next statement, the total variable is declared and initialized with 
the result of the addition of the current value of mynumber plus 10 (5 + 10). var mynumber = 5
mynumber += 4 // 9
In Listing 2-13, we used a new variable to store the result of the operation,

but when the old value is not important anymore, we can store the result
back into the same variable.
The process generated by the code in Listing 2-15 is straightforward. After
Listing 2-14: Performing operations on the variable’s current value the value 5 is assigned to the mynumber variable, the system reads the
 second statement, gets the current value of the variable, adds 4 to that
var mynumber = 5 value, and stores the result back to mynumber (9).
mynumber = mynumber + 10 // 15

IMPORTANT: Swift also offers Overflow operators (&+, &-, &*, &/
and &%). These operators are useful when we think that an
In this example, the current value of mynumber is added to 10 and the result
operation could produce a result that goes over the limit the data
is assigned to the same variable. After the execution of the second
type can handle. For more information, visit our website and follow
statement, the value of mynumber is 15.
the links for this chapter.
Working with values previously stored in a variable allows our program to
evolve and adapt to new circumstances. For instance, we could add 1 to
the current value of a variable and store the result in the same variable to
create a counter. Every time the statement is executed, the value of the
variable is incremented by one unit. Recurrent increments and decrements
of the values of variables are very important in computer programming.
Because of this, Swift supports two operators that were specifically
designed for this purpose.
All the rules for variables also apply to constants; with the exception that
we cannot assign a new value after the constant was already initialized.
Besides primitive data types, Swift defines additional data types to allow us Because of their nature, computers cannot store decimal numbers,
to work not only with numbers but also more complex values such as characters, or text. As we have seen in the previous section, the computer
logical values (true or false), characters, or text. memory is only capable of storing 1s and 0s (switches on and off), but they
can work with more complex values using tables that contain the
information necessary to represent those values (numbers, letters,
symbols, etc.). What the system stores in memory is not the character but
the value corresponding to the index of the character on the table. For
example, if we use the letter A, the value stored in memory will be the
decimal number 65 (in its binary representation) because that’s the
position of the letter A on the table used by Swift to define these
characters.
There are several standard tables of characters available. Swift is compliant
with a table called Unicode. This is a comprehensive table that includes
every character from almost any language in the world, and special
characters, such as emojis. Due to its broad range, the space in memory
required to store a single character varies from one character to another.
For this reason, Swift provides the Character type to store these values.
A character is declared using the Character data type and initialized with the
value between double quotes. In Listing 2-17, we declare a variable called
myletter with the value A.
Besides the characters we can type from the keyboard, Unicode allows us
to store emojis and symbols as characters, and Xcode offers a handy tool to
select the graphics we want. By pressing the combination of keys Control +
Command + Space, we can open a popup window and select a graphic with Strings
a click of the mouse.

Figure 2-11: Emojis and symbols
Individual characters are barely used in computer programming. Instead,
we usually store strings of characters. The String type was created for this
purpose.
In Listing 2-19, the mytext variable is created with the value "My name is "
and then the string "John" is added at the end of the current value to get
the string "My name is John". The += operator works in a similar way, and
we can also combine them to get the string we want.
Boolean variables are a type of variables that can only store two values: As mentioned at the beginning of this chapter, after a variable is declared,
true or false. These variables are particularly useful when we want to execute we must provide its initial value. We cannot use a variable if it was not
an instruction or a set of instructions only if a condition is met. To declare a initialized. This means that a variable has a valid value all the time. But this
is not always possible. Sometimes we do not have an initial value to assign
Boolean variable, we can specify the data type as Bool or let Swift infer it
to the variable during development or we need to indicate the absence of
from the value, as in the following example.
a value because the current one becomes invalid. For these situations,
Swift defines a modifier that turns every data type into an optional type.
Listing 2-25: Declaring a Boolean variable
This means that the variable marked as optional may have a value or be

empty. To declare an optional, we add a question mark after the type’s
var valid = true
name.

 read the value of total later, we must unwrap it as we did with mynumber
var mynumber: Int? before.
mynumber = 5
mynumber = nil
 IMPORTANT: Before unwrapping an optional, we need to make
sure it contains a value (it is not equal to nil). If we try to unwrap an
This example declares an optional integer, assigns the value 5 to it, and empty optional, the app will return an error and crash. Later in this
then declares the variable as empty with the keyword nil. Although chapter we will learn how to use conditional statements to check
optionals seem to work like regular variables, they do not expose their this condition.
values. To read the value of an optional, we must unwrap it by adding an
There are times when we know that an optional will always have a value,
exclamation mark at the end of the name.
but we do not know what the initial value is. For example, we could have a
Listing 2-29: Unwrapping an optional variable variable that receives a value from the system as soon as the application is
 executed. When the variable is declared in our code, we do not have a
var mynumber: Int? value to assign to it, but we know that the variable will have a value as
mynumber = 5
var total = mynumber! * 10 // 50 soon as the user launches the app. For these situations, Swift includes
 implicitly unwrapped optionals. These are optional variables declared with
the exclamation mark instead of the question mark. The system treats
The last statement of Listing 2-29 unwraps mynumber to get its value, these variables as optionals until we use them in a statement, as in the
multiplies this value by 10, and assigns the result to the total variable. This is following example.
only necessary when we need to use the value. If we just want to assign an
Listing 2-31: Declaring implicitly unwrapped optionals
optional to another optional, the process is as always.

Listing 2-30: Assigning an optional to another optional var mynumber: Int!
mynumber = 5
 var total = mynumber * 10 // 50
var mynumber: Int? 
mynumber = 5
var total = mynumber
 In this code, the mynumber variable was declared as an implicitly unwrapped
optional and it was later initialized with the value 5. Notice that it was not
In this example, the system infers the type of the total variable to be an necessary to write the exclamation mark when reading its value anymore.
optional of type Int and assigns the value of mynumber to it. If we want to The system unwraps the mynumber variable automatically to use its value in
the multiplication (this is only available for implicitly unwrapped optionals).
Tuples

In Listing 2-34, we read the values of the myname tuple at index 0 and 2 to 
include them in a new string and assign the string to mytext. The same
syntax may be used to modify a value. The names of the variables are declared between parentheses and the
tuple is assigned to this construction. The values are assigned to the
Listing 2-35: Changing the value of a tuple variables in the same order they are in the tuple. If only some of the values

are required, the rest may be ignored with an underscore.
var myname = ("John", "Doe", 44)
myname.0 = "George" Listing 2-38: Ignoring some of the values of a tuple
var mytext = "\(myname.0) is \(myname.2) years old" 

var myname = ("John", "Doe", 44)
var (name, _, age) = myname
var mytext = "\(name) is \(age) years old"
The second statement in Listing 2-35 assigns a new string to the first value

of the tuple. The new value must be of the same data type as the old one
or we will get an error. After the code is executed, the value of mytext is
Only the variables name and age are created in this last example (notice the
"George is 44 years old".
underscore in the place of the second variable). The string assigned to
Indexes are a quick way to access the values of a tuple, but they do not
mytext is "John is 44 years old".
help us remember what the values represent. To identify the values in a
tuple, we can assign a name to each one of them. The name must be
declared before the value and separated with a colon, as shown next.
Swift also provides a way to copy the values of the tuple into independent
variables.
Up to this point, we have been writing instructions in a sequence, one after A simple but handful conditional statement available in Swift is if. With if
another. In this programming pattern, the system executes each statement we can check a condition and execute a group of instructions only when
once. It starts with the one at the top and goes on until it reaches the end the condition is true. The instructions that are going to be executed must
of the list. The purpose of Conditionals and Loops is to break this
be declared between braces after the condition.
sequential flow. Conditionals allow us to execute one or more instructions
only when a condition is met, and Loops let us execute a group of
Listing 2-39: Comparing two values with if
instructions repeatedly.

var age = 19
var message = "John is old"
if age < 21 {
message = "John is young"
}

Two variables are declared in this code. The age variable contains the value
we want to check, and the message variable is the one we are going to
modify depending on the state of the condition. The if statement compares
the value of age with the number 21 using the character < (less than). This
comparison returns the state of the condition (true or false). If the
condition is true (the value of age is less than 21), the instruction between
braces is executed, assigning a new value to the message variable, otherwise
the instruction is ignored, and the execution continues with the
instructions after the braces. In this case, the value of age is less than 21
and therefore the string "John is young" is assigned to the message variable.
the rest of the statements. This whitespace is automatically When only two results are required, we may define the condition using a
generated for you by Xcode, but you can add it yourself, when Boolean. These values do not need to be compared with the expected
necessary, by pressing the Tab key on your keyboard. value; they already return a state (true or false).
The < symbol we used in the last example to compare values is part of a Listing 2-41: Conditions with Boolean values
group of operators called comparison operators. The following is the list of 
This example introduces the process we must follow to read the value of
The if statement in Listing 2-43 compares the value of the age variable with an optional variable. The optional is checked first against nil. If it is different
21 and checks the value of the smart variable. If age is less than 21 and smart from nil (which means it contains a value), we unwrap the optional inside
is true, then the overall condition is true, and a new string is assigned to the block of statements using an exclamation mark, assign its value to a
message. If any of the individual conditions is false, then the overall condition constant, and use the constant to perform any operation necessary.
is false, and the block of instructions is not executed. In this case, both We always need to make sure that an optional has a value before
conditions are true and therefore the "John is allowed" string is assigned to unwrapping it. Because of this, Swift introduces a convenient syntax that
message. checks the optional and unwraps its value at the same time. It is called
Optional Binding.
IMPORTANT: Using && (AND) and || (OR) you can create a logical
sequence of multiple conditions. The system evaluates one condition Listing 2-45: Using optional binding to unwrap an optional variable
at a time from left to right and compares the results. If you want to 
make sure that the expressions are evaluated in the correct order, var count = 0
var myoptional: Int? = 5 executed only if both conditions are true (the myoptional variable contains a
if let uvalue = myoptional {
count = count + uvalue // 5 value and the value is equal to 5).
} Sometimes, a group of instructions must be executed for each state of the

condition. For this purpose, Swift includes the if else statement. The
This code is cleaner and easy to read. The optional is unwrapped as part of instructions are declared in two blocks. The first block is executed when
the condition. If it is different from nil, its value is assigned to the uvalue the condition is true, and the second block when the condition is false.
constant and the statements in the block are executed, otherwise, the
statements inside the block are ignored. Listing 2-47: Using if else to respond to both states of the condition

var mynumber = 6
IMPORTANT: The constant created to unwrap the optional in this if mynumber % 2 == 0 {
way is only available inside the block assigned to the if statement. If mynumber = mynumber + 2 // 8
we try to read the value of this constant from outside the block, we } else {
mynumber = mynumber + 1
will get an error. We will learn more about the scope of variables in }
Chapter 3. 
If we want to unwrap several optionals at the same time using Optional This is a simple example that checks whether a value is odd or even using
Binding, we must declare the expressions separated by a comma. This also the remainder operator. The condition gets the remainder of the division
applies when we want to check other conditions in the same statement. between the value of the mynumber variable and 2 and compares the result
For instance, the following example unwraps an optional and only executes against 0. If true, it means that the value of mynumber is even, so the first
the code between braces if its value is equal to 5. block is executed. If the result is different from 0, it means that the value is
odd and the condition is false, so the block corresponding to the else
Listing 2-46: Checking multiple conditions with Optional Binding
instruction is executed instead.

The statements if and else may be concatenated to check for as many
var count = 0
var myoptional: Int? = 5 conditions as we need. In the following example, the first condition checks
if let uvalue = myoptional, uvalue == 5 { whether age is less than 21. If not true, the second condition checks
count = count + uvalue // 5
} whether age is over 21. And if not true, the final else block is executed.

Switch When we need to execute the same set of instructions for more than one
value, we can declare the values separated by comma.

Listing 2-53: Checking multiple conditions per case
We can repeat the if and else statements to check as many conditions as we 
need, but this pattern can make the code impossible to read and maintain. var age = 6
When several conditions must be verified, it is better to use the switch var message = "You go to "
switch age {
instruction instead. This instruction compares a value with a list of values case 2, 3, 4:
and executes the statements corresponding to the value that matches. The message += "Day Care"
case 5, 6, 7, 8, 9, 10, 11:
possible matches are listed between braces using the case keyword, as in message += "Elementary School" // "You go to Elementary School"
the following example. case 12, 13, 14, 15, 16, 17:
message += "High School"
case 18, 19, 20, 21:
Listing 2-52: Checking conditions with switch message += "College"
default:
 message += "Work"
var age = 19 }
var message = "" 
switch age {
case 13:
message = "Happy Bar Mitzvah!"
The switch statement can also work with more complex data types,
case 16: such as strings and tuples. In the case of tuples, switch provides additional
message = "Sweet Sixteen!"
case 21:
options to build complex matching patterns. For example, the following
message = "Welcome to Adulthood!" code checks the second value of a tuple to determine the difference in age.
default:
message = "Happy Birthday!" // "Happy Birthday!"
} Listing 2-54: Matching a tuple in a switch statement
 
var message = ""
The cases must be exhaustive; every possible value of the variable being var ages = (10, 30)
checked must be contemplated. If we do not include a case statement for switch ages {
every possible value, we must add a default statement at the end that is case (10, 20):
message = "Too close"
executed when no match is found. case (10, 30):
In Listing 2-52, we compare the value of the age variable with a small set of message = "The right age" // "The right age"
case (10, 40):
values corresponding to special dates. If no case matches the value of the message = "Too far"
variable, the default statement is executed and the string "Happy Birthday!" default:
message = "Way too far"
is assigned to message. }
 message = "Way too far"
}

This example always compares the first value of the tuple against 10
but checks different matches for the second value. If a value does not
matter, we can use an underscore to ignore it. In this example, when the switch statement checks the first and third
cases, it creates a constant called x and assigns the first value to it, so we
Listing 2-55: Matching only the second value of a tuple can access and use the value from the statements inside the case (in this
 example, we just add the value to a string).
var message = "" There is an even more complex matching pattern that involves the use
var ages = (10, 30) of a clause called where. This clause allows us to check additional conditions.
switch ages { In the following example, we capture the values of the tuple with another
case (_, 20): tuple and compare them against each other.
message = "Too close"
case (_, 30):
message = "The right age" // "The right age" Listing 2-57: Comparing values with where
case (_, 40): 
message = "Too far"
default: var message = ""
message = "Way too far" var ages = (10, 20)
}
 switch ages {
case let (x, y) where x > y:
message = "Too young"
An alternative offered by the switch statement to create complex case let (x, y) where x == y:
matching patterns is to capture a value in a constant to be able to access it message = "The same age"
case let (x, y) where x < y:
from the instructions of the case. message = "Too old" // "Too old"
default:
message = "Not found"
Listing 2-56: Capturing values with constants }
 
var message = ""
var ages = (10, 20) Every time the switch statement tries to match a case in this example, it
switch ages { creates a tuple and assigns the values of ages to it. The where clause
case (let x, 20): compares the values and when the condition returns true it executes the
message = "Too close to \(x)" // "Too close to 10"
case (_, 30):
statements inside the case.
message = "The right age"
case (let x, 40):
message = "Too far to \(x)"
default:
If the first time the condition is checked returns false, the statements in the
block are never executed. If we want to execute the statements at least
once, we must use repeat while.
The code in Listing 2-60 defines two String variables: mytext with the text
"Hello" and message with an empty string. Next, we use a for in loop to iterate In this example, we iterate over the value of mytext to count the number of
over the characters of the string in mytext and add each character to the characters in the string. The value of the counter variable is incremented by
current value of message. In each cycle of the loop, the instruction takes one 1 each cycle, giving a total of 5.
character from the value of mytext, assigns it to the letter constant, and A for in instruction may include the where clause to perform the next cycle
executes the statements in the block. The first statement uses a ternary only when a condition is met. For instance, the following code checks the
operator to check whether the value of message is an empty string. If not, it value of the letter and only performs the cycle when the letter is not an L.
adds the - character at the end of it, otherwise, it adds an empty string. In consequence, only the letters H, e, and o are counted.
The continue instruction is applied when we do not want to execute the rest
of the statements in the block, but we want to keep the loop running. For
instance, the following code counts the letters in a string but ignores the
letters "l".
The if statement inside the for in loop of Listing 2-63 compares the value of
letter with the letter "l". If the characters match, the continue instruction is
executed, the last statement inside the loop is ignored, and the loop moves Listing 2-65: Ignoring values in a switch statement
on to the next character in mytext. In consequence, the code counts all the 
characters that are different from "l" (H, e, and o). var age = 19
Unlike the continue instruction, the break instruction interrupts the loop var message = ""
completely, moving the execution of the program to the statements after switch age {
the loop. The following example only counts the characters in the string case 13:
message = "Happy Bar Mitzvah!"
that are placed before the first letter "l". case 16:
message = "Sweet Sixteen!"
case 21:
Listing 2-64: Interrupting the loop
message = "Welcome to Adulthood!"
 default:
var mytext = "Hello" break
var counter = 0 }

for letter in mytext {
if letter == "l" { After the execution of this code, the message variable is empty because
break
} there is no case that matches the value of the age variable and therefore the
counter += 1 code in default is executed and the break instruction returns the control to
}
var message = "The string contains \(counter) letters" // 2 the statements after the switch.

Again, the if statement of Listing 2-64 compares the value of letter with the
character "l", but this time it executes the break instruction when a match is
found. If the character currently processed by the loop is "l", the break
instruction is executed, and the loop is over, no matter how many
characters are left in the string. In consequence, only the characters
located before the first letter "l" are considered (H and e).
The break instruction is also useful to cancel the execution of a switch
statement. The problem with the switch statement in Swift is that the cases
must be exhaustive, which means that every possible value must be
contemplated. When this is not possible or necessary, we can use the break
instruction to ignore the values that are not applicable. For example, we
can declare the cases for the values we need and then break the execution
in the default case for the rest of the values that we do not care about.
The guard instruction is intended to prevent the execution of the code that
follows the statement. For example, we can break the execution of a loop
when a condition is satisfied, as we do with an if else statement.
The guard instruction works along with the else instruction and therefore it is
very similar to the if else statement, but the code is only executed when the
condition is false. In the example of Listing 2-66, the for in loop reads the
characters of the string in mytext one by one, as we did before. If the
characters are different from the letter "l", we increase the value of counter
by 1, but when the value of letter is equal to "l", the condition of the guard
instruction is false and therefore the break instruction is executed,
interrupting the loop.
The processing units that define the Swift paradigm (objects, structures, Functions are declared with the func keyword followed by a name,
and enumerations) are capable of encapsulating data along with parentheses, and the code between braces.
functionality. The data is stored in the same variables we studied before,
but the functionality is provided by functions. Functions are blocks of code Listing 3-1: Declaring and calling functions
delimited by curly braces and identified by a name. The difference between 
functions and the block of codes used in loops and conditional statements var mynumber = 5
is that there is no condition to satisfy; the statements inside a function are func myfunction() {
mynumber = mynumber * 2 // 10
executed every time the function is called (executed). Functions are called }
by writing their names followed by parentheses. This call may be myfunction()
performed from anywhere in the code and every time necessary, which 
completely breaks the sequential processing of a program. Once a function
is called, the execution of the program continues with the statements The code in Listing 3-1 declares the mynumber variable, initializes it with the
inside the function and only returns to the section of the code that called value 5, and then declares a function called myfunction(). The statements in a
the function once the execution of the function is over. function are only processed when the function is called, so after the
myfunction() function is declared, we call it with the instruction myfunction().
When our function is called, it multiplies the current value of mynumber
times 2 and assigns the result back to the variable. Once all the statements
inside the function are processed, the execution continues from the
statement after the call.
As we already mentioned, once the function is declared, we can call it any
time necessary and from anywhere in the program. For example, the
following code runs a while loop that calls myfunction() a total of 5 times (the
loop runs while counter is less than 5). Every time the function is executed,
mynumber’s current value is multiplied by 2, getting a result of 160.
func myfunction() {
mynumber = mynumber * 2 // 160 In this example, the function is declared with one parameter of type Int
}
while counter < 5 { called number.
myfunction() The call must include the name of the parameter and the value we
counter += 1
} want to send to the function. When the function of Listing 3-3 is called, the
 value between the parentheses of the call (5) is assigned to the number
constant, the value of the constant is multiplied by 2, and finally the result
The functions in these examples are modifying the value of an external is included in a string with string interpolation.
variable (a variable that was not declared inside the function). Creating a Of course, we can include as many parameters as we need. The
function that works with values and variables that do not belong to the following example multiplies two values and creates a string with the
function itself could be dangerous; some variables may be modified by result.
accident from other functions, the function may be called before the
variables were even declared or initialized, or the variables that the Listing 3-4: Sending different values to a function
function tries to modify may not be accessible by the function (functions 
have limited scope, as we will see later). To make sure that a function func multiply(number1: Int, number2: Int) {
processes the right values, they must be sent to the function when it is let result = number1 * number2
let message = "The result is \(result)" // "The result is 80"
called. The type of values the function can receive and the names they are }
going to take are specified within the function’s parentheses separated by multiply(number1: 20, number2: 4)

a comma. When the function is executed, these parameters are turned into
constants that we can read inside the function to get their values.
Functions may not only be called every time we need them, but also the
values we provide to the function in the call may be different each time.
Listing 3-3: Sending values to a function
This makes functions reusable.

func doubleValue(number: Int) {
let total = number * 2 Listing 3-5: Sending different values to a function
let message = "Result: \(total)" // "Result: 10" 
}
doubleValue(number: 5) func doubleValue(number: Int) {
let total = number * 2

let message = "Result: \(total)"
}
In this example, we don't use external variables anymore. The value to doubleValue(number: 5) // "Result: 10"
doubleValue(number: 25) // "Result: 50"
be processed is sent to the function when it is called and received by the 
function through its parameter. The parameters are declared within the
function’s parentheses with the same syntax used for constants and The constants and variables declared inside a function, like total and message,
variables. We must write the name and the data type separated by a colon. are not accessible from other parts of the code. This means that a function
can receive values, but the result produced by processing those values is Listing 3-7: Returning a tuple
trapped inside the function. To communicate this result to the rest of the 
code, functions can return a value using a special instruction called return. func sumCharacters(word: String) -> (String, Int) {
var characters = ""
The return instruction finishes the processing of the function, so we must var counter = 0
declare it after all the statements required have been processed, as in the for letter in word {
characters += "\(letter) "
following example. counter += 1
}
return (characters, counter)
Listing 3-6: Returning a value from a function }
 var (list, total) = sumCharacters(word: "Hello")
var message = "There are \(total) characters (\(list))"
func doubleValue(number: Int) -> Int { 
let total = number * 2
return total
} The sumCharacters() function of Listing 3-7 receives a string (word: String) and
let result = doubleValue(number: 25)
returns a tuple composed of a string and an integer (-> (String, Int)). The
let message = "The result is \(result)" // "The result is 50"
 function adds the characters to the characters variable and counts them with
the counter variable, as we did before (see Listing 2-60). At the end, the
When we create a function that returns a value, the type of the value tuple is returned, its values are assigned to the list and total variables, and
returned is specified in the declaration after the parentheses with the then incorporated into a string ("There are 5 characters (H e l l o )").
syntax -> Type, where Type is just the data type of the value that is going to Besides returning the result of an operation, the return instruction can also
be returned by the function. A function can only return values of the type be used to interrupt the execution of a function. The guard instruction
indicated in its definition. For instance, the function in Listing 3-6 can only introduced in Chapter 2 is perfect for cases like this, as illustrated by the
following example.
return integer values because we declared the returned type as -> Int.
When a function returns a value, the system calls the function first and
Listing 3-8: Interrupting the execution of a function with guard
then the value returned is processed inside the statement that made the

call. For instance, in the code of Listing 3-6, we create the result variable and func doubleValue(number: Int) -> Int {
assign to this variable a call to the doubleValue() function. When the system guard number < 10 else {
return number
processes this statement, the function is executed first and then the value }
returned (50) is assigned to the variable. return number * 2
}
The values received and returned by a function may be of any available let result = doubleValue(number: 25)
data type. The following example takes a string and returns a tuple with a let message = "The result is \(result)" // "The result is 25"
string and an integer. 
The doubleValue() function of Listing 3-8 is similar to previous examples. It
receives a number, multiplies it by 2, and returns the result, but this time Listing 3-10: Modifying external variables from a function
we first check that the value received by the function is less than 10. If the 
value is equal or higher than 10, the guard instruction calls the return func first() {
instruction with the received value, otherwise, the statements of the var number = 25
second(value: &number)
function are executed as normal. In this case, the value sent to the function print("The result is \(number)") // "The result is 50"
is 25, therefore the condition is false, and the same value is returned. }
func second(value: inout Int) {
Notice that in the example of Listing 3-8 we simplified our code including value = value * 2
the multiplication in the return instruction. The return instruction can take }
first()
single values or expressions like this. The instruction takes care of solving 
the expression (or operation, as in this case) and returning the result. For
this reason, sometimes we may find functions with only one statement in This code defines two functions: first() and second(). The second() function
charge of returning a value. If this is the case, we can remove the return receives an inout parameter called value, which means that any modification
keyword. In the following example, the call sends the number 25 to the on its value is stored in the original variable. The first() function defines a
function, the value is multiplied by 2, and returned, as in previous variable called number and then executes the second() function with it, so
examples, but this time we didn't have to declare the return keyword when the second() function multiplies this value times 2, the result (50) is
because there is only one statement inside the function and therefore the stored in number. At the end, we execute the first() function to start the
compiler knows what to return. process. Notice that in the call to the second() function we include an
ampersand before the variable's name (&). This tells the system that the
Listing 3-9: Removing the return keyword variable is going to be modified by the function.
 An important aspect of the definition of a function are the parameter's
func doubleValue(number: Int) -> Int { names. When we call a function, we must declare the names of the
number * 2
} parameters. For example, the function doubleValue() of previous examples
let result = doubleValue(number: 25) includes a parameter called number. Every time we call this function, we
let message = "The result is \(result)" // "The result is 50"

must include the name of the parameter (e.g., doubleValue(number: 50)). These
names are called argument labels. Swift automatically generates argument
Besides the return keyword, Swift offers the inout keyword to preserve a labels for every parameter using their names. Sometimes the names
value after the function finishes processing. When a parameter is marked assigned to the parameters of a function may be descriptive enough for
with inout, any changes performed on the value are stored in the original the statements of the function but may be confusing when we perform the
variable. This is useful when we call a function from another function (or call. For cases like these, Swift allows us to define our own argument labels
block), and we want the modifications introduced by the second function in the function's definition; we just need to declare them before the name
to persist. of the parameter separated by a space.
very limited due to the impossibility of the compiler to know the Standard Functions
nature of the values received. For example, we can add two integers,

but we can't add two Boolean values. To solve these issues, we can
constraint the generic data types with protocols. We will study how
to define protocols and how to use them later in this chapter.
The main advantage of functions is that we can call them from any part of
the program that has access to them, and they will always perform the Of all the functions in the Swift Standard Library, print() is probably the most
same operations. We don't even need to know how the function does it, useful. Its purpose is to print messages on the Xcode's console that may
we just send to the function the values we want to process and read the help us to fix bugs in our code. In the following example, we use it to print
result. Because of these features, functions can be shared, and the result of two operations.
programmers can implement in their code pre-programmed functions
provided by libraries and frameworks to incorporate additional Listing 3-16: Printing values on the console with print()
functionality that would take them too long to develop themselves. 
All the features of the Swift language we have implemented so far are let absolutenumber = abs(-25)
let minnumber = min(absolutenumber, 100)
included in a library called Standard Library. The Standard Library includes print("The number is: \(minnumber)") // "The number is: 25"
everything, from operators to primitive data types, as well as predefined 
functions. The following are some of the most frequently used.
The code in Listing 3-16 implements the abs() function to calculate the
print(String)—This function prints a string on the Xcode’s console. absolute value of -25, then gets the minimum value between absolutenumber
and the number 100 with the min() function, and finally prints a message on
abs(Value)—This function returns the absolute value of an integer.
the console with the result.
max(Values)—This function compares two or more values and Sequences or collections of values are very important in computer
returns the largest.
programming. The strings studied in Chapter 2 are a clear example. A string
min(Values)—This function compares two or more values and is a sequence of values of type Character. As we will see later in this chapter,
returns the smallest. the Swift Standard Library includes several types of collections to store
sequences of values that are important to the application or the user, but it
There are also functions available to stop the execution of the application
also offers a few functions to create sequences of values our application
in case of an unrecoverable error.
may need temporarily to process information. The following are some of
the most frequently used.
fatalError(String)—This function stops the execution of the
application and prints on the console the message provided by the
stride(from: Value, through: Value, by: Value)—This function
argument.
returns a collection of values from the value specified by the from
precondition(Bool, String)—This function stops the execution of argument to the value specified by the through argument in intervals
the application and prints a message on the console if a condition is specified by the by argument.
false. The first argument is the condition to be checked and the
stride(from: Value, to: Value, by: Value)—This function returns a
second argument is the message we want to print on the console.
collection of values from the value specified by the from argument to
the value specified by the through argument in intervals specified by over the values of the finalsequence collection and print them on the console
the by argument. The last value is not included. ("Hello - 0", "Hello - 2", "Hello - 4", "Hello - 6", "Hello - 8").
repeatElement(Value, count: Int)—This function returns a
collection with the number of elements specified by the count
argument and with the value specified by the first argument.
zip(Collection, Collection)—This function returns a collection of
tuples containing the values of the collections provided by the
arguments in sequential order.
func first() {
let base = 10.0
total += base * multiplier
}
func second() {
let multiplier = 5.0
let base = 3.5
total += base * multiplier
}
create independent blocks called Closures to take advantage of it. let myaddition = { () -> Int in
var total = 0
Closures are simple blocks of code with the syntax { (parameters) -> Type in let list = stride(from: 1, through: 9, by: 1)
statements }. They are like functions (functions are closures with a name), but
for number in list {
everything goes between braces and the in keyword is included to separate total += number
}
the data types from the statements. return total
Closures can be assigned to variables and executed using the name of the }()
variable, as we do with functions. The name of the variable becomes the print("The total is \(myaddition)") // "The total is 45"

name of the closure, as shown next.
The closure declared in Listing 3-20 doesn't receive any value and returns
Listing 3-19: Assigning closures to variables
an integer (() -> Int). The code in the closure adds the values of a collection

(1 to 9) and returns the result, but because we included the parentheses at
let multiplier = { (number: Int, times: Int) -> Int in
let total = number * times the end of the definition, the value assigned to the myaddition constant is
return total
the one returned by the closure (45), not the closure itself. The task
}
print("The result is \(multiplier(10, 5))") // "The result is 50" performed in this example is simple but executing a closure as soon as it is

declared is a technique usually implemented for more complex processes
such as loading a file or opening a database.
This example defines a closure and assigns it to the multiplier constant. After
If the closure does not receive any parameter, we can simplify its syntax
this, the name of the constant may be used to execute the closure. Notice
declaring the type of the constant or variable and letting Swift infer the
that the parameters of the closure and the return type are declared with
type of the value returned by the closure. Notice that when the closure
the same syntax as functions ((number: Int, times: Int) -> Int), but the
doesn't receive a value, we can also remove the in keyword.
parameters' names are not turned into argument labels and therefore they
are ignored in the call.
Listing 3-21: Simplifying a closure
 sent to the function. The function assigns the closure to the myclosure
let myaddition: Int = { constant, and the closure is executed inside the function using this name
var total = 0
let list = stride(from: 1, through: 9, by: 1) and the values 10 and 2, producing the result 20.
The closure was defined in the global space and was executed inside the
for number in list {
total += number processclosure() function, but we don't need to assign the closure to a
} variable, we can just define it in the call.
return total
}()
print("The total is \(myaddition)") // "The total is 45" Listing 3-23: Assigning the closure to the function's argument


func processclosure(myclosure: (Int, Int) -> Int) {
Closures cannot only be assigned to variables but also sent and returned print("The total is: \(myclosure(10, 2))") // "The total is: 20"
from functions, as any other value. When a function receives a closure, the }
parameter's data type only has to include the data types the closure processclosure(myclosure: { (number: Int, times: Int) -> Int in
return number * times
receives and returns, as in the following example. })

Listing 3-22: Sending a closure to a function
 The code in Listing 3-23 works the same way as the previous example, but
let multiplier = { (number: Int, times: Int) -> Int in it was simplified by assigning the closure directly to the function's
let total = number * times argument. This can be simplified even further by using a pattern called
return total
} Trailing Closures. When the final argument of a function is a closure, we
func processclosure(myclosure: (Int, Int) -> Int) {
let total = myclosure(10, 2)
can declare the closure at the end of the call, as in the following example.
print("The total is: \(total)") // "The total is: 20"
} Listing 3-24: Using Trailing Closures
processclosure(myclosure: multiplier)
 
func processclosure(myclosure: (Int, Int) -> Int) {
print("The total is: \(myclosure(10, 2))") // "The total is: 20"
The first statement in Listing 3-22 defines a closure that multiplies two
}
integers and returns the result. A function that receives a closure of this processclosure() { (number: Int, times: Int) -> Int in
number * times
type is defined next. Notice that the data type of the value received by the }
function was declared as (Int, Int) -> Int. This indicates to the compiler that 
the processclosure() function can receive a closure that in turn receives two
integer values and returns another integer. When the processclosure() When we pass the closure this way, the call does not include the myclosure
function is called in the last statement, the value of the multiplier variable is argument anymore. The closure declared after the parentheses is
considered as the last argument of the function and therefore the memory after the execution of the function is over. They are declared by
argument label is not necessary. preceding them with the @escaping keyword, as in the following example.
The code in Listing 3-24 works the same way as previous examples, the
only advantage is the reduction in the amount of code we had to write. Listing 3-26: Declaring escaping closures
And that can be simplified even further. In the last example we already 
removed the return keyword. As explained before, when the content of a var myclosure: () -> Void = {}
function (or in this case a closure) includes only one statement, the
func passclosure(closure: @escaping () -> Void) {
compiler implies that the value produced by that statement is the one to myclosure = closure
return and therefore the return keyword is not required anymore. But when }
passclosure() { () -> Void in
we are passing the closure to a function, Swift can also infer the data types print("Closure Executed")
of the values received by the closure and therefore we don't have to }
myclosure()
declare that either. Instead, we can represent these values using shorthand

argument names. These are special placeholders composed by the $
symbol and an index starting from 0. The first value received by the closure In the code of Listing 3-26, we declare a variable called myclosure that stores
is represented by $0, the second value by $1, and so on. a closure that doesn't receive or return any values (() -> Void) and then we
assign an empty closure to it. After that, we define a function called
Listing 3-25: Inferring the closure's data types passclosure() that receives an escaping closure and all it does is assign that
 closure to the myclosure variable. Next, we call the passclosure() function with a
func processclosure(myclosure: (Int, Int) -> Int) { trailing closure that prints a message on the console. Up to this point, all
print("The total is: \(myclosure(10, 2))") // "The total is: 20"
} we are doing is passing the closure to the function and the function is
processclosure() { $0 * $1 } assigning that closure to the myclosure variable, so at the end we execute

the closure in myclosure and the message is printed on the console.
Again, the code is the same, but now the closure is extremely simple.
When it is executed from the processclosure() function, it receives the values
IMPORTANT: When we define the data type of a closure, we must
declare the data types of the values it receives and the data type of
10 and 2 and assigns them to the placeholders $0 and $1, respectively.
the values it returns. Therefore, if the closure doesn't return any
Then, it multiplies their values and returns the result.
value, we must declare the return type as Void, as we did in the
In the previous examples, we have executed the closure received by the
example of Listing 3-26.
function inside the same function, but there are situations in which a
closure must be executed outside the function. This usually applies to
asynchronous operations, as we will see later. If we want to execute a
closure received by the function from outside the function, we must
declare it as an escape closure. Escape closures are closures that remain in
3.3 Structures Definition of Structures
 
Structures are an essential part of the organizational paradigm proposed To define a new structure, we must use the struct keyword and enclose the
by Swift. They are custom data types that include not only the data but data and functionality between braces.
also the code in charge of processing that data. When we define a
structure, what we are doing is declaring a data type that may contain Listing 3-27: Defining a structure
variables and constants (called properties) and functions (called methods). 
Later we can declare variables and constants of this type to store struct Item {
information with the characteristics defined by the structure. These values var name: String = "Not defined"
var price: Double = 0
(called instances) will be unique, each one with its own properties and }
methods. 
This example defines a structure called Item with two properties (variables):
name and price. The definition by itself does not create anything; it is just
delineating the elements of the type (also called members), like a blueprint
used to create the real structures. What we need to do to store values of
this new data type in memory, as we would do with any other data type, is
to declare a variable or constant of this type. The declaration is done as
before, but the data type is the name of the structure and the initialization
value is a special initializer with the syntax Name() (where Name is, again, the
name of the structure).
The code in Listing 3-28 creates a variable of type Item that stores an
instance of the Item structure containing the properties name and price. The
instance is created by the Item() initializer and then assigned to the purchase modifying the properties of the instance, the system creates a new
variable. structure and assigns the values to the properties of that instance.
In the previous example, the properties of a new instance always take the For this to be possible, the structure must be stored in a variable so
values declared in the structure’s definition ("Not Defined" and 0), but we can it can be replaced by the new structure later.
change them as we do with any other variable. The only difference is that
the properties are inside a structure, so every time we want to access Structures may be instantiated inside other structures, as many times as
them, we must mention the structure they belong to. The syntax necessary. The dot notation is extended in these cases to reach every
implements dot notation, as in variable.property, where variable is the name of element in the hierarchy.
the variable that contains the instance of the structure and property is the
name of the property we want to access. Listing 3-30: Structures inside structures

Listing 3-29: Assigning new values to the properties of a structure struct Price {
 var USD = 0.0
var CAD = 0.0
struct Item { }
var name = "Not defined" struct Item {
var price = 0.0 var name: String = "Not defined"
} var price: Price = Price()
var purchase = Item() }
purchase.name = "Lamps" var purchase = Item()
purchase.price = 10.50 purchase.name = "Lamps"
purchase.price.USD = 10.50
print("Product: \(purchase.name) $ \(purchase.price)") 

Listing 3-30 defines two structures: Price and Item. The price property of the
In this example, the properties of the Item structure and the purchase
Item structure was defined as a property of type Price. Instead of storing a
variable are declared as before, but this time we let Swift infer their data
single value, now it can store a structure with two properties, USD and CAD,
types. After the instance is created, new values are assigned to its
for American and Canadian dollars. When the Item structure is created and
properties using dot notation. Dot notation is not only used to assign new
assigned to the purchase variable, the Price structure for the price property is
values but also to read the current ones. At the end, we read and print the
also created with its values by default (var price: Price = Price()). By
values of the name and price properties on the console ("Product: Lamps $
concatenating the names of the variables and properties containing these
10.5").
structures we can read and modify any value we want. For instance, the
last statement in the code in Listing 3-30 accesses the USD property of the
IMPORTANT: Notice that we stored the structure in a variable (var). price structure inside the purchase structure to assign a price to the item in
This is to be able to assign new values to its properties later. When American Dollars (purchase.price.USD = 10.50).
the values of the properties in a structure are modified, instead of
In this example, the price object is created during instantiation, but this is but when all the optionals have values, the instruction performs the task
not usually the case. Sometimes the values of the properties containing (in this case, assigning the number 10.50 to the USD property).
objects are defined after the instance is created and therefore those
properties must be declared as optionals. The problem with optionals is
that we always need to check whether the variable or property has a value
before we use it. To simplify this task, Swift introduces Optional Chaining.
Optional Chaining is a simple tool to access objects, properties and
methods in a hierarchical chain that contains optional components. As
always, the access to these components is done through dot notation, but
a question mark is added to the names of the properties that have optional
values. When the system finds an optional, it checks whether it contains a
value and continues processing the expression only in case of success. Here
is the same example, but with the price property turned into an optional.
Key Paths are defined as constants, the key path is created of type KeyPath (a read-
only key path). In the last statement, we use this key path to access the

value of the price property of the purchase instance and print it on the
console.
Besides using dot notation to read and write a property, we can use key
We can easily create a read-and-write key path by defining the structure's
paths. A key path is a reference to a property that we can use to read and
properties as variables. In the following example, we turn the name and price
modify its value. The advantage of using key paths instead of dot notation
properties into variables and modify the value of price using our keyPrice key
is that they are stored in structures and therefore we can pass them to
path.
other parts of the code and then use them to access the values of the
properties they are referencing without even knowing what those
Listing 3-33: Using read and write key paths
properties are. This can be useful when we need to interact with

frameworks, or when we are extending code to include our own
struct Item {
functionality. var name: String
Swift defines several structures to store key paths. For instance, a read-only var price: Double
}
key path is stored in an instance of a structure of type KeyPath and read- var purchase: Item = Item(name: "Lamps", price: 27.50)
and-write key paths are stored in a structure of type WritableKeyPath. The
syntax to define a key path includes a backward slash and the name of the let keyPrice = \Item.price
purchase[keyPath: keyPrice] = 30.00
data type followed by the name of the property we want to reference. To print(purchase.price)
access the value of a property using a key path, Swift offers a syntax that 
includes square brackets after the instance's name and the keyword keypath,
as illustrated in the following example.
The code defines a structure with two properties: name and price. Next, we
create a key path to reference the price property. Because the properties
Methods statements perform the task, no matter how complex it is. And second, we
do not have to write the operation over and over again, because it is

always part of the instance of the structure we are working with.
A method can read the values of the instance’s properties but cannot
If we could only store properties, structures would be just complex data
assign new values to them. If we want a method to be able to modify the
types, like tuples, but structures may also include code. This is done
values of the properties of its own instance, we must declare the mutating
through functions. Functions inside structures are called methods, but their
keyword before the func keyword in the structure's definition.
definition and functionality are the same.
The syntax to execute a method is variable.method(), where variable is the
Listing 3-35: Assigning new values to properties from inside the structure
name of the variable that contains the instance of the structure and method

is the name of the method we want to call inside that structure, as shown
struct Item {
in the following example. var name = "Not defined"
var price = 0.0
Listing 3-34: Defining methods mutating func changename(newname: String) {
 name = newname
}
struct Item {
}
var name = "Not defined"
var purchase = Item()
var price = 0.0
purchase.changename(newname: "Lamps")
func total(quantity: Double) -> Double {
print("Product: \(purchase.name)") // "Product: Lamps"
return quantity * price

}
}
var purchase = Item() The changename() method of the Item structure in Listing 3-35 is declared as a
purchase.name = "Lamp"
purchase.price = 10.50 mutating method so it can assign a new value to the name property.
Therefore, we do not need to modify the name property directly, we can call
print("Total: \(purchase.total(quantity: 2))") // "Total: 21.0"

this method with the value we want to store, and the method takes care of
assigning the value to the property.
In Listing 3-34, a method is declared as part of the definition of the Item
structure. The method receives a value representing the number of items
sold and calculates the total money spent in the transaction. We could
have performed this operation outside the structure by reading the value
of the price property but having a method in the structure itself presents
some advantages. First, we don't have to worry about how the method
calculates the value; we just call the method with the right value and let its
Initialization Listing 3-37: Using memberwise initializers to provide the initial values of a
structure


struct Item {
Every instance created from the structure’s definition has the purpose to var name: String
store and process specific data. For example, we can create multiple var price: Double
}
instances of the Item structure defined before to store information about var purchase = Item(name: "Lamp", price: 10.50)
different products. Each product will have its own name and price, so the print("Purchase: \(purchase.name) $ \(purchase.price)")
properties of each instance must be initialized with the proper values. The 
This is similar to what Swift creates for us in the background when we use
memberwise initializers, but the advantage of declaring the init() method
ourselves is that we can specify only the parameters we need (as in the last
example) or even declare multiple init() methods to present several
alternatives for initialization, as shown next.
init(americans: Double) {
USD = americans
CAD = USD * 1.29
}
init(canadians: Double) {
CAD = canadians
USD = CAD * 0.7752
Computed Properties calculate the value every time the property is read. No matter if the value
of the ratetoCAD property changes, the canadians property will always return

the right price in Canadian dollars.
Computed properties with only a getter are called read-only properties
The properties we have declared up to this point are called Stored
because we can only read their values. When we declare a read-only
Properties. Their function is to store a value in memory. But there are other
property, we can omit the get method. And as we have seen before, when a
types of properties called Computed Properties. These properties do not
block contains only one statement, it knows what to return, so we can also
store a value of their own, instead they have access to the rest of the
omit the return keyword. The previous example can therefore be simplified
properties of the structure and can perform operations to set and retrieve
as follows.
their values.
Two methods were included for computed properties to be able to set
Listing 3-42: Defining read-only properties
and retrieve a value: get and set. These methods are also called getters and

setters and are declared between braces after the property’s name.
struct Price {
Although both methods are useful, only the get method is required. var USD: Double
var ratetoCAD: Double
Listing 3-41: Declaring computed properties var canadians: Double {
 USD * ratetoCAD
}
struct Price {
}
var USD: Double
var purchase = Price(USD: 11, ratetoCAD: 1.29)
var ratetoCAD: Double
print(purchase.canadians) // "14.190000000000001"

var canadians: Double {
get {
return USD * ratetoCAD Including the set method for the canadians property we can, for example,
}
} set a new price using the same currency.
}
var purchase = Price(USD: 11, ratetoCAD: 1.29)
print("Price in CAD: \(purchase.canadians)") // "Price in CAD: 14.19"
Listing 3-43: Adding the set method to set a new value
 
struct Price {
The structure defined in Listing 3-41 contains a stored property called var USD: Double
var ratetoCAD: Double
USD to store the price in American dollars, a stored property called var ratetoUSD: Double
ratetoCAD to store the exchange rate for Canadian dollars, and a computed
var canadians: Double {
property called canadians that converts the US dollars into Canadian dollars get {
and returns the result. Computed properties are like methods, they USD * ratetoCAD
}
set { }
USD = newValue * ratetoUSD var purchase = Price(USD: 11, ratetoCAD: 1.29, ratetoUSD: 0.7752)
} 
}
}
var purchase = Price(USD: 11, ratetoCAD: 1.29, ratetoUSD: 0.7752)
purchase.canadians = 500
print("Price: \(purchase.USD)") // "Price: 387.6"

The new structure defined in Listing 3-43 can retrieve and set a price in
Canadian dollars. When we set a new value for the canadians property, the
value is stored in a constant called newValue (the constant is created
automatically for us). Using this constant, we can process the new value
and perform the operations we need. In this example, the value of newValue
is multiplied by the exchange rate to get the price in American dollars. The
price is always stored in American dollars but using the canadians property
we can set it and retrieve it in Canadian dollars.
If we want to use a different name for the new value, we can set the
parameter’s name between parentheses. In the following example, the
parameter was called CAD and used instead of newValue to calculate the
value for the USD property.
Listing 3-44: Using a different name for the parameter of the set method

struct Price {
var USD: Double
var ratetoCAD: Double
var ratetoUSD: Double
Property Observers last value, we declare property observers for the price property. Every time
 a new value is assigned to the property, the willSet() and didSet() methods are
executed. Swift automatically creates a parameter called newValue for the
willSet() method to provide access to the value that is going to be assigned
The properties of an instance of a structure may be modified at any
moment by different processes, such as in response to user interaction or to the property, and a parameter called oldValue for the didSet() method to
events produced by the system. To inform an instance that one of its provide access to the property’s old value after the new value was assigned
properties was modified, Swift introduces Property Observers. (we can change the names of these parameters as we did for the set()
Property Observers are special methods, similar to get() and set(), that can method in Listing 3-44). In our example, when the willSet() method is
be added to a property to execute code before and after a value is assigned executed, the current value of price is subtracted from newValue to get the
to it. The methods are called willSet() and didSet(), and are declared between difference, and the result is assigned to the increment property. And in the
braces after the properties declaration. didSet() method, we assign the old price provided by oldValue to the oldprice
property to have access to the item's previous price later.
Listing 3-45: Adding observers to a property

struct Price {
var increment: Double = 0
var oldprice: Double = 0
Generic Structures angle brackets, but this is only required when the initialization doesn't
include any value. For example, the following code creates an instance of
 the same structure but with a string and lets Swift infer the generic data
type from the value.
At the beginning of this chapter, we explained how to create generic
functions. These are functions that can process values of different data Listing 3-49: Using generic structures
types. The function defines a placeholder for the data type and then 
adopts the data type of the received value. But generics data types are not struct MyStructure<T> {
exclusive to functions, we can also turn data types themselves, such as var myvalue:T
structures, into generic types. The advantage is that we can create func description() {
independent processing units that can handle different types of values. To print("The value is: \(myvalue)") // "The value is: Hello"
}
create a generic structure, we must declare the generic data type after the
}
name of the structure and between angle brackets, as we did for functions. let instance = MyStructure(myvalue: "Hello")
instance.description()

Listing 3-48: Defining generic structures

IMPORTANT: These are basic examples of how to create generic
struct MyStructure<T> {
var myvalue:T data types. As with functions, generics only become useful when we
constrain the data using protocols. We will learn more about
func description() {
print("The value is: \(myvalue)") // "The value is: 5" generics in the following sections and study protocols at the end of
} this chapter.
}
let instance = MyStructure<Int>(myvalue: 5)
instance.description()

Including properties and methods inside a structure and then assigning an Listing 3-50: Initializing variables with standard initializers
instance of that structure to a variable is a simple way to wrap data and 
functionality in a single portable unit of code. Structures are usually used var mynumber = Int(25)
this way, as practical wrappers of code, and Swift takes advantage of this var myprice = Double(4.99)

feature extensively. In fact, all the primitive data types defined in Swift are
structures. The syntax variable: Int = value, for example, is a shortcut provided These initializers create a structure for each value and assign the instances
by Swift for the initializer variable = Int(value). Every time we assign a new to the variables. This is the same as assigning the values directly to the
value to a variable of a primitive data type, we are assigning a structure variable (e.g., var myprice = 4.99). There is no advantage in using initializers for
that contains that value. The following are the initializers of some of the primitive data types except when the value provided is of a different type.
primitive data types studied in Chapter 2. The definitions of these structures include several initializers that convert
the value to the right type. This is usually called casting, and we can use it
Int(Value)—This is the initializer of the Int data type. The argument is to turn a variable of one data type into another. For example, when we
the value we want to assign to the instance. If no value is provided, divide numbers, the system converts those numbers to the right type and
the value 0 is assigned by default. Initializers for similar types are also performs the operation, but variables are already of a specific type and
available (Int8(), Int16(), Int32(), and Int64()). therefore they must be explicitly converted before the operation is
UInt(Value)—This is the initializer of the UInt data type. The performed or we get an error (The process does not really convert the
argument is the value we want to assign to the instance. If no value is variable; it just creates a new value of the right type).
provided, the value assigned is 0. Initializers for similar types are also
available (UInt8(), UInt16(), UInt32(), and UInt64()). Listing 3-51: Casting a variable

Float(Value)—This is the initializer of the Float data type. The
var number1: Int = 10
argument is the value we want to assign to the instance. If no value is var number2: Double = 2.5
provided, the value 0.0 is assigned by default. var total = Double(number1) / number2 // 4.0

Double(Value)—This is the initializer of the Double data type. The
argument is the value we want to assign to the instance. If no value is The variables number1 and number2 defined in Listing 3-51 are of type Int
provided, the value assigned is 0.0. and Double. To perform a division between them we must cast one of them
to the data type of the other (arithmetic operations cannot be performed
on values of different data type). Using the Double() initializer, we create a max—This type property returns the maximum value the data type
new value of type Double from the value of number1 and perform the can handle.
operation (the value 10.0 created by the initializer is divided by the value random(in: Range)—This type method returns a random number.
2.5 of number2 to get the result 4.0). The process is described as "casting the The value is calculated from a range of integers provided by the in
number1 variable to a Double".
argument.
These initializers are also useful when working with String values.
Sometimes the characters of a string represent numbers that we need to negate()—This method inverts the sign of the value.
process. The problem is that strings cannot be processed as numbers. We isMultiple(of: Int)—This method returns true if the value is a
cannot include a string in an arithmetic operation without first converting multiple of the value provided by the of argument (this is similar to
the string into a value of a numeric data type. Fortunately, the initializers what we can achieve with the % operation).
for numeric types such as Int and Double can convert a value of type String
into a number. If the operation cannot be performed, the initializer returns The min and max properties are especially useful because they allow us
nil, so we can treat it as an optional value. In the following example, we to determine whether an operation could overflow a variable (produce a
convert the string "45" into the integer 45 and add the value 15 to it. result that is greater or lesser than the minimum and maximum allowed).
Listing 3-52: Extracting numbers from strings Listing 3-53: Checking the maximum possible value for the Int8 type
 
var units = "45" var mynumber: Int8 = 120
let increment: Int8 = 10
if let number = Int(units) {
let total = number + 15 if (Int8.max - mynumber) >= increment { // (127 - 120) >= 10
print("The total is \(total)") // "The total is 60" mynumber += increment
} }
 print(mynumber) // "120"

The structures defined for primitive data types also have their own
properties and methods. This includes type properties and methods. For This example takes advantage of the max property to make sure that
instance, the following are the most frequently used properties and incrementing the value of a variable will not overflow the variable (the
methods provided by the structures that process integer values (e.g., Int). result will not be greater than the maximum the variable can handle). The
code starts by defining a variable of type Int8 to store the result of the
min—This type property returns the minimum value the data type operation and another to store the number we want to add. Then, we
can handle. calculate how far the current value of mynumber is from the maximum value
admitted by an Int8 variable (Int8.max – mynumber) and compare this result
with the value of increment. If the number of units we have left is greater or
equal than the value of increment, we know that the operation can be Listing 3-54: Rounding floating-point values
performed without going over the limit (in this example, the operation is 
not performed because the addition of 120 + 10 produces a result greater var mynumber: Double = 2.890
mynumber = mynumber.rounded(.toNearestOrAwayFromZero)
than the limit of 127 admitted by Int8). print("The round number is \(mynumber)") // "The round number is 3.0"
The type Double also includes its own selection of properties and 
methods. The following are the most frequently used.
Of course, Boolean values are also structures. Among others, the Bool
pi—This type property returns the value of the constant pi. data type offers the following methods.
infinity—This type property returns an infinite value.
toggle()—This method toggles the value. If the value is true, it
minimum(Double, Double)—This type method compares the becomes false and vice versa.
values provided by the arguments and returns the minimum.
random()—This type method returns a random Bool value.
maximum(Double, Double)—This type method compares the
values provided by the arguments and returns the maximum. The following example checks the current value of a variable and changes
random(in: Range)—This type method returns a random number. the value to false if it is true.
The value is calculated from a range of values of type Double provided
by the in argument. Listing 3-55: Changing the value of a Bool variable

negate()—This method inverts the sign of the value.
var valid: Bool = true
squareRoot()—This method returns the square root of the value. if valid {
print("It is Valid")
remainder(dividingBy: Double)—This method returns the valid.toggle()
}
remainder produced by dividing the value by the value specified by print(valid) // false
the dividingBy argument. 
In this case, the most useful method is probably rounded(). With this
method, we can round a floating-point value to the nearest integer.
When we declare a range using these operators, Swift creates the proper Of course, we can also invert that loop.
structure according to the operator involved. A structure of type Range is
created for an open range and a structure of type ClosedRange is created for Listing 3-57: Inverting a range
a closed range. These structures provide common properties and methods 
to work with the range. The following are the most frequently used. var message = ""
var range = 0..<10
lowerBound—This property returns the range's lower value (the for item in range.reversed() {
message += "\(item) "
value on the left). }
print(message) // "9 8 7 6 5 4 3 2 1 0 "
upperBound—This property returns the range's upper value (the 
value on the right).
This example creates a range from 0 to 9 with the ..< operator and then As mentioned before, ranges are used by the random() method of
calls the reversed() method to invert it. This method creates a collection with primitive data types to get a random value. The following example
the values in reverse order, so we can read it with a for in loop. The generates a loop that calculates multiple random values from 1 to 10. The
statement inside the loop adds the values to the message string, and this condition stops the loop when the number returned by the method is
string is printed at the end to confirm that the values were effectively equal to 5. Inside the loop, we also increment the value of the attempts
reversed. variable to calculate the number of cycles required for the random() method
Ranges can also simplify switch statements that have to consider multiple to return our number.
values per case.
Listing 3-59: Calculating random numbers
Listing 3-58: Using range operators in a switch statement 
 var mynumber: Int = 0
var age = 6 var attempts = 0
var message = "You have to go to "
while mynumber != 5 {
switch age { mynumber = Int.random(in: 1...10)
case 2...4: attempts += 1
message += "Day Care" }
case 5...11: print("It took \(attempts) attempts to get the number 5")
message += "Elementary School" 
case 12...17:
message += "High School"
case 18..<22:
message += "College"
case 22...:
message += "Work"
default:
message += "Breastfeeding"
}
print(message) // "You have to go to Elementary School"

index(after: Index)—This method increments the index specified by Listing 3-63: Getting the next index
the after argument one unit and returns a new Index value with the 
Listing 3-62: Calculating a specific index Listing 3-64: Inserting a character in a string
 
var text = "Hello World" var text = "Hello World"
if text != "" { text.insert("!", at: text.endIndex)
let start = text.startIndex
let newIndex = text.index(start, offsetBy: 6) print("New string is \(text)") // "New string is Hello World!"

print("The character is \(text[newIndex])") // "The character is W"
}
 If we do not know where the character is located, we can find the index
with the index() method. The value returned by this method is an optional
The index() method applied in Listing 3-62 takes an integer to calculate the containing the Index value of the first character that matches the argument
new index. The original index is increased the number of units indicated by or nil if no character is found. In the following example, we implement it to
the integer and the resulting Index value is returned. With this index, we get find the first space character and remove it with the remove() method.
the character at the position 6 (indexes start from 0).
If we wanted to get the previous index, we could have specified a negative Listing 3-65: Removing a character
number of units for the offset value, but another way to move forward and 
backward is to implement the other versions of the index() method. The var text = "Hello World"
var findIndex = text.firstIndex(of: " ")
following example gets the next index after the initial index and prints the
corresponding character on the screen. if let index = findIndex {
text.remove(at: index)
print("New string is \(text)") // "New string is HelloWorld" findIndex = text.firstIndex(of: " ")
} if let start = findIndex {
 text.removeSubrange(start...) // "Goodbye"
}

If we want to work with groups of characters, we must implement ranges
of Index values.
The replaceSubrange() method in Listing 3-67 replaces the characters from the
beginning of the string up to the character before the space character
Listing 3-66: Getting a range of characters
("Hello") with the string "Goodbye", and the removeSubrange() method uses

var text = "Hello World"
an open range to remove the characters of this sentence from the space
var start = text.startIndex character to the end of the string (" World"), getting the final string
var findIndex = text.firstIndex(of: " ")
"Goodbye". Notice that after applying the methods over the same string,
if let end = findIndex { the indexes are lost and therefore they must be recalculated. That is why
print("First word is \(text[start..<end])") //"First word is Hello"
} before calling the removeSubrange() method we search for the position of the
 space character once more and update the findIndex variable.
The rest of the methods provided by the String structure are
The firstIndex() method in Listing 3-66 looks for a space character and
straightforward. For instance, the following example implements two of
returns its index. With this value, we can create a range from the first
them to check if a string contains the word "World" at the end and
character to the space character and get the first word. But we must be
converts all the letters into uppercase letters.
careful because the end index is pointing to the space character, not to the
last character of the word. To get the word without the space, we create an
open range with the..< operator, so the character on the right is not Listing 3-68: Implementing String methods

included.
let text = "Hello World"
We can also use ranges to replace or remove parts of the text. The String
structure offers the replaceSubrange() and removeSubrange() methods for this if text.hasSuffix("World") {
purpose. print(text.uppercased()) // "HELLO WORLD"
}

Listing 3-67: Working with ranges of characters

var text = "Hello World"
var start = text.startIndex
var findIndex = text.firstIndex(of: " ")
Array Structures frequently used is declaring the array with initial values enclosed in square
 brackets and separated by comma.
of those generic data types collections is Array. var list = [15, 25, 35]

Arrays are collections that contain an ordered list of values. They are
generic structures that have the capacity to store all the values we need of
The list array declared in these examples was initialized with three integer
any data type we want, but with the condition that once a data type is
values, 15, 25 and 35. The values of an array are usually called elements or
selected, all the values must be of that same type. For example, if we
items. On these terms, we can say that the code of Listing 3-70 declares an
create an array of type Int, we will only be able to store values of type Int in
array of three elements of type Int.
it. Swift offers multiple syntaxes to create an array, including the following
An index is assigned to each value automatically, starting from 0, and as
initializers.
with strings, we must specify the index of the value we want to read
surrounded by square brackets.
Array<Type>()—This initializer returns an empty Array structure of
the data type indicated by the value of Type. Listing 3-71: Reading the array’s elements
Array(repeating: Value, count: Int)—This initializer returns an 
Arraystructure with copies of the same value. The repeating var list = [15, 25, 35]
print(list[1]) // 25
argument determines the value to copy, and the count argument

determines how many copies the array will contain.
The last statement of Listing 3-71 prints the value of the second element of
A shortcut to create an array is to declare the data type between square
the list array (the element at index 1) on the console. We can also use
brackets followed by parentheses (e.g., var list = [Int]()), but the most
indexes to modify the values of an array.
Listing 3-72: Assigning a new value to an element Listing 3-75: Creating multidimensional arrays
 
var list = [15, 25, 35] var list: [[Int]] = [[2, 45, 31], [5, 10], [81, 12]]
list[0] = 400 
print(list) // [400, 25, 35]

This example creates an array of arrays of integers (notice the declaration
Assigning new values is only possible for elements that already exist in the of the array inside another array [[Int]]). To access the values, we must
array. To add a new element, or several, we can use the += operator. declare the indexes of each level in square brackets, one after another. The
following example returns the first value (index 0) of the second array
Listing 3-73: Adding new elements to an array (index 1). The instruction looks for the array at index 1 and then gets the
 number at index 0.
var list = [15, 25, 35]
list += [45, 55] Listing 3-76: Reading values from a multidimensional array
print(list) // [15, 25, 35, 45, 55]


var list: [[Int]] = [[2, 45, 31], [5, 10], [81, 12]]
print(list[1][0]) // 5
The += operator adds an array at the end of another array. In Listing 3-73, 
we use it to add two more elements to the array declared in the first
statement. What the += operator does is to concatenate two arrays and To remove all the elements from an array, we can assign to the variable
one of the initializers introduced before or just square brackets with no
assign the result back to the same variable. If we want to use two or more
values.
arrays to create a new one, we can apply the + operator.
Listing 3-77: Removing the elements of an array
Listing 3-74: Concatenating two arrays 

var list = [15, 25, 35]
var list1 = [15, 25, 35] list = []
var list2 = [45, 55, 65] 
var final = list1 + list2 // [15, 25, 35, 45, 55, 65]

Arrays are collections of values and therefore we can iterate over their
It is possible to declare arrays of arrays. These types of arrays are called values with a for in loop, as we did with strings before.
multidimensional arrays. Arrays inside arrays are listed separated by
Listing 3-78: Reading an array with a for in loop
comma.

var total = 0 removeFirst()—This method removes the first element of the array.
let list = [15, 25, 35]
It returns the value of the element deleted.
for value in list {
total += value removeLast()—This method removes the last element of the array.
} It returns the value of the element deleted.
print("The total is \(total)") // "The total is 75"
 removeAll(where: Closure)—This method removes the elements
in the array that meet the condition established by the closure
The code in Listing 3-78 uses a for in loop to add the numbers of the list assigned to the where argument.
array to the total variable. At the end, we print the result. Although this is a removeSubrange(Range)—This method removes a range of
legit way to do it, arrays offer multiple properties and methods to read and elements from the array. The argument is a range of integers
process their values. representing the indexes of the elements to remove.
replaceSubrange(Range, with: Array)—This method replaces a
count—This property returns the total number of elements in the range of elements with the elements of the array provided by the
array.
with argument. The first argument is a range of integers
isEmpty—This property returns a Boolean value that indicates if the corresponding to the indexes of the elements we want to replace.
array is empty.
dropFirst(Int)—This method removes the number of elements
first—This property returns the first element of the array or nil if the specified by the argument from the beginning of the array. If no
array is empty. amount is declared, only the first element is removed.
last—This property returns the last element of the array or nil if the dropLast(Int)—This method removes the number of elements
array is empty. specified by the argument from the end of the array. If no amount is
append(Element)—This method adds the value specified by the declared, only the last element is removed.
argument at the end of the array. enumerated()—This method is used to iterate over the elements of
insert(Element, at: Int)—This method adds a new element in a the array. It returns a tuple containing the index and the value of the
specific position of the array. The first argument is the value we want current element.
to assign to the new element, and the at argument represents the min()—This method compares the values of the elements and
position of the array where we want to insert the element. returns the smallest.
remove(at: Int)—This method removes an element from the array max()—This method compares the values of the elements and
at the index specified by the at argument. returns the largest.
sorted()—This method returns an array with the elements of the first argument is the value that is going to be processed with the first
array in ascending order. value of the array.
sorted(by: Closure)—This method returns an array with the contains(where: Closure)—This method returns a Boolean that
elements of the array in the order determined by the closure provided determines if the array contains an element that meets the condition
to the by argument. in the closure.
randomElement()—This method randomly selects an element from allSatisfy(Closure)—This method returns a Boolean value that
the array and returns it. If the array is empty, the value returned is nil. determines if all the elements of the array comply with the requisites
shuffled()—This method returns an array with the elements of the of a closure.
array in random order. difference(from: Array)—This method returns a CollectionDifference
reversed()—This method returns an array with the elements of the structure containing all the changes that has to be performed to
array in reverse order. synchronize the array with the array provided by the from method.
This method can work in conjunction with the applying() method to
swapAt(Int, Int)—This method exchanges the values of the
apply all the changes in the array at once.
elements at the indexes specified by the arguments.
joined(separator: String)—This method returns a string that In the previous example, we have seen how to iterate over the elements of
includes all the values in an array of strings joined by the string an array with the for in loop, but that iteration only returns the value of the
specified by the separator argument. element, not its index. An alternative is provided by the enumerated()
filter(Closure)—This method filters an array and returns another method, designed to work with these types of loops. Each cycle returns a
array with the values that passed the filter. The argument is a closure tuple with the index and the value of the current element.
that processes the elements and returns a Boolean indicating whether
the value passed the test or not. Listing 3-79: Reading indexes and values of an array

map(Closure)—This method returns a new array containing the
let fruits = ["Banana", "Orange", "Apple"]
results of processing each of the values of the array. var message = "My fruits:"
compactMap(Closure)—This method returns a new array
for (myindex, myfruit) in fruits.enumerated() {
containing the results of processing each of the values of the array, message += " \(myindex + 1)-\(myfruit)"
but ignores the values that produce a nil result. }
print(message) // "My fruits: 1-Banana 2-Orange 3-Apple"
reduce(Value, Closure)—This method sends the values of the array 
to the closure one by one and returns the result of the operation. The
This example uses the constants myindex and myfruit to capture the values
produced by the enumerated() method and generates a string. Notice that
since the array’s indexes start from 0, we added 1 to myindex to start and the index 1 to the third element. The system makes sure that
counting from 1. the indexes are always consecutive and start from 0.
Another useful property is count. As mentioned before, we can access each
element of the array with the index between square brackets. But trying to A more complex method is removeAll(where:). This method removes several
read a value in an index that has not yet been defined will return an error. elements at once, but only those that meet a condition. The condition is
To make sure that the index exists, we can check whether it is greater than established by a closure that processes each of the values in the array and
returns true or false depending on whether the value meets the condition or
0 and less than the total amount of elements in the array using this
not. In the following example, we compare each value with the string
property.
"Orange" and therefore all the values "Orange" are removed from the
array.
Listing 3-80: Checking whether an array contains a value in a specific index

Listing 3-82: Removing all the elements that meet a condition
let ages = [32, 540, 12, 27, 54]
let index = 3 
if index > 0 && index < ages.count { var fruits = ["Banana", "Orange", "Apple", "Orange"]
print("The value is: \(ages[index])") // "The value is: 27" fruits.removeAll(where: { (value) in
} value == "Orange"
 })
print(fruits) // ["Banana", "Apple"]

The methods to add and remove elements from an array are
straightforward. The following example illustrates how to implement them.
Another method that work with a closure is contains(where:). In the following
Listing 3-81: Adding and removing elements example, we use this method to determine whether an array contains a
 value greater than 60 or not.
var fruits = ["Banana", "Orange"]
if !fruits.isEmpty { Listing 3-83: Finding if an element meets a condition
fruits.append("Apple") // ["Banana", "Orange", "Apple"]

fruits.removeFirst() // "Banana"
fruits.insert("Pear", at: 1) // ["Orange", "Pear", "Apple"] var list = [55, 12, 32, 5, 9]
fruits.insert(contentsOf: ["Cherry", "Peach"], at: 2) let found = list.contains(where: { (value) in
// ["Orange", "Pear", "Cherry", "Peach", "Apple"] value > 60
} })
 print(found) // false

Another random operation is performed by the shuffled() method. With this Listing 3-87: Casting arrays of type ArraySlice
method we can randomly sort the elements of an array. 
var fruits = ["Banana", "Orange", "Apple", "Cherry"]
var someFruits = fruits[0..<2] // ["Banana", "Orange"]
Listing 3-85: Changing the order of the elements of an array var newArray = Array(someFruits)
 
var fruits = ["Banana", "Orange", "Apple"]
fruits = fruits.shuffled() The Array structure also offers the removeSubrange() and replaceSubrange()
print(fruits) // e.g.: ["Orange", "Apple", "Banana"]
methods to remove and replace a range of elements.

Besides working with all the elements of an array, we can do it with a range Listing 3-88: Removing and replacing elements
of elements. 
var fruits = ["Banana", "Orange", "Apple", "Banana", "Banana"]
fruits.removeSubrange(1...2)
Listing 3-86: Reading a range of elements fruits.replaceSubrange(0..<2, with: ["Cherry", "Cherry"])
 print(fruits) // "["Cherry", "Cherry", "Banana"]"

var fruits = ["Banana", "Orange", "Apple", "Cherry"]
var someFruits = fruits[0..<2] // ["Banana", "Orange"]
In Listing 3-88, we call the removeSubrange() method to remove the range of
print("The new selection has \(someFruits.count) fruits")

elements from index 1 to 2, getting an array filled with the value "Banana",
and then we call the replaceSubrange() method to replace the elements from
This example gets the elements at the indexes 0 and 1 from the fruits array index 0 to 1 with another array filled with "Cherries". This is just to
and assigns them to the new someFruits array. Now we have two arrays: fruits illustrate how the methods work, but it shows a recurrent situation in app
with 4 elements and someFruits with 2. development where sometimes we need to fill a collection with elements
of the same value. When working with arrays, this is easy to achieve. The
Array structure includes an initializer that takes two arguments, repeating and value "Grape", and returns a Boolean with the result. If the value is true, the
count, and generates an array with the number of elements indicated by element is included in filteredArray.
count and the value indicated by repeating. If what we need is to modify the elements of an array all at once, we can
use the map() method. This method sends to a closure the values of the
Listing 3-89: Initializing an array with elements of the same value array one by one and returns another array with the results produced by
 the closure.
var fruits = ["Banana", "Orange", "Apple"]
Listing 3-91: Mapping an array
let total = fruits.count
let newArray = Array(repeating: "Cherry", count: total) 
fruits.replaceSubrange(0..<total, with: newArray) let list = [2, 4, 8, 16]
let half = list.map({ $0 / 2 })
print(fruits) // "["Cherry", "Cherry", "Cherry"]" print(half) // "[1, 2, 4, 8]"
 
In this example, we create an array with the same amount of elements as The example in Listing 3-91 defines a list of integers and then calls the map()
the fruits array and then use the replaceSubrange() method to replace every method on the array to divide each value by 2. The map() method sends the
element with a new one. values of the array to the closure one by one, the closure replaces the
The methods to remove and replace elements of an array are not selective placeholder ($0) with the current value, divides the number by 2, and
enough; they affect the elements in a specific index or a range of indexes returns the result. All the results are stored in a new array and that array is
without considering their values. If we want to perform a more specific job, returned by the map() method when the process is over.
we must use the filter() method. This method takes a closure and sends Of course, we can perform any kind of operations on the values in the
each element to the closure for processing. If the closure returns true, the closure. For instance, the following code converts the values into strings
element is included in the new array, otherwise it is ignored, as shown with the String() initializer.
next.
Listing 3-92: Converting the elements of an array into strings
Listing 3-90: Filtering the values of an array 
 let list = [1, 2, 3, 4, 5]
var fruits = ["Apple", "Grape", "Banana", "Grape"] let listtext = list.map({ String($0) })
var filteredArray = fruits.filter({ $0 != "Grape" }) print(listtext) // "["1", "2", "3", "4", "5"]"
print(filteredArray) // "["Apple", "Banana"]" 

When all we want to do is to initialize a new structure with the value
The filter() method sends the values one by one to the closure, the closure received by the closure, instead of a closure, Swift allows us to provide the
replaces the placeholder ($0) with the current value, compares it with the structure's initializer. The value received by the closure is sent to the
initializer and a new structure of that type is returned.
returns a new array with the same elements in reversed order. The value
Listing 3-93: Using a structure initializer with the map() method returned by the method is stored in a structure of type ReversedCollection. As
 we did before with the ArraySlice type, we can cast these values as Array
let list = [1, 2, 3, 4, 5] structures with the Array() initializer.
let listtext = list.map(String.init)
print(listtext) // "["1", "2", "3", "4", "5"]"
 Listing 3-95: Reversing the elements of an array

This example produces the same result as before, but instead of using a var fruits = ["Apple", "Blueberry", "Banana"]
closure, we use a reference to the String initializer. The map() method sends var array = Array(fruits.reversed()) // ["Banana", "Blueberry", "Apple"]

the values to the initializer, the initializer returns a new String structure with
that value, and the process continues as always. The sorted() method sorts the array in ascending order and returns a new
Another way to process all the values of an array at once is with the reduce()
array.
method. This method works in a similar way than map(), but instead of
storing the results in an array, it sends the result back to the closure to get
Listing 3-96: Sorting the elements of an array
only one value in return. For instance, the following code uses the reduce() 
method to get the result of the addition of all the numbers in an array.
var fruits = ["Blueberry", "Apple", "Banana"]
let basket = fruits.sorted()
Listing 3-94: Reducing an array print(basket) // ["Apple", "Banana", "Blueberry"]


let list = [2, 4, 8, 16]
let total = list.reduce(0, { $0 + $1 })
If we want to sort the elements in a custom order, we can use the sorted(by:)
print(total) // "30" method. This method works in a similar fashion to the filter() method
 studied before. It takes a function or a closure that receives the value of
two elements and returns true if the first element should appear before the
The code in Listing 3-94 defines an array of integers and then calls the
second element, or false otherwise.
reduce() method on it. This method sends two values at a time to the
closure. In the first cycle, the values sent to the closure are the ones Listing 3-97: Sorting the elements of an array in a custom order
provided by the first argument (0) and the first value of the array (2). In the

second cycle, the values sent to the closure are the value returned by the
var fruits = ["Apple", "Raspberry", "Banana", "Grape"]
closure in the first cycle (0 + 2 = 2), and the second value of the array (4). var newArray = fruits.sorted(by: { $0 > $1 })
The loop goes on until all the values of the array are processed. print(newArray[0]) // "Raspberry"

When it comes to sorting the elements of an array, there are several
options available. The most frequently used are reversed() and sorted() (and its
variant sorted(by:)). The reversed() method takes the elements of an array and
matches the value of the of argument. let ages = [32, 540, 12, 27, 54]
let first = ages.firstIndex(where: { $0 < 30 })
firstIndex(where: Closure)—This method returns the index of the if first != nil {
print("The first value is at index \(first!)") // 2
first value that meets the condition in the closure assigned to the }
where argument. 
If what we need instead is to get the index of a value that meets certain
condition, we can use methods like firstIndex(where:) or lastIndex(where:)
depending on whether we want to search from the beginning or the end of
the array.
Set Structures To process the elements of a set, we can use a for in loop, as we did
before with strings and arrays, but sets also provide their own properties

and methods for this purpose.
If we store two elements in an array, one element will automatically
receive the index 0 and the other the index 1. This correlation between
count—This property returns the number of elements in the set.
indexes and values never changes, allowing elements to be listed always in isEmpty—This property returns a Boolean that indicates whether the
the right order and have elements with the same value at different indexes. set is empty or not.
Sets are like arrays, but they do not assign an index to their values and contains(Element)—This method returns a Boolean that indicates
therefore there is no order or duplicated values. Sets are created from the whether there is an element in the set with the value specified by the
Set structure. argument.
contains(where: Closure)—This method returns a Boolean that
Set<Type>()—This initializer returns an empty Set structure of the
determines if the set contains an element that meets the condition in
data type indicated by the value of Type.
the closure.
This initializer can be used to create an empty set (e.g., let myset = Set<Int>()), min()—This method compares the elements in the set and returns
but we can also use square brackets, as we do with arrays. The difference the smallest.
with arrays is that we must declare that we are creating a set with the Set max()—This method compares the elements in the set and returns
keyword, as shown next. the largest.
sorted()—This method returns an array with the elements of the set
Listing 3-104: Creating an empty set of integers in ascending order.

var ages: Set<Int> = []
sorted(by: Closure)—This method returns an array with the
 elements of the set in the order determined by the closure specified
by the by argument.
If we initialize the set with some values, Swift can infer its type from the randomElement()—This method randomly selects an element from
values’ data type, simplifying the declaration. the set and returns it. If the set is empty, the value returned is nil.
with the values of the original set that match the values provided by if !fruits.contains("Grape") {
the argument (an array or another set). fruits.insert("Grape")
}
remove(Element)—This method removes from the set the element print("The set has \(fruits.count) elements") // 4
with the value provided by the argument. 
Using these methods, we can easily access and modify the values of a
set. For instance, we can implement the contains() method to search for a
value.
if fruits.contains("Apple") {
print("Apple exists!")
In Listing 3-107, we use the contains() method again to check if an The rest of the methods available are straightforward. The following
element with the value "Grape" already exists in the set, but this is not example joins two sets with the union() method and then subtracts
really necessary. If the value is already part of the set, the insert() method elements from the result with subtract().
does not perform any action.
To remove an element we must call the remove() method. Listing 3-110: Combining sets

Listing 3-108: Removing an element from a set var fruits: Set = ["Apple", "Banana"]
var newSet = fruits.union(["Grapes"]) // "Banana", "Grapes", "Apple"

newSet.subtract(["Apple", "Banana"]) // "Grapes"
var fruits: Set = ["Apple", "Orange", "Banana"] 
There is only one way to access the elements of an array and that is Listing 3-113: Declaring a dictionary with type inference
through their numeric indexes. Dictionaries offer a better alternative. With 
dictionaries, we can define the indexes ourselves using any custom value var list = ["First": "Apple", "Second": "Orange"]
we want. Each index, also known as key, must be explicitly declared along 
with its value. Swift offers multiple syntaxes to create a dictionary,
including the following initializers. As with arrays, if we want to read or replace a value, we must declare the
key (index) in square brackets after the name of the dictionary.
Dictionary<Type1: Type2>()—This initializer returns an empty
structure with the keys and values of the data type indicated
Dictionary
Listing 3-114: Assigning a new value to an element of a dictionary

by the value of Type1 and Type2.
var list = ["First": "Apple", "Second": "Orange"]
Dictionary(grouping: Collection, by: Closure)—This initializer list["Second"] = "Banana"

returns a Dictionary structure with the values provided by the grouping
argument grouped in arrays according to the keys returned by the
The second statement in Listing 3-114 assigns a new value to the element
closure provided by the by argument. identified with the keyword "Second". Now, the dictionary contains two
elements with the values "Apple" and "Banana". If the keyword used to
If the data types are explicitly defined, we can also declare an empty assign the new value exists, the system updates the value, but if the
dictionary with a simplified syntax, as in var list: [String: String] = Dictionary(), or keyword does not exist, a new element is created, as shown next.
use square brackets with a colon, as in var list: [String: String] = [:]. This
shortcut is also used to define a dictionary with initial values. In this case, Listing 3-115: Adding a new element to a dictionary
the keys and values are separated by a colon and the items are separated 
by comma, as in the following example. var list = ["First": "Apple", "Second": "Orange"]
list["Third"] = "Banana"
Listing 3-112: Declaring a dictionary with initial values print(list) // "["Second": "Orange", "First": "Apple", "Third": "Banana"]"
 
Dictionaries return optional values. If we try to read an element with a containing the key and the element’s value.
keyword that does not exist, the value returned is nil.
Listing 3-119: Using for in to iterate over a dictionary
Listing 3-116: Reading an element that does not exist 
 var fruits = ["First": "Apple", "Second": "Orange"]
var message = "My fruits:"
var list = ["First": "Apple", "Second": "Orange"]
print(list["Third"]) // nil
for (myindex, myfruit) in fruits {

message += " \(myindex)-\(myfruit)"
}
The code in Listing 3-116 tries to read a value with the keyword "Third" print(message) // "My fruits: First-Apple Second-Orange"

that does not exist in the list dictionary. As a result, the value nil is printed
on the console. If the element exists and we want to read its value, we The for in loop in Listing 3-119 reads the elements of the fruits dictionary
must unwrap it. one by one, assigns the index and the value to the myindex and myfruit
constants, and adds their values to the message variable, so at the end we
Listing 3-117: Reading the values
get a string with all the keys and values in the dictionary.

Of course, dictionaries may also contain arrays as values. The declaration is
var list = ["First": "Apple", "Second": "Orange"]
if let first = list["First"], let second = list["Second"] {
simple, the key is declared as always, and the single value is replaced by an
print("We have \(first) and \(second)") // "We have Apple and Orange" array.
}

Listing 3-120: Combining dictionaries with arrays

Since dictionary elements are optionals, we can assign the value nil to
var fruits: [String: [String]] = ["A": ["Apple", "Apricot"], "B": ["Banana", "Blueberries"]]
remove them. The following example removes the element with the

keyword "First".
Reading the values of a dictionary like this is a bit more complicated.
Listing 3-118: Removing an element from a dictionary Because dictionaries return optionals, we cannot just specify the indexes as
 we do for multidimensional arrays (see Listing 3-76). The value returned by
var list = ["First": "Apple", "Second": "Orange"] the dictionary must be unwrapped before accessing its values.
list["First"] = nil

Listing 3-121: Reading arrays inside dictionaries
As with arrays and sets, we can also iterate over the values of a dictionary 
with a for in loop. The value produced by each cycle of the loop is a tuple var fruits: [String: [String]] = ["A": ["Apple", "Apricot"], "B": ["Banana", "Blueberries"]]
if let list = fruits["A"] {
print(list[0]) // "Apple"
} isEmpty—This property returns a Boolean value that indicates if the

dictionary is empty.
In this example, we create a dictionary with two values. The values are keys—This property returns a collection with the keys in the
arrays of strings with a string as key. The code gets the array corresponding dictionary.
to the "A" key, unwraps it, and stores it in a constant. The list constant now values—This property returns a collection with the values in the
contains the array assigned to the "A" key, and therefore when we read the dictionary.
element at index 0 of that array, we get the value "Apple". sorted(by: Closure)—This method returns an array of tuples with
This is actually how the Dictionary(grouping:, by:) initializer works. It takes the each element of the dictionary (key and value) in the order
values of a collection and groups them together in arrays according to the determined by the closure.
value of a key returned by the closure, as in the following example. randomElement()—This method randomly selects an element from
the dictionary and returns a tuple with its key and value. If the
Listing 3-122: Grouping values by a key
dictionary is empty, the value returned is nil.

let list = [15, 25, 38, 55, 42]
shuffled()—This method returns an array of tuples containing the
let group5 = Dictionary(grouping: list, by: {$0 % 5 == 0 ? "Yes" : "No"}) keys and values of each element of the dictionary in random order.
print(group5) // "["No": [38, 42], "Yes": [15, 25, 55]]"
 updateValue(Value, forKey: Key)—This method updates the value
of an element with the value and key specified by its arguments. If the
The Dictionary initializer implemented in Listing 3-122 takes the values of the key does not exist, the method creates a new element. It returns the
list array, sends them to the closure one by one, and creates a new previous value if the key exists or nil otherwise.
dictionary with the keys returned by the closure. The closure receives the removeValue(forKey: Key)—This method removes the element
value and returns the strings "Yes" or "No" depending on whether the with the key equal to the value of the forKey argument. It returns an
current value is multiple of 5. If the value is multiple of 5, it is included in optional containing the value of the deleted element or nil if no
an array with the key "Yes", otherwise it is included in an array with the key element with the specified key was found.
"No". contains(where: Closure)—This method returns a Boolean value
Dictionaries incorporate plenty of functionality by themselves, but they that determines if the dictionary contains an element that meets the
also include properties and methods to manage their values. The following condition in the closure.
are the most frequently used.
Some of the methods provided by the Dictionary structure are like those
count—This property returns the total number of elements in the included in the Array and Set structures, but others are more specific. For
dictionary.
example, the updateValue() and removeValue() methods require the element's compare the values of the tuples at the index 1 ($0.1 < $1.1). The array
key to process the values. returned is a collection of tuples in alphabetical order, with every element
containing the keys and values of the dictionary ([(key: "two", value: "Apple"),
Listing 3-123: Adding and removing elements from a dictionary (key: "one", value: "Banana"), (key: "three", value: "Pear")]).

Earlier, we saw how to iterate over the elements of a dictionary with a for in
var fruits = ["one": "Banana", "two": "Apple", "three": "Pear"]
fruits.updateValue("Banana", forKey: "three") // "Pear" loop (see Listing 3-119). The loop gets each element and generates a tuple
fruits.removeValue(forKey: "one") // "Banana" with the key and value. But there are times when we only need the
print(fruits) // "["three": "Banana", "two": "Apple"]"
 element’s key or the element’s value. The Dictionary structure provides two
properties for this purpose: keys and values. The properties return a
The updateValue() method updates the value of an element when there is collection containing only the keys or the values of the elements,
already an element with that key or creates a new one if the key does not respectively.
exist. This is the same as assigning a value directly to an element (see
Listings 3-114 and 3-115), but the method returns the previous value, Listing 3-125: Iterating over the dictionary’s keys
which may be useful sometimes. 
Like sets, dictionaries are an unordered collection of values, but we can var fruits = ["one": "Banana", "two": "Apple", "three": "Pear"]
create an array with their elements in a specific order using the sorted() for key in fruits.keys {
method. The method returns the values as tuples, with the element’s key if key == "two" {
print("We have an element with the key ‘two’")
first and the value second. }
}

Listing 3-124: Sorting the values of a dictionary
 The collections returned by the keys and values properties are structures of
var fruits = ["one": "Banana", "two": "Apple", "three": "Pear"] type Keys and Values defined inside the Dictionary structure. As we did before
var list = fruits.sorted(by: { $0.1 < $1.1 })
print(list) with other collection types, to work with their values we can turn them
 into arrays with the Array() initializer.
As with arrays, the sorted() method sends to the closure two values at a Listing 3-126: Reading the keys of a dictionary
time, but the values in a dictionary are sent as tuples containing the key 
and value of each element. For instance, the first values sent to the closure var fruits = ["one": "Banana", "two": "Apple", "three": "Pear"]
in Listing 3-124 are ("one", "Banana") and ("two", "Apple"). These values let keys = Array(fruits.keys)
print(keys)
take the place of the placeholders $0 and $1, so if we want to order the 
elements according to their values (the names of the fruits), we must
3.4 Enumerations Listing 3-129: Declaring a variable of type Number


enum Number {
case one, two, three
Enumerations are a way to create data types with a limited set of values. }
var mynumber: Number = Number.one
An enumeration type is like the Boolean type but with the possible values 
defined by the programmer. They are declared with the enum keyword, and
the values are defined with the case keyword between braces. Variables declared of this type may only have the values allowed by the
type (one, two, or three). To assign a value, we must use the name of the
Listing 3-127: Defining an enumeration type enumeration and dot notation. The mynumber variable declared in Listing 3-

129 is of type Number and has the value one.
enum Number {
case one Once the type of the variable was already defined, only the dot and the
case two value are necessary to modify its value.
case three
}

Listing 3-130: Assigning a new value to a variable of type Number

enum Number {
This example defines an enumeration call Number with three possible case one, two, three
values: one, two, and three. We can assign any names we want for the }
var mynumber = Number.one
enumeration and its values. The values may also be declared just in one mynumber = .two
case statement separated by comma. 
Listing 3-128: Declaring the enumeration values in one statement In the last statement of Listing 3-130, we assign a new value to mynumber.
 The value .two may have been written as Number.two. Both syntaxes are valid,
enum Number { but Swift infers that the value provided is of the same type as the variable’s
case one, two, three
} type, so it is not necessary to declare the name anymore.
 Like Booleans, enumeration types may be used as signals to indicate a state
that can be checked later to decide whether to perform a certain task.
An enumeration is a custom data type. As we did with structures, we must Therefore, they are frequently used with conditionals and loops. The
create a variable of this type and assign to that variable one of the possible following example checks the value of an enumeration variable with a switch
values using dot notation. statement. This statement is particularly useful when working with
enumerations because these data types have a limited set of values, Raw Values
making it easy to define a case for each one of them.

Listing 3-131: Using switch with an enumeration type
The cases of an enumeration can have values by default. These values are

called raw values. Swift assigns values by default to every case, starting
enum Number {
case one from 0, but we can assign our own to each case.
case two
case three
}
Listing 3-132: Assigning raw values to enumeration values
var mynumber = Number.two 
enum Number: String {
switch mynumber {
case one = "Number One"
case .one:
case two = "Number Two"
print("The number is 1")
case three = "Number Three"
case .two:
}
print("The number is 2") // "The number is 2"
var mynumber = Number.one
case .three:

print("The number is 3")
}
 Enumerations behave like structures. We can define our own properties
and methods inside an enumeration, and they also include initializers,
In this example, the Number enumeration is defined and then the properties, and methods by default. The most useful property is called
mynumber variable is declared of this type and the value two is assigned to it. rawValue, which lets us read the raw value of each case.
Next, a switch statement compares the value of this variable with the three
possible values of its type and prints a message on the console. Listing 3-133: Reading raw values

enum Number: String {
case one = "Number One"
case two = "Number Two"
case three = "Number Three"
}
var mynumber = Number.one
print("The value is \(mynumber.rawValue)") // "The value is Number One"

We can read the case value or the raw value to identify an instance of an
enumeration type. In Listing 3-134, we create an instance of Number with
the raw value "Number Two" and then check that the variable contains the
proper case value with an if statement.
What makes enumerations part of the programming paradigm proposed by
Swift is not their capacity to store different types of values but the
possibility to include custom methods and computed properties. The
following example adds a method to our Number enumeration that prints a
message depending on the current value of the instance.
Associated Values parenthesis, as we did with tuples before (see Listing 2-37). If we need to
test a single case, we can use an if or a guard statement and assign the value

to the case we want to test.
At the end of this process, the array stored in the list2 variable will be the 3.5 Objects
same as the one stored in the list1 variable. Of course, we could have

simplified this process by assigning list1 to list2 but knowing exactly which
elements change in one array with respect to the other is useful when we Objects are data types that encapsulate data and functionality in the form
need to perform tasks after a change is applied. of properties and methods, but unlike the structures and enumerations
If we don't need to process the changes one by one but still want to modify introduced before they are stored by reference, which means that more
the array with the values returned by the difference() method, we can call the than one variable can refer to the same object in memory.
applying() method offered by the Array structure to perform all the changes
at once.
The applying() method returns the array with all the changes applied, or
nilif it can't apply the difference to the array, so we have to check this value
and then assign the result back to list2. The result is the same as before, the
array in list2 contains the same values as list1, but with the difference that all
the changes were performed at once.
Definition of Objects In Listing 3-141, the Employee() initializer creates a new instance of the class
Employee. Since in this case the words instance and object are synonyms, we

can say that we have created a new object called employee1 containing two
properties, name and age.
Like structures and enumerations, objects are defined first and then
Of course, we can also modify the values of the properties of an object
instances are created from their definition. The definitions of objects are
from its methods, but unlike structures, the object's methods can modify
called classes, and what we called objects are the instances created from
the properties of their own object without adding anything to the
those classes. Classes are declared the same way as structures or
definition (they do not need to be declared as mutating).
enumerations, but instead of the struct or enum keywords we must use the
class keyword.
Listing 3-142: Modifying properties from the object’s methods

Listing 3-140: Defining a class class Employee {
 var name = "Undefined"
var age = 0
class Employee {
var name = "Undefined"
func changename(newname: String, newage: Int) {
var age = 0
name = newname
}
age = newage

}
}
This example defines a simple class called Employee with two properties: let employee1 = Employee()
employee1.changename(newname: "Martin", newage: 32)
name and age. As always, this does not create anything, it is just defining a print("Name: \(employee1.name)") // "Name: Martin"
new custom data type. To store data in memory in this format, we must 
This example defines an Employee class with two properties: name and age.
The type method declared next is just describing the purpose of the class.
Every time the description() method is executed on the class, a description is
printed on the console. Again, we don't have to create an instance because
the method is executed on the class itself.
IMPORTANT: Classes can also use the static keyword to define type
properties and methods. The difference between the static and class
keywords is that properties and methods defined with the static
keyword are immutable and those defined with the class keyword can
be modified by subclasses. (We will learn about subclasses and
inheritance later in this chapter.)
Reference Types nameproperty is reflected in the other because both constants point to the
same object in memory (they refer to the same instance).

Figure 3-2: Objects stored in memory
Structures and enumerations are value types. This means that every time
we assign a variable of any of these data types to another variable, the
value is copied. For example, if we create an instance of a structure and
then assign that instance to another variable, the instance is copied, and
we end up with two instances of the same structure in memory.
The self keyword in the changename() method of Listing 3-144 represents the
object created from the Employee class and helps the system understand
what we are referring to when we use the word name. When we call the
changename() method in the employee1 object, the value of the name parameter Memory Management
is assigned to the object's name property (self.name). The self keyword in this

example is a reference to the object stored in the employee1 variable. This
would be the same as declaring employee1.name, but since we do not know
the name of the variable that is going to store the instance when the class Because objects are stored by reference, they can be referenced by several
is defined, we must use self instead. variables at the same time. If a variable is erased, the object it references
Another useful application of the self keyword is to reference the data type cannot be erased from memory because another variable could still be
itself. The value generated by reading the self keyword on a data type is using it. This creates a situation in which the device's memory is filled with
called metatype. A metatype refers to the type itself, not an instance of it. objects that are no longer necessary. The solution provided by Apple is an
For example, the value Int.self refers to the definition of the Int data type, automatic system that counts the number of variables referencing an
not an integer number created from that type, as shown in the following object and only removes the object from memory when all the references
example. are erased (all the variables were erased, set to nil, or they were assigned a
reference to another object). The system is called ARC (Automatic
Listing 3-145: Referring to the data type with self Reference Counting). ARC automatically erases the objects when there is

no longer a constant or a variable containing a reference to that space in
let reference = Int.self
let newnumber = reference.init(20) memory.
print(newnumber) // "20" In an ideal scenario, this system works like magic, counting how many

references we create to the same object and erasing that object when
none of those references exist anymore. But there are situations in which
The code in Listing 3-145 stores a reference to the Int data type in a
we can create something called Strong Reference Cycle. This happens when
constant and then uses that constant to create an instance of Int with the
two objects have a property that references the other object.
value 20. Notice that when working with metatypes we must declare the
init() method implicitly to create an instance. Metatypes are widely used to
Listing 3-146: Referencing one object from another
pass references of data types to methods and initializers of other types, as

we will see in further chapters.
class Employee {
var name: String?
var location: Department?
}
class Department {
var area: String?
var person: Employee?
}
var employee: Employee? = Employee()
var department: Department? = Department()
employee?.name = "John"
This example defines two classes: Employee and Department. Both classes
contain a property that references an object of the other class (location and
person). After the definition, objects of each class are created and stored in

the employee and department variables. The reference in the department variable
is assigned to the location property of the employee object, and the reference In this example, we assume that the value nil was assigned to the employee
in the employee variable is assigned to the person property of the department and department variables, and in consequence the objects are not accessible
object. After this, each object contains a reference to the other, as shown anymore, but they are preserved in memory because ARC has no way to
below. know that they are no longer required.
Swift has solved this problem classifying the references into three
Figure 3-3: Objects referencing each other categories: strong, weak, and unowned. Normal references are strong;
they are always valid and the objects they point to are preserved in
memory for as long as they exist. These are the kind of references we have
been using so far, and that is why the cycle created by our example is called
Strong Reference Cycle. The solution to break this cycle is to define one of
the references as weak or unowned. When ARC encounters one of these types
of references to be the last reference to an object, the object is erased
from memory as if the reference had never existed.

Listing 3-147: Assigning weak references
At this point, each object is referenced by a variable and a property. The 
object of the Employee class is referenced by the employee variable and the class Employee {
var name: String?
person property, and the object of the Department class is referenced by the
var location: Department?
department variable and the location property. If, for some reason, we do not }
need to access these objects from our code anymore and erase or modify class Department {
var area: String?
the values of the employee and department variables, ARC will not erase the weak var person: Employee?
objects from memory because their properties still have a reference that }
var employee: Employee? = Employee()
keeps them alive. var department: Department? = Department()
employee?.name = "John" Inheritance
employee?.location = department

department?.area = "Mail"
department?.person = employee
 One of the main purposes of structures and objects is to define pieces of
code that can be copied and shared. The code is defined once and then
In the code of Listing 3-147, the person property was declared as weak. Now, instances (copies) of that code are created every time they are required.
when the references from the variables are erased, the object created This programming pattern works well when we define our own code but
from the Employee class is erased from memory because the only reference presents some limitations when working with code programmed by other
left is the weak reference from the person property. After this object developers and shared through libraries and frameworks. The
disappears, the object created from the Department class does not have any programmers creating the code for us cannot anticipate how we are going
other strong reference either, so it is also erased from memory. to use it and all the possible variations required for every application. To
The unowned reference works the same way, but it differs with the weak provide a solution to this problem, classes incorporate inheritance. A class
reference on the type of values it applies to. Weak references apply to can inherit properties and methods from another class and then improve it
variables with optional values (they can be empty at some point) and adding some properties and methods of its own. This way, programmers
unowned references apply to non-optional values (they always have a can share classes and developers can adapt them to their needs.
value). To illustrate how inheritance works, the following examples present a
situation in which a class must be expanded to contain additional
IMPORTANT: Closures can create strong reference cycles if we try information that was not initially contemplated.
to access properties or methods defined outside the closure. If we
need to reference properties or methods with self inside a closure, Listing 3-148: Defining a basic class
we can declare the reference to self as weak with the syntax [weak self] 
or [unowned self]. The expression must be declared before the closure's class Employee {
var name = "Undefined"
parameters (see Chapter 5, Listing 5-30). For more information on var age = 0
ARC and how to avoid strong reference cycles, visit our website and
func createbadge() -> String {
follow the links for this chapter. return "Employee \(name) \(age)"
}
}

The Employee class declared in Listing 3-148 is a normal class, like those
we have defined before. It has two properties and a method called
createbadge() that returns a string with the values of the properties. This class
would be enough to create objects that generate the string of text
necessary to print a badge for every employee showing its name and age. class Employee {
var name = "Undefined"
But for the sake of argument, let’s say that some of the employees require var age = 0
a badge that also displays the department they work in. One option is to
func createbadge() -> String {
define another class with the same properties and methods and add what return "Employee \(name) \(age)"
we need, but this produces redundant code, and it is difficult to do when }
}
the class was taken from a library (they are usually too complex to modify class OfficeEmployee: Employee {
or duplicate). The solution is to create a new class that inherits the var department = "Undefined"
}
characteristics of the basic class and adds its own properties and methods let employee = OfficeEmployee()
to satisfy the new requirements. To indicate that a class inherits from employee.name = "George"
employee.age = 25
another class, we must write the name of the basic class after the name of employee.department = "Mail"
the new class separated by a colon.
var badge = employee.createbadge()
print("Badge: \(badge)") // "Badge: Employee George 25"
Listing 3-149: Inheriting properties and methods from another class 

class Employee { A class like Employee is called superclass, and a class that inherits from
var name = "Undefined" another class like OfficeEmployee is called subclass. In these terms, we can say
var age = 0
that the OfficeEmployee class is a subclass that inherits the properties and
func createbadge() -> String { methods of its superclass Employee. A class can inherit from a superclass
return "Employee \(name) \(age)"
}
that already inherited from another superclass in an infinite chain. When a
} property is accessed, or a method is called, the system looks for it on the
class OfficeEmployee: Employee {
object’s class and, if it is not there, it keeps looking in the superclasses up
var department = "Undefined"
} the hierarchical chain until it finds it.

IMPORTANT: Inheritance does not work the other way around. For
The OfficeEmployee class added to our code in Listing 3-149 only has one example, considering the code in Listing 3-150, objects created from
property called department, but it inherits the name and age properties, and the class OfficeEmployee have access to the department property of this
also the createbadge() method from the Employee class. All these properties class and the properties and methods of the Employee class, but
and methods are available in any of the objects created from the objects created from the Employee class do not have access to the
OfficeEmployee class, as shown next. department property.
Listing 3-150: Creating objects from a subclass Because of this hierarchical chain, sometimes a method does not have
 access to all the properties available for the object. For example, the
method called on the employee object created in Listing 3-150
createbadge() OfficeEmployee(the old method from the superclass is ignored), and the
have access to the properties declared on the Employee class but not those badge generated includes the values of the three properties.
declared in the OfficeEmployee class. If we want the method to also print the Using inheritance, we have created a new class without modifying
value of the department property, we must implement it again in the previous classes or duplicating any code. The Employee class can create
OfficeEmployee class with the appropriate modifications. This is called objects to store the name and age of an employee and generate a badge
overriding. To override a method of a superclass, we prefix it with the with this information, and the OfficeEmployee class can create objects to
override keyword. store the name, age, and the department of the employee and generate a
more complete badge with the values of all these properties.
Listing 3-151: Overriding an inherited method When we call the createbadge() method on the employee object created
 from the OfficeEmployee class in Listing 3-151, the method executed is the
class Employee { one defined in the OfficeEmployee class. If we want to execute the method on
var name = "Undefined"
var age = 0 the superclass instead, we must use a special keyword called super. The
super keyword is like the self keyword, but instead of referencing the object,
func createbadge() -> String {
return "Employee \(name) \(age)" super represents the superclass. It is often used when we have overridden a
} method, but we still need to execute the method on the superclass.
}
class OfficeEmployee: Employee {
var department = "Undefined" Listing 3-152: Calling a method on the superclass

override func createbadge() -> String {
return "Employee \(department) \(name) \(age)" class Employee {
} var name = "Undefined"
} var age = 0
let employee = OfficeEmployee()
employee.name = "George" func createbadge() -> String {
employee.age = 25 return "Employee \(name) \(age)"
employee.department = "Mail" }
}
var badge = employee.createbadge() class OfficeEmployee: Employee {
print("Badge: \(badge)") // "Badge: Employee Mail George 25" var department = "Undefined"

override func createbadge() -> String {
let oldbadge = super.createbadge()
The new OfficeEmployee subclass of Listing 3-151 overrides the return "\(oldbadge) \(department)"
method of its superclass to generate a string that includes the
createbadge() }
}
value of the department property. Now, when the method is executed from let employee = OfficeEmployee()
an object of this class, the method called is the one declared in employee.name = "George"
employee.age = 25
employee.department = "Mail"
Type Casting
var badge = employee.createbadge()
print("Badge: \(badge)") // "Badge: Employee George 25 Mail" 

Inheritance not only transfers functionality from one class to another but
This is the same as the previous example, but now, when the
also connects the classes together. The superclasses and their subclasses
createbadge()method of an object created from the OfficeEmployee class is
are linked together in a hierarchical chain. Because of this, whenever we
called, the method calls the createbadge() method of the superclass first and declare a variable of the type of the superclass, objects of the subclasses
assigns the result to the oldbadge constant. The value of this constant is later can be assigned to that variable too. This is a very important feature that
added to the value of the department property to generate the final string. allows us to do things like creating arrays of objects that are of different
classes but belong to the same hierarchy.
This example defines a superclass called Employee and then two subclasses
of Employee called OfficeEmployee and WarehouseEmployee. The purpose is to have
the information for every employee in one class and then have classes for
specific types of employee. Following this organization, we can create
objects that only contain the name, age, and deskNumber properties to
represent employees working at the office and objects that only contain
the name, age, and area properties to represent employees working at the
warehouse.
No matter the differences between one object and another, they all }
print("We have \(countOffice) employees working at the office") //2
represent employees of the same company, so sooner or later we will have print("We have \(countWarehouse) employees working at the warehouse") //1
to include them on the same list. The class hierarchy allows us to do that. 
We can declare a collection of the data type of the superclass and then
In Listing 3-154, we create the list array again with objects from the
store objects of the subclasses in it, as we did in Listing 3-153 with the list
same classes defined in the previous example, but this time we add a for in
array.
loop to iterate over the array and count how many objects of each class we
This is all good until we try to read the array. The elements of the array
have found. The if statement inside the loop uses the is operator to check if
are all considered to be of type Employee, so we can only access the
the current object stored in the obj constant is of type OfficeEmployee or
properties defined in the Employee class. Also, there is no way to know what
WarehouseEmployee and increments the counter respectively (countOffice or
type of object each element is. We could have an OfficeEmployee object at
countWarehouse).
index 0 and later replace it by a WarehouseEmployee object. The indexes do
Counting objects is not really what these operators are all about. The
not provide any information to identify the objects. Swift solve these
idea is to figure out the type with the is operator and then convert the
problems with the is and as operators.
object with the as operator to be able to access their properties and
methods. The as operator converts a value of one type to another. The
is—This operator returns a Boolean indicating whether the value is of
conversions are not always guaranteed, and that is why this operator
a certain data type.
comes in two more forms: as! and as?. These versions of the as operator
as—This operator converts a value of one class to another class when work like optionals. The as! operator forces the conversion and returns an
possible. error if the conversion is not possible, and the as? operator tries to convert
the object and returns an optional with the new object or nil in case of
Identifying an object is easy with the is operator. This operator returns failure.
a Boolean value that we can use in an if statement to check the object’s
class. Listing 3-155: Casting an object

Listing 3-154: Identifying the object’s data type for obj in list {
 if obj is OfficeEmployee {
let temp = obj as! OfficeEmployee
var countOffice = 0 temp.deskNumber = 100
var countWarehouse = 0 } else if obj is WarehouseEmployee {
let temp = obj as! WarehouseEmployee
for obj in list { temp.area = "New Area"
if obj is OfficeEmployee { }
countOffice += 1 }
} else if obj is WarehouseEmployee { 
countWarehouse += 1
}
When we use the as! operator we are forcing the conversion, so we Listing 3-157: Casting an object on the fly
need to make sure that the conversion is possible or otherwise the app will 
crash (this is the same that happens when we unwrap optionals with the let myarea = (list[1] as! WarehouseEmployee).area
print("The area of employee 1 is \(myarea)") // "Undefined"
exclamation mark). In the code of Listing 3-155, we only use this operator 
after we have already checked with the is operator that the object is of the
right class. Once the object is casted (converted) into its original data type, In this example, we do not assign the object to any variable; we just cast
we can access its properties and methods. In this example, the objects the element of the list array at index 1 as a WarehouseEmployee object inside
returned by the as! operator are stored in the temp constant and then new the parentheses and then access its area property. The value of this
values are assigned to the deskNumber and area properties. property is stored in the myarea constant and then printed on the console.
Checking for the type before casting is redundant. To simplify the Remember that conversions performed with the as! operator are only
code, we can use the as? operator. Instead of forcing the conversion and possible when we are sure it is going to be successful.
crashing the app, this version of the as operator tries to perform the
conversion and returns an optional with the result of the operation. IMPORTANT: The as! operator is applied when the conversion is
guaranteed to be successful, and the as? operator is used when we
Listing 3-156: Casting an object with the as? operator are not sure about the result. But we can also use the basic as
 operator when the Swift compiler can verify that the conversion will
for obj in list { be successful, as when we are casting some primitive data types
if let temp = obj as? OfficeEmployee { (e.g., String values into NSString objects).
temp.deskNumber = 100
} else if let temp = obj as? WarehouseEmployee {
temp.area = "New Area" The as operator works on objects that belong to the same class
}
}
hierarchy. Because sometimes the objects that require casting are not in
 the same hierarchy, Swift defines several generic data types to represent
values of any kind. The most frequently used are Any, AnyObject, and
In this example, we use optional binding to cast the object and assign AnyClass. Taking advantage of these generic types, we can create collections
the result to the temp constant. First, we try to cast obj as an OfficeEmployee with values that are not associated with each other.
object. If we are successful, we assign the value 100 to the deskNumber
property, but if the value returned is nil, then we try to cast the object to Listing 3-158: Working with objects of AnyObject type
the WarehouseEmployee class to modify its area property. 
Casting can also be performed on the fly if we are sure that the class Employee {
conversion is possible. The statement to cast the object is the same but it var name = "Undefined"
}
must be declared between parentheses.
class Department {
var area = "Undefined"
}
var list: [AnyObject] = [Employee(), Department(), Department()] Initialization
for obj in list { 
if let temp = obj as? Employee {
temp.name = ""
} else if let temp = obj as? Department { We have been initializing the properties during definition in every class
temp.area = ""
} declared so far. This is because classes do not provide memberwise
}
initializers as structures do. The properties of a class have to be initialized

explicitly in the definition or during instantiation by the init() method. This is
The list array declared in Listing 3-158 is of type AnyObject and therefore the same method previously introduced for structures and we can use it in
it can contain objects of any data type. To populate the array, we created our classes as well.
two simple and independent classes: Employee and Department. A few objects
are created from these classes and included in the array. The objects are Listing 3-159: Declaring a Designated Initializer
later casted by the as? operator inside a for in loop and their corresponding 
properties are modified following the same procedure used in previous class Employee {
var name: String
examples. var age: Int
The init() method declared for the Employee class in Listing 3-159 initializes
every property of the class with the values specified in the Employee()
initializer. This type of initializer is called Designated Initializer. When we
declare a Designated Initializer, we need to make sure that all the
properties are initialized.
If we know that in some circumstances our code will not be able to provide
all the values during initialization, we can also declare a Convenience
Initializer. A Convenience Initializer is an initializer that offers a convenient
way to initialize an object with default values for some or all its properties.
It is declared as an init() method but preceded with the convenience keyword.
A Convenience Initializer must call the Designated initializer of the same var name: String
var age: Int
class with the corresponding values.
init(name: String, age: Int) {
self.name = name
Listing 3-160: Declaring a Convenience Initializer self.age = age
 }
}
class Employee {
class OfficeEmployee: Employee {
var name: String
var department: String = "Undefined"
var age: Int }
let employee1 = OfficeEmployee(name: "George", age: 29)
init(name: String, age: Int) {

self.name = name
self.age = age
} The code in Listing 3-161 defines the subclass OfficeEmployee that inherits
convenience init() {
self.init(name: "Undefined", age: 0) from the Employee class. The OfficeEmployee class does not provide any
} initializer, so the only initializer available is the one provided by its
}
let employee1 = Employee() superclass. This initializer only initializes the properties name and age. The
 department property of OfficeEmployee is explicitly initialized with the value
"Undefined". To provide an initializer that also includes this property, we
When we create an instance of Employee, the system detects the number
must declare a new Designated Initializer in the OfficeEmployee class.
and type of arguments provided and executes the corresponding initializer.
For example, if we provide the values for the name and the age parameters, Listing 3-162: Declaring a Designated Initializer for the subclass
the system executes the Designated Initializer because this is the initializer 
that contains the necessary parameters to receive those values, but if the class Employee {
initialization does not include any argument, the Convenience Initializer is var name: String
var age: Int
executed instead and then the Designated Initializer is called with values by
default ("Undefined" and 0). init(name: String, age: Int) {
self.name = name
Unlike structures, classes can inherit properties and methods from other self.age = age
}
classes, and this includes the init() method. When a subclass does not
}
provide its own Designated Initializer, the initializer of its superclass is used class OfficeEmployee: Employee {
var department: String
instead.
init(name: String, age: Int, department: String) {
Listing 3-161: Inheriting the Designated Initializer self.department = department
deinit {
print("This instance was erased")
}
}
var purchase: Item? = Item()
purchase = nil

Access Control and Modifiers The Employee class in Listing 3-164 defines two properties, name and age, but
this time a closure is assigned to the name property to get the employee's

name from a server (we will see how to retrieve information from the Web
Swift defines keywords (also called modifiers) that can be applied to in Chapter 19). Because we declared this property as lazy, the closure will
entities (classes, structures, properties, methods, etc.) to confer them only be executed when we try to read the property’s value. If we execute
special attributes. We have already seen the mutating and override keyword, the example as it is, we get nothing in return, but if we read the name
but there are more available, like the following. property with a statement at the end, we will see the text "Loading..."
printed on the console.
lazy—This keyword defines a property whose initial value is not The Swift language also includes keywords in charge of defining the level of
assigned until the property is used for the first time. access for each entity in our code. Access control in Swift is based on
modules and source files, but it also applies to single properties and
final—This keyword is used on a class when we don't want to allow
methods. Source files are the files we create for our application, the
the code to create subclasses of it. It must be declared before the class
properties and methods are the ones we have created for our structures
keyword. and classes in previous examples, and modules are units of code that are
related. For instance, a single application and each of the frameworks
The lazy keyword is frequently used when the property’s value may take included in it are considered modules (we will introduce frameworks in
time to be determined and we do not want the initialization of the Chapter 4). Considering this classification, Swift defines five keywords to
structure or the class to be delayed. For example, we may have a property determine accessibility.
that stores a name retrieved from a server, which is a resource intensive
task that we should perform only when the value is required. open—This keyword determines that an entity is accessible from the
module it belongs to and other modules.
Listing 3-164: Defining lazy properties

public—This keyword determines that an entity is accessible from
class Employee { the module it belongs to and other modules. The difference between
lazy var name: String = { public and open is that we can't create subclasses of public classes
// Loading name from a server
print("Loading...") outside the module in which they were defined. This also applies to
methods and properties (e.g., public methods can't be overridden
return "Undefined"
}() outside the module in which they were declared).
var age = 0
} internal—This keyword determines that an entity is accessible only
let employee = Employee() inside the module in which it was created. This is the default access

mode for applications. By default, every entity defined in our
application is only accessible from inside the application.
private—This keyword determines that an entity is accessible only
from the context in which it was created (e.g., a private property in a The code in Listing 3-165 defines the name and age properties of our Employee
class will only be accessible from methods of the same class). class as private and adds a method called showValues() to access their values.
Due to access control, these properties are only accessible by the method
fileprivate—This keyword determines that an entity is accessible
in the class. If we try to read their values from outside the object using dot
only from the file in which it was declared (e.g., a fileprivate property in
notation, Xcode will return an error (employee.name).
a class will only be accessible by other entities defined inside the file If what we want is to be able to read the property from outside the object
in which it was declared). but not allow assigning new values to it, we can declare it with a public
getter but a private setter.
As we will see later, most of these keywords apply to frameworks and are
rarely used in single applications. By default, the properties and methods Listing 3-166: Declaring a public getter and private setter
we include in our classes and structures are declared internal, which means 
that our classes and structures are only available from inside our class Employee {
private var name = "Undefined"
application (module). Unless we are creating our own frameworks, this is public private(set) var age = 0
all we need for our applications, and is the reason why we didn't have to
func setAge(newAge: Int) {
specify any keyword when we defined our structures and classes before. age = newAge
All our classes and structures are accessible by the rest of the code inside }
}
our application, but if we want to have a level of control in our data types let employee = Employee()
employee.setAge(newAge: 25)
or avoid modifying values by mistake, we can declare some of them as
print(employee.age)
private, as in the following example. 
Listing 3-165: Declaring private properties The age property in the Employee class of Listing 3-166 was declared as
 public, so everyone can read its value, but with a private setter (private(set)),
class Employee { so only the methods inside the class can modify it. To change the value, we
private var name = "Undefined" defined the setAge() method. The code creates an instance of the class and
private var age = 0
calls the method, but this time we can read the value of the age property
func showValues() { and print it on the console because it was declared with a public getter.
print("Name: \(name)")
print("Age: \(age)")
}
}
let employee = Employee()
employee.showValues()

3.6 Protocols common are defined in the protocol and then implemented by the
structures’ definitions. This lets us associate different structures together

through a common pattern. The code implemented by each structure is
unique, but they follow a blueprint set by the protocol. If we know that a
The main characteristics of classes, and therefore objects, are the capacity
structure uses a protocol, we can always be sure that besides its own
to encapsulate data and functionality and the possibility to share and
definitions, it will also include the properties and methods defined by the
improve code through inheritance. This introduced an advantage over
protocol. In addition, protocols can be extended to provide their own
previous paradigms and turned Object-Oriented Programming into the
implementations of the properties and methods we want the structures to
industry standard for a while. But that changed with the introduction of
have in common, allowing the paradigm to completely replace classes and
protocols in Swift. Protocols define properties and methods that structures
objects.
can have in common. This means that Swift’s structures not only can
encapsulate data and functionality, just like objects, but by conforming to
IMPORTANT: The Swift paradigm is built from the combination of
protocols they can also share code. Figure 3-5 illustrates the differences
structures and protocols, but protocols may also be adopted by
between these two paradigms.
enumerations and classes. For instance, Objective-C and the
frameworks programmed in this language use protocols to offer a
Figure 3-5: Object-Oriented Programming versus Protocol-Oriented
programming pattern called Delegation. We will study how classes
Programming conform to protocols and how to implement delegation later.
In OOP, the code is implemented inside a class and then objects are
created from that class. If we need to create objects with additional
functionality, we must define a subclass that inherits the code from the
superclass and adds some of its own. Protocols offer a slightly different
approach. The properties and methods we want the structures to have in
Definition of Protocols this protocol, and along with the protocol’s property and method it also
implements its own property called age. Although this property was not

defined in the protocol, we can read it inside the printdescription() method
Protocols are defined with the protocol keyword followed by the name and and print its value.
the list of properties and methods between braces. No values or The advantage of this practice is evident when structures of different types
statements are assigned or declared inside a protocol, only the names and conform to the same protocol, as shown in the following example.
the corresponding types. Because of this, methods are defined as always,
Listing 3-168: Defining multiple structures that conform to the same
but they omit the braces and the statements, and properties must include
protocol
the get and set keywords between braces to indicate whether they are read-

only properties, or we can read and assign values to them (see Listing 3-43
protocol Printer {
for an example of getters and setters). To indicate that the structure var name: String { get set }
conforms to the protocol, we must include the protocol’s name after the func printdescription()
}
structure’s name separated by a colon, as shown in the following example. struct Employees: Printer {
var name: String
var age: Int
Listing 3-167: Defining protocols
 func printdescription() {
print("Description: \(name) \(age)")
protocol Printer {
}
var name: String { get set }
}
func printdescription()
struct Offices: Printer {
}
var name: String
struct Employees: Printer {
var employees: Int
var name: String
var age: Int
func printdescription() {
print("Description: \(name) \(employees)") // "Description: Mail 2"
func printdescription() {
}
print("Description: \(name) \(age)") // "Description: John 32"
}
}
let employee1 = Employees(name: "John", age: 32)
}
let office1 = Offices(name: "Mail", employees: 2)
let employee1 = Employees(name: "John", age: 32)
office1.printdescription()
employee1.printdescription()


A protocol tells the structure what properties and methods are required, Although the structures created in Listing 3-168 from the Employees and
Offices definitions are different (they have different properties), they both
but the structure must implement them. In the example of Listing 3-167,
we define a protocol called Printer that includes the name property and the conform to the Printer protocol and provide their own implementation of
printdescription() method. The Employees structure defined next conforms to
the printdescription() method. The common functionality defined by the
protocol ensures that no matter what type of structure we are working var list: [Printer] = [employee1, office1]
for element in list {
with, it will always have an implementation of printdescription(). if let employee = element as? Employees {
Protocols are also data types. We can treat a structure as if it is of the data print(employee.age) // "32"
}
type of the protocol it conforms to. This allows us to associate structures element.printdescription()
by their common functionality. }

Protocols can also define generic properties and methods, but they work The Swift language makes use of protocols extensively. Almost every API
slightly different than the generic types studied before. When we want to includes protocols that define common features and behavior for their
define a protocol with a generic property or method, we first must define data types, including enumerations, structures, and classes. But there are
the name of the generic type with the associatedtype keyword. also important protocols defined in the Swift Standard Library that we can
use to improve our custom data types. The following are some of the
protocols available.
Listing 3-172: Defining generic protocols

Equatable—This protocol defines a data type which values can be
protocol Printer {
associatedtype protype compared with other values of the same type using the operators ==
var name: protype { get set } and !=.
}
struct Employees: Printer { Comparable—This protocol defines a data type which values can be
var name: String
} compared with other values of the same type using the operators >, <,
let employee = Employees(name: "John") >=, and <=.
print(employee.name) // "John"
 Numeric—This protocol defines a data type that only works with
values that can participate in arithmetic operations.
The code in Listing 3-172 defines a generic protocol called Printer and a
Hashable—This protocol defines a data type that provides the hash
structure that conforms to that protocol called Employees. The protocol
value (unique identifier) required for the value to be included in
defines a generic type with the name protype and then a property of that
collections, such as sets and dictionaries.
type. The property's real data type is defined by the structure or the class
that conforms to the protocol. In this case, the Employees structure defines CaseIterable—This protocol defines a data type, usually an
the name property to be of type String, and that's the type of values we can enumeration without associated values, that includes a property
use in the instances of this structure, but we could have declared the called allCases that contains the collection of all the cases included in
property as an integer or any other data type we needed. the data type.
values' data types to compare them and solve the condition (true or false, the value 32 and therefore the value "Equal" is assigned to the message
depending on whether the values are equal or not). Swift primitive data constant when we compare the objects with the ternary operator.
types conform to the Equatable protocol and implement its methods, but we If what we want is to compare each of the properties in the structure,
can also implement them in our own data types to compare their values. then we can omit the method. When we conform to the Equatable protocol,
For this purpose, we must declare that the data type conforms to the the compiler automatically generates the method for us to compare all the
protocol and implement the methods required by it. The Equatable protocol values of the structure (in this case, name and age).
requires only one method called == to check for equality (the system infers
that if two values are not equal, they are different, and therefore the Listing 3-174: Letting the compiler create the protocol methods for us
method for the != operator is optional). This method must have a name 
equal to the operator (==), receive the two values to compare, and return a struct Employees: Equatable {
var name: String
Boolean value to communicate the result of the comparison. For instance, var age: Int
we can make our Employees structure conform to the Equatable protocol and }
let employee1 = Employees(name: "John", age: 32)
implement a method with the name == to be able to compare two different let employee2 = Employees(name: "George", age: 32)
instances of the same structure.
let message = employee1 == employee2 ? "Equal" : "Different"
print(message) // "Different"
Listing 3-173: Conforming to the Equatable protocol 

struct Employees: Equatable { Because we did not declare the == method in the example of Listing 3-
var name: String 174, the system creates the method for us and compares the values of
var age: Int
both properties. As a result, the objects are considered different (the ages
static func == (value1: Employees, value2: Employees) -> Bool { are the same, but the names are different).
return value1.age == value2.age
} Of course, we could have compared the properties directly
} (employee1.name == employee2.name) but being able to compare the objects
let employee1 = Employees(name: "John", age: 32)
let employee2 = Employees(name: "George", age: 32) instead simplifies the code and allows us to use our structures (or objects)
in APIs that require the values to be comparable. For example, when we
let message = employee1 == employee2 ? "Equal" : "Different"
print(message) // "Equal" created a generic function earlier in this chapter, we could not perform any
 operations on the values (see Listing 3-15). Because the data type we used
in those functions is generic, Swift is incapable of knowing the capabilities
In this example, we use the == method to compare the values of the of the data type and therefore Xcode returns an error if we try to perform
properties and therefore the structures are going to be equal when the
age
operations on the values, but we can easily change the situation by making
employees are the same age. In this case, both instances are created with the generic type conform to a protocol. This feature is called type
constraint because it constrains the generic type to a data type with
certain capabilities. For instance, the function in the following example }
calculateResult(value1: 3.5, value2: 4)
receives two generic values, but only of a data type that conforms to the 
Equatable protocol, and therefore we can compare the values inside the
function. The calculateResult() function in Listing 3-176 is a generic function and
therefore it can receive any value of any type, but because we set a type
Listing 3-175: Adding a type constraint to a generic function constraint with the Numeric protocol, the function can only receive values of
 data types that can participate in arithmetic operations.
struct Employees: Equatable { Besides comparing for equality with the Equatable protocol, we can also
var name: String
var age: Int compare magnitudes with the Comparable protocol. This protocol is like
} Equatable, but the system does not offer a default implementation of the
func compareValues<T: Equatable>(value1: T, value2: T) -> String {
let message = value1 == value2 ? "equal" : "different" type methods, we must implement them ourselves. The protocol requires
return message four methods to represent the operations >, <, >= and <=. In the following
}
let employee1 = Employees(name: "George", age: 55)
example, we compare the ages of the employees.
let employee2 = Employees(name: "Robert", age: 55)
Listing 3-177: Conforming to the Comparable protocol
let result = compareValues(value1: employee1, value2: employee2)
print("The values are \(result)") // "The values are different" 
 struct Employees: Comparable {
var name: String
var age: Int
The conformance to the protocol is specified inside the angle brackets
after the name of the generic type. The compareValues() function in Listing 3- static func > (value1: Employees, value2: Employees) -> Bool {
175 declares the T type to conform to Equatable and then compares the return value1.age > value2.age
}
values with a ternary operator and returns the result. In this case, the ages static func < (value1: Employees, value2: Employees) -> Bool {
of the employees are the same (55), but the names are different ("George" return value1.age < value2.age
}
and "Robert"), and therefore the system considers the structures to be static func >= (value1: Employees, value2: Employees) -> Bool {
different. return value1.age >= value2.age
}
Another protocol used as a type constraint is Numeric. This protocol static func <= (value1: Employees, value2: Employees) -> Bool {
determines that the data types of the values received by the function must return value1.age <= value2.age
}
support arithmetic operations. }
let employee1 = Employees(name: "George", age: 32)
let employee2 = Employees(name: "Robert", age: 55)
Listing 3-176: Using the Numeric protocol to set a type constraint
 if employee1 > employee2 {
func calculateResult<T: Numeric>(value1: T, value2: T) { print("\(employee1.name) is older")
} else {
print(value1 + value2) // 7.5
office.printdescription() // "The name is Mail" Of course, we can also extend our own data types if we consider that

appropriate. The following example extends our Employees structure to add
In this example, we define a Printer protocol with just the name property and a new method.
then extend it to include a common implementation of the printdescription()
Listing 3-183: Extending custom data types
method. Now, the Employees and Offices structures in our example share the

same implementation and produce the same result when their
struct Employees {
printdescription() methods are executed. var name: String
var age: Int
As we already mentioned, extensions are not only available for protocols
}
but also for any other data type. We can use them to extend structures, extension Employees {
func printbadge() {
enumerations, or classes. This is particularly useful when we do not have print("Name: \(name) Age: \(age)")
access to the definitions of the data types and need to add some }
}
functionality (like when they are part of a library or framework). In the let employee = Employees(name: "John", age: 50)
following example, we extend the Int structure to provide a method that employee.printbadge() // "Name: John Age: 50"

prints a description of its value.
Extensions can also be conditional. For instance, if we have a generic
Listing 3-182: Extending data types
structure, we can add an extension only for specific types of values. The

condition is determined by the where clause. The clause works like an if
extension Int {
func printdescription() { statement, so the extension is only applied if the condition is met.
print("The number is \(self)")
}
}
Listing 3-184: Defining a conditional extension
let number = 25 
number.printdescription() // "The number is 25"
struct Employees<T> {

var value: T
}
The Int data type is a structure defined in the Swift Standard Library. extension Employees where T == Int {
func doubleValue() {
We cannot modify its definition, but we can extend it to add more print("\(value) times 2 = \(value * 2)")
functionality. In this example, we add a method called printdescription() to }
}
print a message with the current value (notice the use of the self keyword let employee = Employees(value: 25)
to refer to the instance). This method is not included in the original employee.doubleValue() // "25 times 2 = 50"

definition, but it is now available in our code.
In this example, we define a generic structure called Employees with a Another useful implementation of extensions is the customization of
generic property called value and then define an extension for this structure string interpolation. We have introduced string interpolation in Chapter 2
with a method called doubleValue(), but this method will only be added to the and have been using it in almost every example to insert values into strings
instance if the data type used to create the instance is Int. At the end, we (e.g., print("My name is \(name)")). What we haven't mentioned is that these
create an instance with the value 25 and call the method, which multiplies values are managed by a structure called String.StringInterpolation (a typealias
the value by 2 and prints a string with the result. This works because we of DefaultStringInterpolation) and that by extending this structure we can
created the instance with an integer, but if we try to use another type of customize how the system processes the values. The StringInterpolation
value, Xcode will show an error. structure includes the following methods for this purpose.
into Fahrenheit and then add the result to the interpolation with the Delegates
appendLiteral() method to get the string "Temperature in Fahrenheit 77.0".

func generatereport() {
delegate.showMoney(name: name, money: money)
}
}
let salary = Salary()
var employee1 = Employees(name: "John", money: 45000, delegate: salary)
func generatereport() { Errors are common in computer programming. Either our code or the code
delegate.showMoney(name: name, money: money)
} provided by libraries and frameworks may return errors. No matter how
} many precautions we take, we can't never guarantee success and many
let salary = Salary()
var employee1 = Employees(name: "John", money: 45000, delegate: salary)
problems may be found as our code tries to serve its purpose. For this
reason, Swift introduces a systematic process to handle errors called Error
employee1.delegate = BasicSalary() Handling.
employee1.generatereport() // "Salary is over the minimum"

Handling Errors check for errors. If the method returns the OutOfStock error, the statements
inside the catch block are executed. This pattern allows us to respond every

time there is an error and report it to the user or correct the situation
without having to crash the app or produce unexpected results.
Now that we have a method that can throw errors, we must handle the
errors when the method is executed. Swift includes the try keyword and
Do It Yourself: Create a new Playground file. Copy the code in
the do catch statements for this purpose. The do catch statements create two
Listing 3-191 inside the file. You should see the message "We do not
blocks of code. If the statements inside the do block return an error, the
statements in the catch block are executed. To execute a method that have enough lamps" printed on the console. Replace the number 8
throws errors, we must call the method inside the do statement with the try with the number 3. Now the message should not be printed because
keyword in front of it. there are enough lamps in stock.
Listing 3-191: Handling errors IMPORTANT: You can add as many errors as you need to the Errors
 enumeration. The errors can be checked later with multiple catch
enum Errors: Error { statements in sequence. Also, you may add all the statements you
case OutOfStock need to the do block. The statements before try are always executed,
}
struct Stock { while the statements after try are only executed if no error is found.
var totalLamps = 5
mutating func sold(amount: Int) throws {
if amount > totalLamps {
If the error is not one of the types we are expecting, we can print
throw Errors.OutOfStock information about it. The information is stored in a constant called error
} else { that we can read inside the catch block.
totalLamps = totalLamps - amount
}
} Listing 3-192: Getting information about the error
}
var mystock = Stock() 
enum Errors: String, Error {
do { case OutOfStock = "Hello"
try mystock.sold(amount: 8) }
} catch Errors.OutOfStock { struct Stock {
print("We do not have enough lamps") var totalLamps = 5
} mutating func sold(amount: Int) throws {
 if amount > totalLamps {
throw Errors.OutOfStock
} else {
The code in Listing 3-191 expands the previous example to handle the error totalLamps = totalLamps - amount
thrown by the sold() method. Because of the addition of the try keyword, }
}
the system tries to execute the sold() method in the mystock structure and }
var mystock = Stock() code checks if there are enough lamps before calling the sold() method, so
do { we know that the instruction will never throw the OutOfStock error.
try mystock.sold(amount: 8)
} catch {
print(error) // OutOfStock
Listing 3-194: Ignoring the errors
} 
 enum Errors: Error {
case OutOfStock
On the other hand, if we do not care about the error, we can force the try }
struct Stock {
keyword to return an optional with the syntax try?. If the method throws an var totalLamps = 5
error, the instruction returns nil, and therefore we can avoid the use of the mutating func sold(amount: Int) throws {
if amount > totalLamps {
do catch statements.
throw Errors.OutOfStock
} else {
Listing 3-193: Catching errors with try? totalLamps = totalLamps - amount
}
 }
enum Errors: Error { }
case OutOfStock var mystock = Stock()
}
struct Stock { if mystock.totalLamps > 3 {
var totalLamps = 5 try! mystock.sold(amount: 3)
mutating func sold(amount: Int) throws { }
if amount > totalLamps { print("Lamps in stock: \(mystock.totalLamps)")
throw Errors.OutOfStock 
} else {
totalLamps = totalLamps - amount
}
}
}
var mystock = Stock()
try? mystock.sold(amount: 8) // nil

The instruction at the end of Listing 3-193 returns the value nil if the
method throws an error, or an optional with the value returned by the
method if everything goes right.
Sometimes, we know beforehand that a throwing method is not going to
throw an error and therefore we want to avoid writing unnecessary code.
In cases like this, we can use the syntax try!. For instance, the following
Results }
}
 
The sold() method in Listing 3-195 now returns a Result value of type <Int,
Sometimes we need to return more than just an error. For this purpose,
Errors>, so if an error occurs, the method can return a failure() value with the
the Swift Standard Library defines the Result enumeration. The enumeration
associated value OutOfStock, but if we have enough lamps to fulfil the order,
defines two cases with associated values to use in case of success or
we can return a success() value with the remaining number of lamps. The
failure, called success() and failure(). The Result enumeration is generic, which
result can be read by a switch statement. We check whether the value
means that the data types of the associated values can be anything we
returned by the method is failure() or success(), get the associated value with
want. For instance, in the following examples we define a Result
a let statement, and proceed accordingly. In this case, there are enough
enumeration of type <Int, Errors> to return an integer and the OutOfStock
lamps available, so a message is printed on the console with the remaining
error defined in the previous example.
stock.
Instead of using a switch statement, we can use the following method
Listing 3-195: Returning an error with a Result enumeration
defined by the Result enumeration.

enum Errors: Error {
case OutOfStock get()—This method returns the associated value of the success() case
} or throws an error with the associated value of the failure() case.
struct Stock {
var totalLamps = 5
The only purpose of the get() method is to simplify the code. Now, instead
mutating func sold(amount: Int) -> Result<Int, Errors> {
if amount > totalLamps { of a switch statement, we can use a do catch.
return .failure(.OutOfStock)
} else {
totalLamps = totalLamps - amount
Listing 3-196: Processing an error with the get() method
return .success(totalLamps) 
} enum Errors: Error {
} case OutOfStock
} }
var mystock = Stock() struct Stock {
var totalLamps = 5
let result = mystock.sold(amount: 3)
switch result { mutating func sold(amount: Int) -> Result<Int, Errors> {
case .success(let stock): if amount > totalLamps {
print("Lamps in stock: \(stock)") return .failure(.OutOfStock)
case .failure(let error): } else {
if error == .OutOfStock { totalLamps = totalLamps - amount
print("Error: Out of Stock") return .success(totalLamps)
} else { }
print("Error")
}
} CHAPTER 4 - INTRODUCTION TO
var mystock = Stock()
The result is the same, but now all we need to do is to call the get() method.
If the method doesn't return an error, the remaining stock is printed on the
console, otherwise, the catch block is performed, and an error is printed
instead.
The programming tools introduced in previous chapters are not enough to The Swift Standard Library we have been using in previous chapters is
build professional applications. Creating an app requires accessing complex automatically loaded for us and available everywhere in our code, but
technologies and performing repetitive tasks that involve hundreds or even when we require the use of other frameworks, we must indicate it to the
thousands of lines of code. Faced with this situation, developers always system. This is done by adding the import instruction at the beginning of
implemented pre-programmed codes that perform common tasks. These each file followed by the name of the framework we want to include
pieces of code are organized according to their purpose in what we know (e.g., import Foundation). Once the framework is imported, it is included in
as frameworks. our file, giving us access to all the structures, classes, functions, and any
Frameworks are libraries (pre-programmed code) and APIs (Application of the values defined in its code.
Programming Interfaces) that we can use to add functionality to our
applications. This includes managing databases, creating graphics on the
screen, storing files, accessing resources on the Web, sharing data online,
and more. These frameworks are essential for creating professional
applications for Apple devices and are therefore part of the SDK (Software
Development Kit) included with Xcode.
pow(Float, Float)—This function returns the result of raising the print("The maximum value is \(maximum)") // "The maximum value is 4.0"

first value to the power of the second value. The arguments may be
numbers of type Float or Double.
The first thing we do in the code of Listing 4-1 is to import the
sqrt(Float)—This function returns the square root of the value of its Foundation framework. After this, we can implement any of the tools
argument. The argument may be of type Float or Double. defined inside the framework, including the basic functions introduced
log(Float)—This function returns the natural logarithm of a value. above. This example gets the square root of 4.0, calculates 2.0 to the
Similar functions are log2(), log10(), log1p(), and logb(). It can take a value power of 2.0, and compares the results using the max() function from the
of type Float or Double. Swift Standard Library.
sin(Float)—This function returns the sine of a value. Similar
functions are asin(), sinh(), and asinh(). The argument may be of type Float
or Double.
cos(Float)—This function returns the cosine of a value. Similar
functions are acos(), cosh(), and acosh(). The argument may be of type
Float or Double.
precedes the value of the first argument, and the orderedDescending search. Except for the first argument, the rest of the argument s are
value is returned when the original string follows the value of the first optional.
argument. The options argument is a property of the CompareOptions
structure. The properties available are caseInsensitive (it considers As we already mentioned, the String structure is bridged to the NSString
lowercase and uppercase letters to be the same), literal (performs a class and therefore we can call these methods from String values, but
byte-to-byte comparison), diacriticInsensitive (ignores diacritic marks because they are defined in the NSString class, we still must import the
such as the visual stress on vowels), widthInsensitive (ignores the width Foundation framework to be able to use them. Some of them are like those
difference in characters that occurs in some languages), and offered by the String structure but allow us to perform additional
forcedOrdering (the comparison is forced to return orderedAscending or
operations on the values. For instance, we can incorporate values into
orderedDescending values when the strings are equivalent but not strictly
strings with string interpolation, but the localizedStringWithFormat() method
equal). The range argument defines a range that determines the offers a different approach. This method takes a string with placeholders
and replaces them with a list of values. The placeholders are declared with
portion of the original string we want to compare. Finally, the locale
the % symbol followed by a character that represents the type of value we
argument is a Locale structure that defines localization. Except for the
want to include. For example, if we want to replace the placeholder by an
first argument, the rest of the arguments are optional.
integer, we must use the characters %d.
caseInsensitiveCompare(String)—This method compares the
original string with the string provided by the argument. It works Listing 4-4: Creating a formatted string
exactly like the compare() method but with the option caseInsensitiveSearch 
set by default. import Foundation
The NSRange class also includes two properties to retrieve its values:
and length. The following example initializes an NSRange object from a
location
Swift range and prints its values.
Numbers 
 Besides its own data type, Foundation also provides the means to format
numbers. Every time we print a number, all the digits are shown on the
Foundation offers a class called NSNumber to represent and store numbers. screen, including all the decimal digits. In Listing 4-4, we explained how to
With the introduction of the Swift’s primitive data types, the use of this specify how many digits of a number we want to include in a string using
class is no longer necessary, but there are a few old frameworks that still placeholders (e.g., %.2f), but this is not customizable enough. To provide a
require these types of values. The class includes the following initializer. better alternative, the framework includes the following formatting
method.
NSNumber(value: Value)—This initializer creates an NSNumber
object with the value specified by the value argument. The argument formatted(FormatStyle)—This method formats the number
may be a value of any of the data types available in Swift for numbers. according to the styles provided by the argument.
The class also provides properties to perform the opposite operation, To format a number, we must call this method from the instance with the
getting Swift data types from NSNumber objects. The following are the most styles we want to apply to it. The styles are defined by a structure that
frequently used. conforms to the FormatStyle protocol. For numbers, the framework defines
the IntegerFormatStyle and the FloatingPointFormatStyle structures. These
intValue—This property returns an Int value with the object’s structures include the following methods to style a number.
number.
floatValue—This property returns a Float value with the object’s precision(Precision)—This method defines the number of digits
number. included in the integer and decimal parts of the number. The
argument is a Precision structure, which includes the integerLength(Int)
doubleValue—This property returns a Double value with the object’s
and fractionLength(Int) methods to determine the number of digits in the
number.
integer and decimal parts, and the integerAndFractionLength(integer: Int,
fraction: Int) method to determine both.
The following example shows how to create NSNumber objects and how to
get them back as Swift data types to perform operations. rounded(rule: FloatingPointRoundingRule)—This method rounds
the number to the nearest value. The rule argument is an
Listing 4-12: Working with NSNumber objects enumeration with the values up, down, awayFromZero,
 toNearestOrAwayFromZero, toNearestOrEven, and towardZero.
import Foundation grouping(Grouping)—This method determines if the digits of a
var mynumber = NSNumber(value: 35) number are going to be separated in groups (e.g., 9,000,000). The
var mydouble = mynumber.doubleValue * 2 // 70
argument is a structure with the properties automatic (default) and send to this method the value returned by the fractionLength() method,
never. which formats the number with 2 decimal digits. As a result, we get a string
with the value "32.57" (the value is rounded up).
notation(Notation)—This method determines the number's
Styles can be concatenated, one after another, with dot notation. For
notation. The argument is a structure with the properties automatic
instance, in the previous example, the number was rounded up by default,
(default), compactName, and scientific.
but we can change this behavior by applying the rounded(rule:) method, as
sign(strategy: SignDisplayStrategy)—This method determines if shown next.
the sign will be included (+ and -). The strategy argument is a
SignDisplayStrategy structure, which includes the automatic (default) and Listing 4-14: Rounding a number
never properties, and also the always(includingZero: Bool) method to 
styling structure from the number property (in this case, the number is a
Double so the value of the property is an instance of the
We can also show the sign in front of the number (+ or -). In the following
FloatingPointFormatStyle structure). Next, we call the precision() method, and
example, we always show the sign except when the number is equal to 0.
On the other hand, the currency(code:) method can produce a number with
any format and currency symbol we want. The currency is defined by the
string assigned to the argument. There are values for any currency
available. For instance, the USD string is for American Dollars, the CAD
string is for Canadian Dollars, EUR for Euros, and so on. The following
example gets the number expressed in Canadian dollars (for more values,
visit our website and follow the links for this chapter).
The following example shows different ways to initialize a date. distantFuture—This type property returns a Date structure with a
value that represents a date in a distant future.
Listing 4-19: Storing dates with Date structures
distantPast—This type property returns a Date structure with a value

that represents a date in a distant past.
import Foundation
var currentdate = Date() The Date structure also includes properties and methods to calculate and
var nextday = Date(timeIntervalSinceNow: 24 * 60 * 60)
var tendays = Date(timeInterval: -10 * 24 * 3600, since: nextday) compare dates. The following are the most frequently used.

timeIntervalSinceNow—This property returns a TimeInterval value var event = Date(timeIntervalSinceNow: Double(days) * 24 * 3600)
representing the difference in seconds between the date in the Date if today.compare(event) == .orderedAscending {
structure and the current date. let interval = event.timeIntervalSince(today)
print("We have to wait \(interval) seconds")
compare(Date)—This method compares the date in the Date }

structure with the date specified by the argument and returns an
enumeration of type ComparisonResult with a value corresponding to the The dates in Date structures are not associated to any calendar. This means
temporal order of the dates. The possible values are orderedSame (the that to get the components in a date (year, month, day, etc.) we must
dates are equal), orderedAscending (the date is earlier than the value), decide first in the context of which calendar the date is going to be
and orderedDescending (the date is later than the value). interpreted. The calendar for a date is defined by the Calendar structure.
timeIntervalSince(Date)—This method compares the date in the This structure provides properties and methods to process a date
Datestructure with the date specified by the argument and returns the according to a specific calendar (Gregorian, Buddhist, Chinese, etc.). To
initialize a Calendar structure, we have the following initializer and type
interval between both dates in seconds.
property.
addingTimeInterval(TimeInterval)—This method adds the
seconds specified by the argument to the date in the Date structure Calendar(identifier: Identifier)—This initializer creates a Calendar
and returns a new Date structure with the result. structure with the calendar specified by the argument. The identifier
addTimeInterval(TimeInterval)—This method adds the seconds argument is a property of a structure called Identifier defined inside the
specified by the argument to the date in the Date structure and stores Calendar structure. The properties available are gregorian, buddhist, chinese,
the result in the same structure. coptic, ethiopicAmeteMihret, ethiopicAmeteAlem, hebrew, ISO8601, indian, islamic,
islamicCivil, japanese, persian, republicOfChina, islamicTabular and
Comparing dates and calculating the intervals between dates is a constant islamicUmmAlQura.
requirement in app development. The following example compares the
current—This type property returns a structure with the current
current date with a date calculated from a specific number of days. If the
calendar set in the system.
resulting date is later than the current date, the code prints a message on
the console to show the time remaining in seconds.
A Calendar structure includes the following properties and methods to
manage the calendar and to get and set new dates.
Listing 4-20: Comparing two dates

identifier—This property returns the value that identifies the
import Foundation
calendar.
var days = 7
calendar returned by the system, but if we want to use the same calendar A common task when working with multiple dates is getting the time
between dates, such as the hours remaining for a process to complete or
no matter where the app is executed, we must set it ourselves from the
the days remaining for an event to begin. The Calendar structure includes a
Calendar initializer.
version of the dateComponents() method that allows us to compare two dates
Listing 4-24: Using a Gregorian calendar and get the difference expressed in a specific component.

Listing 4-26: Comparing dates
import Foundation

let id = Calendar.Identifier.gregorian import Foundation
let calendar = Calendar(identifier: id)
let calendar = Calendar.current
var comp = DateComponents() var comp = DateComponents()
comp.year = 1970 comp.year = 1970
comp.month = 8 comp.month = 8
comp.day = 13 comp.day = 21
var birthday = calendar.date(from: comp) // "Aug 13, 1970 at 12:00 AM"
 var today = Date()
var birthdate = calendar.date(from: comp)
Declaring a specific calendar is not only recommended when creating new if let olddate = birthdate {
dates but also when calculating dates by adding components, as in the let components = calendar.dateComponents([.day], from: olddate, to: today)
following example. print("Days between dates: \(components.day!)")
}
 the argument.
This example calculates the days between a birthdate and the current date. intersection(with: DateInterval)—This method returns a
The value returned by the date() method used to generate the birthdate value with the interval in which the original interval and the
DateInterval
returns an optional, so we unwrap it before calculating the difference. We one provided by the with argument overlap.
assign this value to the olddate constant and then compare it with the
current date. The number of days between the dates is returned and A typical use of the DateInterval structure is to create an interval from two
printed on the console. dates and check if a specific date falls within the interval, as in the
Another way to specify intervals between dates is with the DateInterval following example.
structure. This structure allows us to create an interval with Date values.
The following are its initializers. Listing 4-27: Finding a date in an interval

As with numbers, Foundation also provides the tools to format dates. The a FormatStyle structure to format the date any way we want. The following
Date structure defines two versions of the formatted() method for this are some of the methods included for customization.
purpose.
day(Day)—This method includes the day. The argument defines the
formatted(date: DateStyle, time: TimeStyle)—This methods style for the day. It is a structure with the properties defaultDigits,
formats the date with the styles specified by the arguments. The date ordinalOfDayInMonth, and twoDigits.
argument defines the style for the date. It is a structure with the type month(Month)—This method includes the month. The argument
properties abbreviated, complete, long, numeric, and omitted. And the time defines the style for the month. It is a structure with the properties
argument defines the style for the time. It is a structure with the type abbreviated, defaultDigits, narrow, twoDigits, and wide.
properties complete, omitted, shortened, and standard.
year(Year)—This method includes the year. The argument defines
formatted(Date.FormatStyle)—This method formats the date with the style for the year. It is a structure with the properties defaultDigits
the styles specified by the argument. The argument is a FormatStyle and twoDigits.
structure defined by the Date structure.
hour(Hour)—This method includes the hour. The argument defines
the style for the hour. It is a structure with the properties
If all we need is a standard format, we can call the formatted(date:, time:)
defaultDigitsNoAMPM and twoDigitsNoAMPM.
method with the styles we want for the date and time. The method takes
these values and returns a string with a date in the format defined by the minute(Minute)—This method includes the minutes. The argument
current locale (the user's language and location). defines the style for the minutes. It is a structure with the properties
defaultDigits and twoDigits.
Listing 4-28: Formatting dates

second(Second)—This method includes the seconds. The argument
import Foundation defines the style for the seconds. It is a structure with the properties
defaultDigits and twoDigits.
let mydate = Date.now
let text = mydate.formatted(date: .abbreviated, time: .omitted) weekday(Weekday)—This method includes the weekday. The
print(text) // "Jun 18, 2021"

argument defines the style for the day. It is a structure with the
properties abbreviated, narrow, oneDigit, short, twoDigits, and wide.
The code in Listing 4-28 gets the current date from the now property and
then calls the method with the abbreviated and omitted values. This creates a The FormatStyle structure includes the following properties to configure the
string that contains a date with abbreviated text and no time ("Jun 18, parameters used to format the date.
2021").
Standard styles include all the components of the date, but the Date calendar—This property sets or returns the calendar used to format
structure includes an additional version of the formatted() method that takes the date. It is of type Calendar.
locale—This property sets or returns the locale used to format the In this code, we implement the day(), month(), and hour() methods. The result
date. It is of type Locale. is a string with a date that includes the month (full name), the day, and the
hour ("June 18, 6 PM").
timeZone—This property sets or return the time zone used to
Notice that the order in which the methods are called doesn't matter. The
format the date. It is of type TimeZone.
date and time are always formatted with a standard format that depends
on the user's locale (language and country). This is because the formatted()
Although we can create our own FormatStyle structure, the structure
method processes dates according to local conventions, including the
includes a type property called dateTime to return an instance with the
language, symbols, etc. This means that the components of a date are
calendar and standard values set by the device. If the configuration by
going to be interpreted according to the conventions currently set on the
default is enough, we can use this property to format the date, as shown
device. For example, the same date will look like this "Tuesday, August 6,
next.
2021" for a user in the United States and like this "2021 8 6 年 月 日 星期二
"
Listing 4-29: Specifying a custom format for a user in China. How dates are processed is determined by an object of
 the Locale structure. Every device has a Locale structure assigned by default,
import Foundation and our code will work with it unless we determine otherwise. To get a
reference to the current structure or create a new one, the Locale structure
let mydate = Date.now
includes the following initializer and type property.
let text = mydate.formatted(.dateTime.weekday(.wide))
print(text) // "Friday"
 Locale(identifier: String)—This initializer creates a Locale structure
configured for the region determined by the value of the argument.
The code in Listing 4-29 calls the weekday() method from the FormatStyle
The argument is a string that represents a language and a region (e.g.,
structure returned by the dateTime property to get the day of the week. In
en_US for the United States, zh_CN for China).
this case, we call the method with the value wide, which returns the day's
full name ("Friday"). Only one component is included in this example, but current—This type property returns the Locale structure assigned by
we can add more by concatenating the methods with dot notation, as we default to the device or defined by the user in the Settings app.
did before for numbers.
The FormatStyle structure includes the following method to format a date for
Listing 4-30: Including multiple date components
a locale.

import Foundation
locale(Locale)—This method specifies the locale to use by the
let mydate = Date.now formatter.
let text = mydate.formatted(.dateTime.day().hour().month(.wide))
print(text) // "June 18, 6 PM"
 Although it is recommended to use the Locale structure set by the system
and keep the values by default, there are times when our application must
present the information with a specific configuration. For example, we may our device), but we can define a different one as we did with the Locale
need to create an application that always shows dates in Chinese, no structure. To get a reference to the current structure or create a new one,
matter where the user is located. We can do this by defining a Locale the TimeZone structure includes the following initializer and type property.
structure and then include the locale() method in the formatter with this
value. TimeZone(identifier: String)—This initializer creates a TimeZone
structure configured for the time zone determined by the value of the
Listing 4-31: Specifying a different locale identifier argument. The argument is a string that represents the

name of the time zone (e.g., "Europe/Paris", "Asia/Bangkok").
import Foundation
current—This type property returns the TimeZone structure assigned
let mydate = Date.now
let chinaLocale = Locale(identifier: "zh_CN")
by default to the device or defined by the user in the Settings app.
let text = mydate.formatted(.dateTime.locale(chinaLocale).day().month().year())
年月 日
print(text) // "2021 6 18 "
The FormatStyle structure does not include a method to provide a specific

time zone to format the date. For this purpose, we must create a custom
This example creates a new Locale structure with the zh_CN identifier, instance and then assign the time zone to the structure's timeZone property,
which corresponds to China and the Chinese language, and then formats as in the following example.
the date with this locale and the day(), month(), and year() methods. The result
is a string with the date in Chinese ("2021 6 18 ").年月 日 Listing 4-32: Working with different time zones

import Foundation
IMPORTANT: The list of identifiers you can use to create a Locale
structure is extensive, but you can print the type property if let tokyoTimeZone = TimeZone(identifier: "Asia/Tokyo"), let madridTimeZone =
TimeZone(identifier: "Europe/Madrid") {
availableIdentifiers from the Locale structure to get an array with all the let mydate = Date.now
values available. let mytime = mydate.formatted(.dateTime.hour().minute().second())
The value declared for the Measurement structure is the number that
determines the magnitude, like 55 in 55 km, and the unit is a property of a
subclass of the Dimension class that represents the unit of measurement, like
km in 55 km. The Dimension class contains all the basic functionally required
for measurement but is through its subclasses that the units of
measurement are determined. Foundation offers multiple subclasses to
define units for different types of dimensions. The following are the most
frequently used.
decameters, meters, decimeters, centimeters, millimeters, micrometers, nanometers, converted(to: Unit)—This method converts the values of the
picometers, inches, feet, yards, miles, scandinavianMiles, lightyears, nauticalMiles, Measurementstructure to the unit specified by the to argument and
fathoms, furlongs, astronomicalUnits, and parsecs, with meters defined as the returns a new Measurement structure with the result. The argument is a
basic unit. property of a subclass of the Dimension class.
UnitMass—This subclass of the Dimension class defines the units of
measurement for mass. The subclass includes the following properties The initialization of a Measurement structure is simple, we just need to
to represent the units: kilograms, grams, decigrams, centigrams, milligrams, provide the value for the magnitude and the property that represents the
micrograms, nanograms, picograms, ounces, pounds, stones, metricTons, shortTons,
unit of measurement we want to use. The following example creates two
carats, ouncesTroy, and slugs, with kilograms defined as the basic unit.
structures to store a measurement of 30 centimeters and another of 5
pounds.
UnitVolume—This subclass of the Dimension class defines the units of
measurement for volume. The subclass includes the following Listing 4-33: Initializing Measurement structures
properties to represent the units: megaliters, kiloliters, liters, deciliters, 
centiliters, milliliters, cubicKilometers, cubicMeters, cubicDecimeters, import Foundation
cubicMillimeters, cubicInches, cubicFeet, cubicYards, cubicMiles, acreFeet, bushels,
var length = Measurement(value: 30, unit: UnitLength.centimeters) // 30.0 cm
teaspoons, tablespoons, fluidOunces, cups, pints, quarts, gallons, imperialTeaspoons, var weight = Measurement(value: 5, unit: UnitMass.pounds) // 5.0 lb

imperialTablespoons, imperialFluidOunces, imperialPints, imperialQuarts,
imperialGallons, and metricCups, with liters defined as the basic unit.
If the measurements are of the same dimension (e.g., length), we can
perform operations with their values. The Measurement structure allows the
The Measurement structure includes the following properties and methods to
operations +, -, *, /, and also the use of the comparison operators ==, !=, <, >,
access the values and convert them to different units.
<=, and >= to compare values. The following example adds two
measurements in centimeters.
value—This property sets or returns the structure's value. It is of type
Double. Listing 4-34: Adding the values of two Measurement structures
unit—This property sets or returns the structure's unit of 
var length = Measurement(value: 300, unit: UnitLength.meters) The formatted() method requires a FormatStyle structure to format the value.
var width = Measurement(value: 2, unit: UnitLength.kilometers)
The structure includes the following initializer and type method to create
var total = length + width // 2300.0 m an instance for every type of unit.

The FormatStyle structure also includes an additional initializer and method import Foundation
The width argument specifies how the unit is going to be displayed. It this argument is not declared, the formatter uses the value by default,
which formats the measurement with the configuration and locale set in
is a structure with the properties abbreviated, narrow, and wide. The
the device. For instance, if we specify the road value instead, the formatter
locale argument specifies the locale. The usage argument specifies
will format the value to represent a distance using the device's locale,
the purpose of the formatted measurement. The structure to declare
which for a device running in the United States means that the original
this value includes properties for any type of measurement. The ones value will be converted to miles.
available are asProvided (UnitType), food (UnitEnergy), general (UnitType),
person (UnitTemperature), personHeight (UnitLength), personWeight Listing 4-38: Formatting a measurement for a specific purpose
(UnitMass), road (UnitLength), weather (UnitTemperature), and workout 
of the unit is going to be displayed, and the numberFormatStyle let length = Measurement(value: 40, unit: UnitLength.kilometers)
argument specifies the format of the value. let text = length.formatted(.measurement(width: .wide, usage: .road))
print(text) // "25 miles"
measurement(width: UnitWidth, locale: Locale, usage: 
The formatted() method in this example includes the width argument to get
the unit's full name, the usage argument to format the value to represent
distance, and the numberFormatStyle argument to format the value. The
value is expressed in miles again, but it is more accurate.
If our application must display a value always with the same unit of
measurement independently of the device's location, we can set a specific
locale. The formatted() method doesn't include an argument to designate a
locale, but the initializers included in the FormatStyle structure do. The
following example formats the measurements in Chinese, no matter where
the device is located.
The code in Listing 4-40 initializes a FormatStyle structure with the locale
configured for China and then calls the formatted() method with this
structure to format the value. Notice that the FormatStyle structure is
defined inside the Measurement structure, which is a generic structure and
therefore we must specify the type of values the structure is going to
process. In this case, we are working with units of length so we must
specify the UnitLength data type.
Timer The scheduledTimer() method creates a timer according to the value of its
arguments and automatically adds it to an internal loop that will process it

when the time is up. The time is set in seconds with a TimeInterval value (a
typealias of Double), and we can declare the closure as a trailing closure to
Timers are objects created from the Timer class that perform an action after
simplify the code, as in the following example.
a specific period of time. There are two types of timers: repeating and non-
repeating. Repeating timers perform the action and then reschedule
Listing 4-41: Creating a non-repeating timer
themselves to do it again in an infinite loop. Non-repeating timers, on the

other hand, perform the action once and then invalidate themselves. The
import Foundation
Timer class includes the following properties and methods to create and
manage timers. print("Wait 5 seconds...")
Timer.scheduledTimer(withTimeInterval: 5.0, repeats: false) { (timer) in
print("The time is up")
isValid—This property returns a Boolean value that indicates if the }

timer can still be fired, or it was invalidated.
timeInterval—This property returns the time interval in seconds for The code in Listing 4-41 creates a non-repeating timer. It prints a message
repeating timers. and then initializes a timer with the scheduleTimer() method. The timer is set
tolerance—This property sets or returns a period of tolerance in to 5 seconds, non-repeating, and the closure just prints another message
seconds to provide the system with more flexibility. It is a value of on the console. When we execute the code, the first message appears on
type TimeInterval. The value by default is 0. the console and after 5 seconds the message "The time is up" is printed
scheduledTimer(withTimeInterval: TimeInterval, repeats: below.
Bool, block: Closure)—This type method returns repeating and The closure receives a reference to the Timer object that we can use to
non-repeating timers depending on the values of its arguments. The access the timer’s properties or invalidate it. It was not required in the last
withTimeInterval argument represents the seconds the timer must example, but it may be useful when working with repeating timers, as in
wait before performing the action, the repeats argument is a Boolean the following example.
value that determines if the timer is repeating (true) or non-repeating
(false), and the block argument is the closure to be execute when the Listing 4-42: Creating a repeating timer
time is up. 
import Foundation
fire()—This method fires the timer without considering the time
remaining. var counter = 0
invalidate()—This method invalidates the timer (stops the timer). func startTimer() {
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (timerref) in
report(timer: timerref)
}
} 4.3 Core Graphics
func report(timer: Timer) {
print("\(counter) times") 
counter += 1
if counter > 10 {
print("Finished") Core Graphics is an old framework programmed in the C language. It was
timer.invalidate()
} developed to provide a platform-independent two-dimensional drawing
} engine for Apple systems. The framework is composed of basic drawing
startTimer()

tools and its own data types. Due to its characteristics, instead of being
replaced, the framework was integrated with newer frameworks and,
In this code, we define two functions. The startTimer() function schedules therefore, it remains in use.
the timer, and the report() function is executed when the time is up. In this
last function, we count how many times the code is executed with the
counter variable and print a message on the console with the number. If the
value is greater than 10, we print the text "Finished" and invalidate the
timer, so the function is not executed anymore (repeating timers keep
running indefinitely until they are invalidated).
Data Types The structure defines initializers to create instances from values of
type Int, CGFloat, and Double.

zero—This type property returns a CGPoint structure with its values
What modern applications require the most from this old framework are set to zero.
its data types. In Swift, Core Graphics’ data types are implemented as x—This property sets or returns the structure's x coordinate.
structures, with their own initializers, properties, and methods. They can
store values that represent attributes of elements on the screen, such as
y—This property sets or returns the structure's y coordinate.
position or size. The following is a structure included in the framework to
specify floating-point values. There is also a more complex structure called CGRect that we can use to
define and work with rectangles. This data type includes the following
initializers and properties.
CGFloat—This structure is used to store values of type Double for
drawing purposes.
CGRect(origin: CGPoint, size: CGSize)—This initializer creates a
CGRect structure to store the origin and size of a rectangle. The origin
A more complex structure is CGSize, designed to store values that represent
dimensions. This data type includes the following initializer and properties. argument is a CGPoint structure with the coordinates of the rectangle's
origin, and the size argument is a CGSize structure with the rectangle's
CGSize(width: CGFloat, height: CGFloat)—This initializer creates width and height.
a CGSize structure with the values specified by the width and height CGRect(x: CGFloat, y: CGFloat, width: CGFloat, height:
arguments. The structure defines initializers to create instances from CGFloat)—This initializer creates a CGRect structure to store the
values of type Int, CGFloat, and Double. origin and size of a rectangle. The x and y arguments define the
zero—This type property returns a CGSize structure with its values set coordinates of the rectangle's origin, and the width and height
to zero. arguments its size. The structure defines initializers to create instances
from Int, CGFloat, and Double values.
width—This property sets or returns the structure's width.
zero—This type property returns a CGRect structure with its values set
height—This property sets or returns the structure's height.
to zero.
Another structure defined by the framework is CGPoint, which is used to origin—This property sets or returns a CGPoint structure with the
define points in a two-dimensional coordinate system. It includes the coordinates of the rectangle's origin.
following initializer and properties. size—This property sets or returns a CGSize structure with the
rectangle's width and height.
CGPoint(x: CGFloat, y: CGFloat)—This initializer creates a CGPoint
structure with the coordinates specified by the x and y arguments.
midX—This property returns the value of the rectangle’s x coordinate Listing 4-45: Accessing the structures inside a CGRect structure
located at the horizontal center of the rectangle. 
import CoreGraphics
midY—This property returns the value of the rectangle’s y coordinate
located at the vertical center of the rectangle. var myrect = CGRect(x: 30, y: 20, width: 100, height: 200)
Listing 4-43: Initializing Core Graphics' structures When we don't have initial values for the coordinates or the size, we can

use the zero type property to create a structure with all the values
import CoreGraphics initialized to 0.
var myfloat: CGFloat = 35
var mysize: CGSize = CGSize(width: 250, height: 250) Listing 4-46: Assigning empty structures to a CGRect variable
var mypoint: CGPoint = CGPoint(x: 20, y: 50) 
var myrect: CGRect = CGRect(origin: mypoint, size: mysize)
 import CoreGraphics
Listing 4-44: Using the CGRect convenience initializer The myrect variable of Listing 4-46 is a CGRect structure with all its properties
 initialized with the value 0. Assigning the value of the zero property to a
import CoreGraphics variable is the same as using the initializer CGRect(x: 0, y: 0, width: 0, height: 0).
var myrect = CGRect(x: 30, y: 20, width: 100, height: 200) The CGRect structure also includes properties to calculate values from its
coordinates and size. For example, the midX and midY properties return the
print("The origin is at \(myrect.origin.x) and \(myrect.origin.y)")
print("The size is \(myrect.size.width) by \(myrect.size.height)") coordinates at the center of each side.

Listing 4-47: Calculating the coordinate at the center of the rectangle
The origin and size properties of a CGRect value are CGPoint and CGSize 
structures, respectively, so they can be copied into other variables or import CoreGraphics
properties as any other values. var rect = CGRect(x: 0, y: 0, width: 100, height: 100)
UIKit (User Interface Kit) is the framework provided by Apple to define the When the user taps on the icon to run the application, the first step
elements of the graphic interface. From text to buttons, all the standard performed by the system is to create an object of a class called UIApplication
elements that users interact with on the screen to insert, select and (the prefix UI stands for UIKit). This object starts a loop to keep the
process information are defined by the classes in this framework. Although application running, it checks for events generated by the user or the
its primary function is to create the user interface, the framework also system, reports changes in the state of the app, and provides access to the
includes several classes to create the objects the application needs to work windows and the user interface.
and to connect with the rest of the system. The UIApplication object is automatically created as soon as our app is
launched and works along with the system to keep the application
Figure 5-1: App's ecosystem responsive. There is nothing we need to do to set up this object, but there
are times when we must access the object to configure the app or respond
to changes reported by the system. For this purpose, the class includes the
following type property.
Devices like Mac computers and iPads can work with multiple instances of
an app (multiple windows). In UIKit, these instances are called Scenes. Each
copy of the app is a Scene, and the Scenes are associated to Scene sessions
for configuration. The UIApplication class includes the following properties to
manage the windows, the Scenes, and the Scene sessions of our
application.
learn, practice, and test code, but cannot simulate a system such as a
supportsMultipleScenes—This property returns a Boolean to mobile device, and therefore, UIKit classes only return values by
indicate if the application supports multiple Scenes (multiple default. Later in this chapter, we will learn how to create real
windows). applications with the Xcode main interface.
connectedScenes—This property returns a set with references to
the Scenes that are currently connected to the application.
openSessions—This property returns a set with references to the
Scene sessions currently active or archived by the system.
windows—This property returns an array of UIWindow objects
representing the visible and hidden windows managed by the app.
The following example illustrates how to access the UIApplication object and
how to read its properties.
In the code of Listing 5-1, we first import UIKit to have access to all the
tools defined in this framework and then read the shared property to get a
reference to the UIApplication object created for our app. With this
reference, we check whether the application allows the user to open
multiple windows or not by reading the supportsMultipleScenes property.
current—This type property returns the instance of the UIDevice class let current = UIDevice.current
let deviceName = current.systemName
that represents the device in which the app is currently running. let deviceVersion = current.systemVersion
iPhone 6S/7/8/SE2 375 x 667 points 750 x 1334 pixels 2x scale Figure 5-2: Coordinate system
iPhone 6S/7/8 Plus 414 x 736 points 1080 x 1920 pixels 3x scale
iPhone X/XS/11 Pro 375 x 812 points 1125 x 2436 pixels 3x scale
iPhone XR/11 414 x 896 points 828 x 1792 pixels 3x scale
iPhone XS Max/11 Pro Max 414 x 896 points 1242 x 2688 pixels 3x
scale
iPhone 12/13 Mini 375 x 812 points 1080 x 2340 pixels 3x scale
iPhone 12/13 12/13 Pro 390 x 844 points 1170 x 2532 pixels 3x
scale
iPhone 12/13 Pro Max 428 x 926 points 1284 x 2778 pixels 3x
scale

iPad Pro 12.9in 1024 x 1366 points 2048 x 2732 pixels 2x scale
iPad Pro 11in 834 x 1194 points 1668 x 2388 pixels 2x scale
The properties bound, nativeBounds, and scale included in the UIScreen class
iPad Pro 10.5/ Air 3rd 834 x 1112 points 1668 x 2224 pixels 2x
return the values corresponding to the screen of the device where the app
scale
is running. The bound and nativeBounds properties contain CGRect structures,
iPad Air 4th 820 x 1180 points 1640 x 2360 pixels 2x scale
while scale is just a CGFloat value.
iPad 7th 810 x 1080 points 1620 x 2160 pixels 2x scale
Listing 5-3: Getting information from the screen Windows


import UIKit
let screen = UIScreen.main The window is a space on the screen where the elements of the user
let pointsWidth = screen.bounds.size.width
interface are laid out. UIKit includes the UIWindow class to create the object
let pointsHeight = screen.bounds.size.height that manages the windows in our app. When the system creates a new
print("Width: \(pointsWidth) x Height: \(pointsHeight)") instance of our app (Scene), either because the app was just launched or
let pixelsWidth = screen.nativeBounds.size.width because the user requested a new window, a UIWindow object is created
let pixelsHeight = screen.nativeBounds.size.height and assigned to the Scene.
print("Width: \(pixelsWidth) x Height: \(pixelsHeight)")
A UIWindow object defines the space occupied by the user interface, but the
print("Scale: \(screen.scale)") interface is created with objects called Views, as we will see next.

Therefore, once the UIWindow object is initialized, we must tell the system
which views are going to be shown first. The following is the property
The native resolution in pixels is always returned considering a portrait
included by the class for this purpose.
orientation, but the size in points is returned according to the current
orientation. For example, the iPhone X’s screen is 375 points wide and 812
rootViewController—This property sets or returns a reference to
points tall in portrait, but 812 points wide and 375 points tall in landscape.
the object that controls the app’s initial view.
There could be multiple windows available (one per Scene), and some may
be hidden. The following are some of the properties and methods provided
by the class to manage these situations.
Views The following are the most useful initializer, properties, and methods
available.

UIView(frame: CGRect)—This initializer creates a UIView object in
The window is the space where the graphics are displayed, but it does not
the position and size determined by the frame argument.
generate any visible content. The user’s interface is built inside the window
from similar containers called Views. These views are rectangular areas of frame—This property sets or returns a CGRect value that determines
custom size, designed to display graphics on the screen. Some views are the position and size of the rectangular area occupied by the view.
used just as containers while others are improved to present graphic tools, bounds—This property sets or returns a CGRect value that
such as buttons and switches, and graphic content, such as images and determines the position and size of the rectangular area occupied by
text. The views are organized in a hierarchy, one inside another, with a view
the view inside its own frame.
of the size of the window as the root (usually called main view or container
view), as illustrated next. backgroundColor—This property sets or returns an object that
determines the color of the view’s background. The value is an object
Figure 5-3: Views hierarchy of the UIColor class.
alpha—This property sets or returns a CGFloat value that determines
the view’s alpha level (the level of transparency). The property takes
values from 0.0 (transparent) to 1.0 (opaque).
isHidden—This property sets or returns a Boolean value that
determines whether the view is visible (false) or hidden (true).
isOpaque—This property sets or returns a Boolean value that
determines whether the view is opaque or not.
 contentMode—This property sets or returns a value that
determines the mode used by the view to lay out its content when its
No matter the purpose of the view (to be a container or to present size changes (frequently used with images). It is an enumeration
content), they are all created from objects of the UIView class. This is a basic called ContentMode included in the UIView class. The values available are
class that is only capable of creating and managing the rectangular area scaleToFill, scaleAspectFit, scaleAspectFill, redraw, center, top, bottom, left, right,
occupied by the view, but subclasses of this class are defined to add
topLeft, topRight, bottomLeft, and bottomRight.
functionality and present any type of content we want. Although the
objects created directly from the UIView class are very limited, they have an clipsToBounds—This property sets or returns a Boolean value that
extensive list of properties and methods to configure and draw their views. determines whether the content of the view is confined to the view’s
bounds or not.
isUserInteractionEnabled—This property sets or returns a Boolean The initializer of the UIView class takes a parameter called frame of type
value that determines whether the view responds to the user’s CGRect to establish the position and size of the view. The view created by
interaction (e.g., a tap of the finger). the code in Listing 5-4 is positioned at the window's coordinates 0, 0 and
has a size of 375 by 667 points.
isMultipleTouchEnabled—This property sets or returns a Boolean
value that determines if the view can handle multiple touch events.
Do It Yourself: Replace the code in your Playground file with the
superview—This property returns a reference to the UIVIew object code in Listing 5-4. In the Results Side Bar, click on the Quick Look
that is the container of the view. If the view is not inside another view, button corresponding to the line that creates the container object to
the value returned is nil. see a representation of the view. At this moment, you will only be
subviews—This property returns an array containing references to able to see a gray rectangle.
the UIView objects that are inside the view (subviews).
tag—This property sets or returns an integer value that identifies the The background color of the UIView object is set as transparent by default.
view. If we want to change the color, we must assign a new value to the
backgroundColor property of the view. This value must be provided as an
viewWithTag(Int)—This method searches for a subview of the view object of the UIColor class. This class includes some initializers and
with the tag specified by the argument. The value returned is an properties to create the objects. The following are the most frequently
optional containing a UIView object or nil in case no view with that tag used.
was found.
UIColor(red: CGFloat, green: CGFloat, blue: CGFloat, alpha:
The window is positioned inside the screen, while the views are positioned CGFloat)—This initializer creates a UIColor object with a color set by
inside the window and inside one another, generating a hierarchical the values of its arguments. It takes values from 0.0 to 1.0. The red
structure (see Figure 5-3). As we already mentioned, the position of these argument defines the level of red, green the level of green, blue the
elements is determined by a coordinate system. For instance, the next
level of blue, and alpha defines the alpha level (transparency).
example creates a view of 375 points by 667 points, akin to the main view
of a small iPhone. UIColor(patternImage: UIImage)—This initializer creates a UIColor
object with a color defined as the image provided by the
Listing 5-4: Creating a view patternImage argument. The image will be replicated as many times
 as necessary to fill the area to which the color is assigned.
import UIKit
color to the appearance (light or dark): systemBlue, systemBrown, systemGreen, the type property systemRed, although with the difference that this color
systemIndigo, systemOrange, systemPink, systemPurple, systemRed, systemTeal, adapts to the appearance (light or dark).
systemYellow, systemCyan, systemMint, systemGray, systemGray2, systemGray3,
systemGray4, systemGray5, and systemGray6. There are also properties that Listing 5-6: Assigning an adaptable color to the view
return predefined colors for labels: label, secondaryLabel, tertiaryLabel, and 
quaternaryLabel. And properties with predefined fill colors: systemFill, import UIKit
secondarySystemFill, tertiarySystemFill and quaternarySystemFill. A few properties
let container = UIView(frame: CGRect(x: 0, y: 0, width: 375, height: 667))
with predefined colors for backgrounds: systemBackground, let color = UIColor.systemRed
secondarySystemBackground, and tertiarySystemBackground. Other properties that container.backgroundColor = color

return colors for a variety of interface elements, such as placeholderText,
separator, and link. A property called clear to assign a transparent color.
Do It Yourself: Replace the code in your Playground file with the
Another called tintColor that represents the app's global tint color. And
code in Listing 5-6. In the Result Side Bar, click on the Show Result
finally, some properties with fixed colors that do not adapt to the
appearance: black, blue, brown, cyan, darkGray, gray, green, lightGray, magenta, button corresponding to the line that assigns the new color to the
backgroundColor property of the view. You should see a rectangular red
orange, purple, red, white, and yellow.
UIColor objects are assigned to the views and most of the elements in the view within the Editor Area.
user interface to define their colors. They are created from the initializers
or type properties listed above and then assigned to the corresponding IMPORTANT: The UIColor initializer uses values from 0.0 to 1.0 to
properties, as in the following example. determine the levels of red, green, and blue that define the
composition of the color, but RGB colors are usually determined with
Listing 5-5: Assigning a background color to a view integer values from 0 to 255. If you prefer to work with these values,
 you can convert them dividing the values by 255.0. For example, if
import UIKit the level of red is 190, you can get the value for the initializer with
the formula 190.0 / 255.0.
let container = UIView(frame: CGRect(x: 0, y: 0, width: 375, height: 667))
var color = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0) The user’s interface is built by adding views to parent views that work as
container.backgroundColor = color
containers, creating the hierarchical structure introduced in Figure 5-3. The

container view is called the superview and the views inside are called
The color for the UIColor object created in Listing 5-5 is defined by the subviews. When a view is added to a superview, the object is stored in the
initializer's arguments. The system used in this case is called RGB. The color subviews property of the superview. This is a property containing an array of
is constructed by adding the values of every component: Red, Green, and views. To add and manage the views inside this array, the UIView class offers
Blue. In this example, the red argument was set to 1.0 while the rest of the the following methods.
colors were set to 0.0, defining a pure red. A similar color is returned by
addSubview(UIView)—This method adds a view at the end of the container.backgroundColor = UIColor.systemRed
subviews array. let subview1 = UIView(frame: CGRect(x: 20, y: 20, width: 335, height: 300))
subview1.backgroundColor = UIColor.systemGray4
insertSubview(UIView, at: Int)—This method inserts a view in the
subviews array at the index indicated by the at argument. let subview2 = UIView(frame: CGRect(x: 20, y: 347, width: 335, height: 300))
subview2.backgroundColor = UIColor.systemGray4
insertSubview(UIView, aboveSubview: UIView)—This method
container.addSubview(subview1)
inserts a view in the subviews array above the subview indicated by the container.addSubview(subview2)
aboveSubview argument. 
The subviews are shown on the screen according to their position on the
subviews array. The subviews with a lower index are drawn first, so the views
with a higher index are shown at the top. This is why the method most 
frequently used to add a view to another view is addSubview(). This method
adds the view to the end of the subviews array, effectively drawing the view Every subview is attached to its superview and positioned on the screen
at the front. according to its coordinate system. When we set the values of the x and y
parameters in the UIView initializer for every view, we must consider the
Listing 5-7: Adding subviews to a container view coordinates of their superviews, not the screen or other container views
 up the chain. For example, the position of the subview1 view inside the
import UIKit container view is 20, 20, but its own coordinate system starts again from 0, 0
at its top-left corner. The same happens with subview2. Any views added to
let container = UIView(frame: CGRect(x: 0, y: 0, width: 375, height: 667))
these subviews will have a position according to the coordinates of their UIView Subclasses
superviews, not the coordinates of the container view or the window. Figure

5-5 illustrates these views and their coordinate systems.
The views created from the UIView class are good as containers and
Figure 5-5: Subviews and their coordinates
organizers but do not present any content. The content is generated by
subclasses of UIView. These subclasses overwrite some UIView methods and
provide their own properties and methods to draw graphics inside the view
or respond to user interaction. Considering how difficult and time
consuming it is to create every single element of the interface from
scratch, UIKit provides ready to use subclasses of UIView for the creation of
standard elements. From labels and buttons to images and tables, there is
a UIView subclass to present on the screen everything our app needs. Figure
5-6 shows a scheme with the UIView class and some of its most important
subclasses.
These values are provided by the frame and bounds properties of the UIView
objects that represent each view. For example, the x and y values of the
frame property of subview1 are 20, 20, but the x and y values of the bounds
property of subview1 are 0, 0, because the bounds property refers to the
view’s internal frame and its own coordinate system.
iPads and Mac computers allow multiple instances of our app to run at the
same time. When the user opens multiple windows with our app, the
system generates one Scene per window.
Creating the app's interface by adding subviews to the main view from our Applications are built from several files and resources, including our own
code, as we did in previous examples, may be appropriate for very small codes, frameworks, images, databases, and more. Xcode organizes these
applications, but as the interface grows and new elements and elements into projects. An Xcode project comprises all the information and
functionality are introduced, the code becomes difficult to develop and resources necessary to create one application. The welcome window,
illustrated in Figure 1-2, presents a button called Create a new Xcode
maintain. This is especially true with mobile applications, where we not
project to initiate a project. When we click on this button, a new window
only have to define the initial interface but also adapt it to new conditions
presents the templates we can choose from to create our project.
produced by the rotation of the screen or the app running on different
devices. Xcode expedites our work by providing a set of graphic tools that Figure 5-9: Selecting the type of project we want to create
allow us to create and modify the views and the application. This toolset
includes an editor, a visual interface, resource managers, configuration
panels, and debugging tools, all integrated into a single working space.
button that opens a popup window with tools to create the user Running the App
interface (Figure 5-12, number 5), and two buttons to show or remove 
the Navigator Area and the Utilities Area (Figure 5-12, number 3 and
4). Xcode offers two ways to run our application: the simulator or a real
Navigator Area—This is a removable area that provides information device. The buttons to select these options, execute, and stop the app are
about the files that comprise the application and tools for debugging on the toolbar.
(identify and remove errors in the code). From here, we can select the
files to edit, create groups to organize those files, add resources, Figure 5-13: Buttons to run the app
check for errors, and more. In addition to the files, this area shows an
option at the top to configure the app (Figure 5-12, number 6).
Editor Area—This is the only non-removable area and is the one 
where we will do much of the work. The content of files and
configuration panels are displayed here. Although the Editor Area Applications are run on a specific destination. This destination could be
cannot be removed, it includes buttons at the top to split the area different things, from real devices to windows or simulators, and have
different configurations, including settings like the region where the device
into multiple editors or panels, as we will see later (Figure 5-12,
is located, and the human language used by the app to display the
number 8).
information on the screen. Xcode allows us to define and configure the
Debug and Console Area—This is a removable area that splits into possible destinations for our app using an arrangement called Scheme.
two sections that can be shown or hidden by the buttons at the When a project is created, a Scheme is automatically generated for us with
bottom (Figure 5-12, number 7). The section on the left provides options that are according to the type of project and our local settings
information for debugging, while the section on the right is a console (region and language set on the computer). For example, the default
to display the results of the execution of our code, including warnings Scheme of a project for a mobile application includes several iOS
and errors. simulators and offers access to the devices currently connected to the
computer. The Scheme is selected from the button on the left of the
Utilities Area—This is a removable area that provides additional
Scheme section and the destination is selected from the button on the
information about the app and allows us to edit the configuration of
right. When the destination button is clicked, we can see a list of the
the interface and its elements. devices and simulators available.
Figure 5-15: iPhone simulator running the app created by the App template
Templates include files with resources and sample code that we can use to
start building our application, but to take advantage of these files we need
to understand first how Xcode suggests applications should be built and
the tools it provides for this purpose.
Xcode proposes an architectural paradigm that divides the application in This structure is created from several objects. The interface is created from
three interconnected parts, the Model, the View, and the Controller (called the UIView objects introduced before, which are connected to controller
MVC for short). Every part of the code performs a specific task and objects called View Controllers, and these objects are connected to the
communicates to the other parts only the information strictly necessary. objects that manage the data. Although we can define all the objects from
Figure 5-16 illustrates the elements involved. scratch, the files created by the App template provide the definition of
some of the classes we need to create these objects and other resources.
Figure 5-16: MVC architecture
AppDelegate.swift—This file defines a class called AppDelegate to
create an object that is going to work as the delegate of the
object. Every time the state of the application changes,
UIApplication
methods of this delegate are called to report the change.
SceneDelegate.swift—This file defines a class called SceneDelegate to
create an object that is going to work as delegate of the Scenes
(windows). When a Scene is created, an object of this class is assigned
to it. Every time the state of the Scene changes, methods of this
delegate are called to report the change to our code.
ViewController.swift—This file defines a class called ViewController to
control the only view added to the Storyboard by the App template.
Main.storyboard—This file defines our app’s Storyboard; a visual

editor to create the user interface. The Storyboard provided by the
The Model is the data the app is going to process, the View is the user App template includes only one view, but we can add more as we will
interface (window and views), and the Controller is the object in charge of see later.
receiving input from the views, processing the data, and updating the
Assets.xcassets—This is a folder called Assets Catalog used to
interface. The objects managing the data and the views are not connected
with each other, they send the information to the controller and this code organize the app’s resources.
decides what to do with it. This allows reusability. We can use the same LaunchScreen.storyboard—This is a Storyboard with a single view
code to control different views or replace the user’s interface entirely used to define the app’s launch screen (the screen shown by the
without having to change a line of code in the rest of the program.
system while the app is starting up).
Info.plist—This is a file with structured text to define the app’s basic Application Delegate
configuration. 
Do It Yourself: Open the Navigator Area (the panel on the left). You The first file on the list is called AppDelegate.swift and contains a class
should see the list of files created by the App template. If the files called AppDelegate. As we already mentioned, when the app is launched, the
are not visible, click on the Project Navigator button at the top-left system creates an object of the UIApplication class to provide the basic
corner of the Navigator Area. functionality necessary for the app to process information and respond to
user interaction. The object created from the AppDelegate class is assigned as
the delegate of the UIApplication object. Every time the system needs to
report a change in the state of the application to our code, it calls methods
on this delegate. For this purpose, the AppDelegate class conforms to the
UIApplicationDelegate protocol. The following are the methods defined by the
protocol to initialize the app and configure the Scenes.
application(UIApplication, didFinishLaunchingWithOptions:
Dictionary)—This is the first method called by the UIApplication object.
It is called to let us know that all the necessary objects were already
instantiated, and the app is ready to work.
application(UIApplication, configurationForConnecting:
UISceneSession, options: UIScene.ConnectionOptions)—This
method is called when a new Scene (window) is requested by the
system or the user. The method must return a UISceneConfiguration
object with the Scene’s configuration.
application(UIApplication, didDiscardSceneSessions: Set)—
This method is called when the user discards a Scene (closes a
window). The didDiscardSceneSessions argument is a set with
references to the UISceneSession objects representing the Scenes'
sessions.
As soon as the app is launched, the UIApplication object calls the The other two methods defined in the UIApplicationDelegate protocol are
application(UIApplication, didFinishLaunchingWithOptions:) method. In this method, called when a Scene is created or discarded. When the system or the user
we can write all the code we need to prepare our application, such as generates a new window, the application(UIApplication, configurationForConnecting:,
declaring initial values, opening a database, checking the current settings, options:) method is called in the delegate. From this method, we must return
and more, as shown next. the Scene's configuration, which is defined with an object of the
UISceneConfiguration class. The class includes the following initializer.
return true
This initializer provides the name of the configuration and the role of the
}
func application(_ application: UIApplication, configurationForConnecting Scene, but we also must declare the name of the class that is going to be
connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> assigned as the Scene's delegate. For this purpose, the UISceneConfiguration
UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: class includes the following property.
connectingSceneSession.role)
}
}
delegateClass—This property sets or returns the class that is going
 to be used to create the Scene's delegate (the object the system is
going to use to report the state of the Scene).
This example adds a property called basicSalary to the AppDelegate class. When
the application(UIApplication, didFinishLaunchingWithOptions:) method is called, we There are two ways to define the delegate class. We can use the initializer
initialize this property with the value 30000.0. This is a simple but practical to declare the name of the configuration and the role, and then assign the
example that illustrates how we can add properties to the application name of the class used to create the Scene's delegate to the delegateClass
delegate and initialize them when the app is launched. After the property property, or we can define this value in a file called Info.plist. The App
is implemented in the delegate, we can access it from any part of our code template implements this last approach. It defines the name of the
through the delegate property of the UIApplication object, as we will see later. configuration and the delegate class in the Info.plist file, as shown below,
and then calls the initializer with that name.
Scene Delegate
Figure 5-17: Scene definition in the info.plist file

After a Scene is requested by the system or the user, the scene(UIScene, willConnectTo: UISceneSession, options:
application(UIApplication, configurationForConnecting: UISceneSession, options:
UIScene.ConnectionOptions)—This method is called when a new
UIScene.ConnectionOptions) method is called and a UISceneConfiguration object is
Scene is created.
created using the configuration defined in the Info.plist file with the name
"Default Configuration" and the role received by the method sceneDidDisconnect(UIScene)—This method is called when a
(connectingSceneSession.role). Scene was removed.
sceneDidBecomeActive(UIScene)—This method is called when a
IMPORTANT: The configuration defined by the App template is Scene becomes active and therefore it is responding to user events.
enough for most applications, so all we need to do is to leave the
sceneWillResignActive(UIScene)—This method is called when a
method as it is declared by the template and the Scenes will be
properly configured. We will learn more about the info.plist file later, Scene is about to be moved to the background and stop responding to
and how to work with multiple Scenes in Chapter 22. user events.
sceneWillEnterForeground(UIScene)—This method is called
when a Scene is about to become visible and respond to user events.
sceneDidEnterBackground(UIScene)—This method is called
when a Scene is no longer visible and does not respond to user events
anymore.
The files that store the information for the Storyboard have the storyboard 19, number 2) and a small bar at the top with options to configure the
extension. As we already mentioned, the App template includes the scene and the view controller (Figure 5-19, number 1). Clicking on the main
Main.storyboard file with a Storyboard for our app. Figure 5-19 shows area selects the main view and clicking on the bar selects the scene. The
what we see on the Editor Area when we click on that file (the shape and view controller may also be selected from the buttons shown on the bar
size of the view vary depending on the selected device, as we will see when we click on it (see Figure 5-20, below). The button on the left
later). represents the controller object responsible for the view, the button in the
middle provides options to configure the element designated as first
Figure 5-19: Scene in the Storyboard responder (introduced later), and the button on the right allows us to
connect scenes together (studied in Chapter 9).
The initial shape of the scenes resembles an iPhone, but we can change it
anytime we want from the toolbar at the bottom of the Editor Area.
All the buttons on this bar help us configure the Storyboard and its
content, as described next.
View Controllers The subclass that controls the scene is specified by the first option (Figure
5-23, number 1). We can write the name of the class or select it from the

list. For instance, the App template includes the ViewController.swift file
with a class called ViewController that inherits from the UIViewController class
Each scene in the Storyboard is responsible for showing a view and its and is in charge of controlling the only scene in the Storyboard. If we select
content on the screen. To control the scenes and perform any standard this scene, we will see that the ViewController class was already assign to this
function they require, the UIKit framework defines the UIViewController class. scene.
This is a basic class with properties and methods to manage a single scene
and its views. Although the class provides all we need to control a scene, Figure 5-24: ViewController class assigned to the initial scene
we do not work directly with objects created from it. Instead, we define a
subclass of UIViewController for each scene in the Storyboard and add our
own properties and methods to perform the tasks that are specific to the
scene and our app.
As we will see in further chapters, applications with only one scene are
rare. Usually, more scenes are added to the Storyboard to represent all the 
screens our users can navigate through to find the information they need.
To know which object controls each scene, the scenes are associated to a The ViewController class provided by the App template is almost empty,
view controller from the Identity Inspector panel. This is one of the except for the viewDidLoad() method. This is a method defined in the
configuration panels available in the Utilities Area when the scene is UIViewController class that is executed when the scene’s main view and its
selected. To open the panel, we must select the scene and click on the content are created and loaded into memory. We must override this
third button at the top of the Utilities Area (circled in Figure 5-23).
method in our subclass if we want to program our own response. Along
with this method, the class includes a few more to report other states.
Figure 5-23: Identity Inspector panel
Listing 5-8 and print its value on the console as soon as the scene’s main of the UIResponder class and the delegate property returns a value of that
view is loaded. type. Finally, we use Optional Binding to read the basicSalary property in the
delegate and print its value on the console.
Listing 5-10: Adding our own code to the ViewController.swift file The process carried out by this application is as follows. When the app is
 executed, the system creates all the basic objects, including the UIApplication
import UIKit object and its delegate, and then calls the application(UIApplication,
class ViewController: UIViewController { didFinishLaunchingWithOptions:) method of the AppDelegate object to let us know
override func viewDidLoad() { that the app is ready. In this method, we initialize the basicSalary property
super.viewDidLoad()
(see Listing 5-8). When the execution of this method is over, the system
let app = UIApplication.shared looks in the Storyboard for the scene that has to show first and calls the
let mydelegate = app.delegate as! AppDelegate
viewDidLoad() method of its view controller. In this method, we get the value
if let salary = mydelegate.basicSalary { of the basicSalary property defined in the delegate object and print it on the
print("Basic Salary is \(salary)") // "Basic Salary is 30000.0"
} console.
}
}
 Do It Yourself: The last example assumes that you have already
declared and initialized the basicSalary property in the app’s delegate,
The first thing we do inside the viewDidLoad() method of Listing 5-10 is to call as shown in Listing 5-8. Replace the code in your
the same method on the superclass (see super in Chapter 3). When a ViewController.swift file by the code in Listing 5-9. Select an iPhone
method is overridden, the original method is not executed anymore, but simulator from the Scheme and press the Play button to execute the
the original methods of some classes, like the UIViewController class, perform app (Figure 5-13). You should see the message “Basic Salary is
30000.0” printed on the console (Figure 5-12, Debug and Console import UIKit
Listing 5-11: Modifying the main view from the view controller

5.4 Views Library
 
Templates include everything we need to start building our application. We The Library is a popup window that opens when we press the Library
get a Storyboard with the initial scene, a view controller to manage the button on the right side of the toolbar (Figure 5-12, number 5). This
scene's main view and its content, and all the necessary configuration files. window presents a list with all the elements we can add to the Storyboard
and the interface, including views, controls, and scenes.
After the template is created, we are ready to work on our application, and
the first step is to add views to the scene to define the user interface. For
Figure 5-25: Library
this purpose, Xcode offers a tool called Library.
The elements in the Library are incorporated into the view by dragging and
dropping, as illustrated in Figure 5-26, below. If we want to drag more than
one element, we can press and hold down the Option key to keep the
window open.
but for testing purposes, we can expand the view by dragging the square
indicators around the label or define a specific size in points from the Size
Inspector panel (see Figure 5-22, right).
A label is a view that can display text on the screen. The view is the
rectangular area that determines the space occupied by the label, and the
text is the view's content. Therefore, the size of the text is independent of
the size of the view. If the view is not big enough to fit the text inside, the
text will be truncated, as in "My Ti...". In Chapter 6, we will learn how to
adapt the views in the interface to the space available and their content,
Guide Lines Outlets
 
While the element is being dragged over a view, Xcode shows lines to help As we already mentioned, the elements generated in the Storyboard,
us find the right place to drop it. There are lines that determine the including views, subviews, and their properties, are stored as XML code in
horizontal and vertical center of the view, the superior, inferior, and lateral
the storyboard file. Right after the app is launched, the objects for the
margins, and the position of one element relative to another. Figure 5-28
shows what we see when we drag a new label to the corner of the main main views and their content are created from this code. The UIView
view. objects that represent the main views are connected to their view
controllers through the view property (see Listing 5-11), but the objects that
Figure 5-28: Guide lines represent the subviews inside these main views (labels, buttons, etc.) are
not connected to our code. They did not exist when we were programming
our app (they were just XML code in the storyboard file), and therefore we
cannot reference these objects from our code. To solve this problem,
Xcode introduces the concepts of Outlets.
An Outlet is a property referencing an object in the user interface. These
are normal properties but prefixed by the @IBOutlet attribute to indicate to
Xcode that they are related to an element in the Storyboard. When the app
is launched, the objects are created from the Storyboard and then assigned

to their Outlets in our code. It is through these Outlets that we can access
the objects from code and read and set the values of their properties.
These lines become visible again when we drag the elements inside
the view to find the right location and when we expand the area of the There are different ways to create an Outlet and generate the connection.
view by dragging the little squares around it. The simplest is to drag a line from the element in the scene to our view
controller and let Xcode generate the codes on both ends. For this, we
need to divide the Editor Area in two parts by selecting the Assistant
option from the menu opened by the button at the editor's top-right
corner, as illustrated below.
 
Once the Assistant option is selected (Figure 5-29, number 1), the Editor Outlets, as well as properties, should always be created at the beginning of
Area is split in two sections, with the main file on the left and an additional the class. When we reach this place and release the mouse button, Xcode
file opened on the right. If the element selected on the left is a scene in the opens a popup window to configure the connection.
Storyboard, the right area automatically shows its view controller. Figure 5-
30, below, shows what we see when we open the Assistant Editor and Figure 5-32: Setting up a connection
select the scene with the label from Figure 5-26.
The window asks for the type of connection (Outlet or Outlet Collection)
and additional information to generate the code. To create an Outlet, we

must select the option Outlet in the Connection field, insert the name we
want to assign to the property, its data type (for labels is UILabel), and the
The connection between the elements in the view on the left and our code
on the right is established by dragging a line from the element to the class value that determines how the memory is going to be managed. The latter
while pressing and holding down the Control key on the keyboard. Xcode is usually specified as weak, because its container (the UIView object that
generates a blue line on the screen to show the connection that is being represents the main view) already has a strong reference to the object (see
created and to indicate where the property is going to be placed in our Memory Management in Chapter 3).
code. In the example depicted in Figures 5-31 and 5-32, a label with the text My
Title was added to the main view and then connected to the view
Figure 5-31: Connection between a label and its view controller controller with an Outlet called mytitle. When we click the Connect button
in the popup window, Xcode generates the code for the Outlet, as shown When an Outlet is created, Xcode generates code in our view controller
next. and in the storyboard file. Something to keep in mind is that deleting the
code for an Outlet in the view controller is not enough to break the
Listing 5-12: Adding an Outlet for a label to the view controller connection. This only deletes the connection in our code, but it does
 nothing to the XML code in the storyboard file. To also erase the
import UIKit connection in the Storyboard, we must click the mouse's right button
(Secondary click) when the pointer is over the element to open a popup
class ViewController: UIViewController {
@IBOutlet weak var mytitle: UILabel!
window where we can edit the element's configuration, as shown below.
override func viewDidLoad() { Figure 5-33: Popup window to manage connections in the Storyboard
super.viewDidLoad()
}
}
Document Outline Panel represents the area of the main view that is not superposed by toolbars or
navigation bars. And finally, is the item that represents our label. By

selecting any of these items in the Document Outline panel we are
selecting the element they represent. For instance, if we select the item
The Document Outline panel is an additional panel that opens on the left
that represents the label, the label is selected on the scene.
side of the Editor Area when we click on the button at the editor’s bottom-
The items take the name of the element they represent, and in the cases of
left corner (see Figure 5-21, number 1). It offers a quick access to all the
labels and buttons, they are named after the text assigned to the elements.
elements in the Storyboard and allows us to select them, connect them to
For example, the item that represents our label is called My Title, because
Outlets and Actions in our code, change their names to make them easy to
that’s the text assigned to the label. But we can change the name by
find, move them up or down in the hierarchy, delete them, and create
selecting the item and pressing the Return key on the keyboard.
constraints between them (we will study Actions later and constraints in
Chapter 6).
Figure 5-35: Changing the item’s name
The items in the Document Outline panel represent the elements in the
Storyboard and therefore edit their properties, move them up or down the
 hierarchy, change their positions and sizes, and connect them with our
code. For instance, we can create an Outlet for our label by control-
The items are displayed in a hierarchical list. There are items at the top dragging a line from the item in the panel to the view controller.
of the hierarchy to represent each scene in the Storyboard (Figure 5-34,
number 1), and then items inside to represent the components of each Figure 5-36: Creating Outlets
scene. The items at the top are called View Controller, First Responder, and
Exit (the three buttons in the scene’s top bar). As we will see later, the First
Responder represents the element that first responds to user interaction
and the Exit control is used for navigation, but the View Controller
represents the scene’s view controller and therefore it contains the
elements of the interface. The first element on the list is the main view 
(View). Inside this item we find an item with the title Safe Area that
This is the same process introduced in Figure 5-32, but instead of dragging Labels
the line from the element in the scene, we do it form the item in the panel.

This is useful when elements on the interface overlap each other and are
hard to reach.
The label we have added to the scene in the previous section is probably
the most common element we can add to the user interface. Labels are
views that draw one or multiple lines of text on the screen. They can be
used to present information or to identify other elements on the interface.
Labels are created from the UILabel class, a subclass of UIView that adds the
additional functionality necessary to draw the text. The UILabel class
doesn't define its own initializer but we can create objects of this class
from code using the initializer inherited from the UIView class.
As seen in the previous section, labels can be added to the interface from
the Library. All we need to do is to drag and drop the option and the label
is included in the scene (see Figure 5-26).
After the label is created, we must assign a text to it and configure the
view. The following are some of the properties included by the UILabel class
for this purpose.
attributedText—This property sets or returns the formatted text shadowColor—This property sets or returns the color of the
displayed by the label. shadow. It is an optional property of type UIColor. When its value is nil,
font—This property sets or returns the font used to display the label. the text is drawn with no shadow.
Its value is an object of the UIFont class (we will introduce this class shadowOffset—This property sets or returns the offset of the
next). shadow. Its value is a CGSize structure that establishes the horizontal
textColor—This property sets or returns the color of the text. It is of and vertical displacement.
type UIColor.
When the label is added to the scene from the Library, we can modify the
textAlignment—This property sets or returns the alignment of the values of most of these properties from the Attributes Inspector panel.
text. It is an enumeration of type NSTextAlignment with the values left (to
the left side of the view), center (to the center of the view), right (to the
Figure 5-38: Editing the properties of a label from the Utilities Area
right side of the view), justified (the last line of the paragraph is
aligned), and natural (uses the alignment associated with the text).
lineBreakMode—This property sets or returns the mode used to
display lines of text that go beyond the view's boundaries. It is an
enumeration of type NSLineBreakMode with the values byWordWrapping
(the word that does not fit is moved to the next line), byCharWrapping
(the character that does not fit is moved to the next line), byClipping
(characters that does not fit are not drawn), byTruncatingHead (the
beginning of the text is replaced by ellipsis), byTruncatingTail (the end of
the text is replaced by ellipsis), byTruncatingMiddle (the middle of the
text is replaced by ellipsis). 
numberOfLines—This property sets or returns the number of lines
allowed for the text. If the text requires more lines than those set by This is the same we have done for the main view before (see Figure 5-22),
this property, it is truncated according to the mode selected by the but of course the options available are specific for labels. For example, we
can modify the text, color, and font of the label, and some aspects of the
lineBreakMode property. The value 0 declares unlimited lines.
label's view as well, such as the background color.
adjustFontSizeToFitWidth—This property sets or returns a
Boolean value that determines whether the font size needs to be Do It Yourself: Select the label on the scene. Insert a different text
decreased for the text to fit inside the view. It only works when the for the label and select a new color from the Color option. Click on
numberOfLines property is set to a value different from 0.
the T button in the Font option to see the values available (we will
learn more about these options next).
class ViewController: UIViewController { The size of the text is determined by the font set by the system. If we want
@IBOutlet weak var mytitle: UILabel!
to set a different size or font type, we must assign a new UIFont object to
override func viewDidLoad() { the font property of the UILabel object. UIFont objects are not only assigned
super.viewDidLoad() to labels but also to other elements that display text on the screen. They
mytitle.text = "Hello World"
} contain properties and methods to store and manage fonts. The following
} are the most frequently used initializer and type methods included in the

UIFont class to create these objects.
labelFontSize—This type property returns a CGFloat value with the application implements Dynamic Types, it can respond to these changes
size of the font used by the system to create UILabel objects. and show the text on the screen with the size preferred by the user.
To assign a new font to a label, we must create the UIFont object with the
buttonFontSize—This type property returns a CGFloat value with the
styles we want and then assign it to the label’s font property, as shown next.
size of the font used by the system to create UIButton objects
(buttons).
Listing 5-14: Creating dynamic font types
systemFontSize—This type property returns a CGFloat value with the 
size of the font used by the system. import UIKit
by the forTextStyle argument and a size specified by the user in override func viewDidLoad() {
Settings. The argument is a property of a structure called TextStyle super.viewDidLoad()
let myfont = UIFont.preferredFont(forTextStyle: .headline)
included in the UIFont class. The properties available are body, callout, mytitle.text = "Hello World"
caption1, caption2, footnote, headline, subheadline, largeTitle, title1, title2, title3. mytitle.font = myfont
}
systemFont(ofSize: CGFloat)—This type method returns a UIFont }

object with the font set by default on the system and a size
determined by the ofSize argument. The code in Listing 5-14 creates a font with the headline style. This is the
boldSystemFont(ofSize: CGFloat)—This type method returns a style used for titles and headers, but we can specify any style we want
UIFontobject with the font of type bold set by default on the system depending on the purpose of the text. For example, for long texts and
and a size determined by the ofSize argument. content the preferred style is body. Every style uses different font types and
they may look different, but the size is always determined by the system or
italicSystemFont(ofSize: CGFloat)—This type method returns a
the user from the Settings app.
UIFontobject with the font of type italic set by default on the system
and a size determined by the ofSize argument. Figure 5-40: Label with headline style
Although we can create a font object with the initializer or any of the
methods above, Apple recommends working with the preferredFont()
method. The fonts returned by this method are called Dynamic Types
because they are selected by the system according to the style specified by
the argument and the size set by the user. The Settings app offers an option
to change the font size for all the applications installed on the device. If our

The system also provides fonts that were carefully selected to optimize the
Listing 5-15: Defining a specific font and size
design and produce a pleasant experience for the user. The UIFont class

import UIKit
includes several type methods to get an object with these settings, as
shown next.
class ViewController: UIViewController {
@IBOutlet weak var mytitle: UILabel!
Listing 5-16: Using the system’s standard font
override func viewDidLoad() { 
super.viewDidLoad()
import UIKit
let myfont = UIFont(name: "Georgia-Italic", size: 30)
mytitle.text = "Hello World"
mytitle.font = myfont class ViewController: UIViewController {
@IBOutlet weak var mytitle: UILabel!
}
}
the first argument. The attributes argument is a container for all the 
attributes we want to apply to the text. import UIKit
strokeWidth—This property defines the text's stroke width Figure 5-45: Label with attributes
attribute. It is a value of type CGFloat.
shadow—This property defines the text's shadow attribute. It is a
value of type NSShadow.
Listing 5-22: Assigning additional attributes to the label Do It Yourself: Select the label and go to the Attributes Inspector
 panel (Figure 5-38). Change the Text option from Plain to Attributed,
import UIKit select the word "World" and change the font size to 36. You should
class ViewController: UIViewController { see something like Figure 5-47, left. Update the ViewController class
@IBOutlet weak var mytitle: UILabel! with the code in Listing 5-22 and run the application. The label's
override func viewDidLoad() { color should now be blue, but the size of the text should remain the
super.viewDidLoad() same, as illustrated in Figure 5-47, right.
if let oldtext = mytitle.attributedText {
if let attrText = try? AttributedString(oldtext, including: \.uiKit) {
var mytext = attrText As we do with String values, we can work directly with string indexes to
mytext.foregroundColor = .systemBlue
mytitle.attributedText = NSAttributedString(mytext) modify any character we want. For this purpose, the AttributedString
} structure includes two properties, characters and runs. These properties
}
}
return structures with sets of values that reference the characters in the
} string and the runs, which contains the attributes and the text to which
 they are applied. The characters property returns a structure of type
CharacterView. The following are some of the properties and methods
The AttributedString initializer takes an NSAttributedString object and the scope
provided by this structure to manage the indexes.
that defines the attribute keys we want to use to convert the text's
attributes. In this case, we are using UIKit types, so we specify the keypath
indices—This property returns a collection with the characters'
to the uiKit property of the AttributeScopes enumeration. This tells the
indexes.
initializer to use the attribute keys defined in the UIKitAttributes structure.
After the AttributedString structure is ready, we assign it to a variable to be count—This property returns an Int value with the number of
able to modify its attributes, change the foreground color to systemBlue, turn characters in the string.
the value into an NSAttributedString object, and assign it back to the label. As startIndex—This property returns the index of the first character. It
a result, all the previous attributes are preserved, but the text is now is a value of type Index.
shown in blue.
endIndex—This property returns the index of the last character. It is
Figure 5-47: Label with old and new attributes a value of type Index.
first—This property returns the string's first character. It is a value of
type Character.
last—This property returns the string's last character. It is a value of
 type Character.
firstIndex(of: Character)—This method returns an Index value with 
the index of the first character that matches the character specified
In this example, we get the index of the string's first character from the
by the of argument.
startIndex property, then calculate the index of the string's fifth character
index(Index, offsetBy: Int)—This method increments the index with the index(offsetBy:) method, and finally assign the attributes to the
specified by the first argument the amount of units specified by the characters in the range determined by these two indexes. In this case, this
offsetBy argument and returns a new Index value with the result. corresponds to the word "Hello".
index(after: Index)—This method increments the index specified by
the after argument one unit and returns a new Index value with the Figure 5-48: Attributes assigned to a custom range of characters
result.
index(before: Index)—This method decrements the index specified
by the before argument one unit and returns a new Index value with
the result.
override func viewDidLoad() { shadowColor—This property defines the color of the shadow. It is a
super.viewDidLoad()
var mytext = AttributedString("Hello World!")
value of type UIColor.
Effects like shadows are noticeable when they are applied to big fonts. In If our interface contains multiple labels that require the same attributes,
the following example, we assign the dynamic type largeTitle to the text's instead of applying the attributes one by one to each label, we can create a
font and then apply a gray shadow with a horizontal and vertical offset of 2 container with the AttributeContainer structure and apply all the attributes at
and a blur radius of 5. once. The following are some of the methods included in the AttributedString
structure to work with an attributes container.
Listing 5-24: Applying a shadow to the text
 setAttributes(AttributeContainer)—This method assigns the
import UIKit attributes in a container to the text. All the previous attributes are
class ViewController: UIViewController { removed.
@IBOutlet weak var mytitle: UILabel!
mergeAttributes(AttributeContainer)—This method assigns the
override func viewDidLoad() { attributes in a container to the text. The new attributes are merged
super.viewDidLoad()
var mytext = AttributedString("Hello World!")
with the previous attributes.
mytext.font = UIFont.preferredFont(forTextStyle: .largeTitle)
replaceAttributes(AttributeContainer, with:
let shadow = NSShadow() AttributeContainer)—This method replaces the attributes specified
shadow.shadowColor = UIColor.systemGray
by the container in the first argument with the attributes in the
shadow.shadowOffset = CGSize(width: 2, height: 2)
shadow.shadowBlurRadius = 5 container specified by the with argument.
mytext.shadow = shadow
mytitle.attributedText = NSAttributedString(mytext)
} Instead of assigning the attributes to the text, they are first defined in the
} container and later applied to the text with one of the methods listed

above, as in the following example.
Figure 5-49: Label with a shadow
Listing 5-25: Applying multiple attributes at once

import UIKit
The properties of a container are the same we used before to assign the By iterating over the runs property, we can read the attributes currently
attributes directly to the text. The AttributeContainer structure is created first, assigned to the text and modify the sections one by one.
and then all the attributes are assigned to it as always. In this example, we
change the font style to body and the background color to gray. Listing 5-26: Modifying the attributes of a run

Figure 5-50: Attributes applied to a label from a container import UIKit
range and assign a new background color to that section of the string. As a Actions
result, the background of the word "World" is initially red, but it is changed

to gray before assigning the text to the label.
Some elements of the interface are not limited to show text or graphics on
IMPORTANT: The AttributedString structure also includes initializers
the screen; they can also process user interaction. Buttons, Switches,
to assign attributes to the text with a markup language. These are
Segmented Controls, and others are designed to respond to the user and
special characters that can be inserted directly into the string to
fire an event to report the situation to the application. For instance, a
define the attributes. For more information, visit our website and
button performs an action when pressed, such as saving values in a
follow the links for this chapter.
database, or replacing the current scene by another one. To perform these
actions, we need to connect the button in the interface with a method in
our code. For this purpose, Xcode implements Actions.
Adding an Action to an element is like adding an Outlet. We must drag a
line from the control to the view controller class while pressing and holding
down the Control key, and then provide the information for the
connection. For instance, in the following example, we have changed the
label's text to "Pressed: 0" and added a button below with the title "Press
Here". Figure 5-51 shows what we see when we try to add an Action to the
button.
The first thing we need to do in the popup window is to select the type of
the connection as Action. In the options, we must insert the name of the
method that will be executed when the action is performed, the type of
the object that performed the action, and the type of event that will
trigger the action (Buttons respond to the Touch Up Inside event, but there
are others, as we will see later). The last option, Arguments, determines is incremented by 1, and the text in the mytitle label is updated with the
the values we want to send to the method. The option includes three result.
possible values: None (no value is sent to the method), Sender (a reference
to the object that represents the control is sent to the method), and Figure 5-52: Button performing an action
Sender and Event (a reference to the object that represents the control and
information about the event that triggered the action are sent to the
method). By default, this value is set to Sender, which will add to the
method a parameter called sender to receive a reference to the object 
(buttons are created by objects of type UIButton, as we will see later).
After all the information is declared, Xcode generates the codes in both Do It Yourself: Change the label's title in the Storyboard to
ends (the Storyboard and the view controller). The declaration of the "Pressed: 0". Open the Library and drag the option called Button to
method in our view controller is like any other, but it is prefixed by the the scene. You should see something like the interface in Figure 5-
@IBAction attribute to establish its relationship with the element in the 51. Create an Action for the button with the name showCounter, the
Storyboard. Type UIButton, the Event Touch Up Inside, and the rest of the values
by default. Update the ViewController class with the code in Listing 5-
Listing 5-27: Defining an Action for the button 27. Run the application and press the button. You should see the
 value on the label change, as illustrated in Figure 5-52.
import UIKit
Along with the Outlet for the label, the view controller in Listing 5-27
includes a property called counter to count the number of times the user
pressed the button. When the button is pressed, the UIButton object that
represents the button added to the scene in the Storyboard calls the
showCounter() method in our view controller, the value of the counter property
Buttons

gray background.
Buttons can also include an image on the sides. The Configuration structure
tinted()—This type method creates a configuration for a button with
includes the following properties to set up this image.
a tinted background color.
image—This property sets or returns the buttons image. It is a
Of course, these buttons can be added to the scene in the Storyboard from
UIImage object.
the Library.
imagePadding—This property sets or returns a CGFloat value that
Figure 5-53: Button options in the Library determines the distance between the image and the text.
imagePlacement—This property sets or returns a value that
determines the position of the image in relation to the text. It is an IMPORTANT: Some of these properties work with leading and trailing
NSDirectionalRectEdge structure with the properties all, bottom, leading, top, values. Instead of using names like left and right, UIKit implements
and trailing. these values because the side they reference depends on the human
language set on the device. In English, for example, leading
showsActivityIndicator—This property sets or returns a Boolean
represents the left side of the element and trailing the right, but this
value that determines if the button will include an activity indicator (a
may change for other languages. We will learn more about leading
wheel that spins to indicate that an operation is in progress). and trailing in Chapter 6.
The Configuration structure also includes properties to configure the button. In UIKit, the most common way to incorporate a button into the user
interface is by dragging the option from the Library to the scene in the
baseBackgroundColor—This property sets or returns the button’s Storyboard, as we have done for the label and the button in previous
background color. It is a value of type UIColor. The color automatically examples. For instance, we can implement the previous interface, but now
adapts to the state of the button (normal, pressed, and disabled). with two buttons, one to increment the value on the label and another to
baseForegroundColor—This property sets or returns the color of decrement it.
the button’s content. It is a value of type UIColor. The color
Figure 5-54: Interface to test buttons
automatically adapts to the state of the button (normal, pressed, and
disabled).
cornerStyle—This property sets or returns a value that determines
the style of the corners. It is an enumeration of type CornerStyle with
the values dynamic, fixed, capsule, large, medium, and small.
buttonSize—This property sets or returns a value that determines
the size of the button. It is an enumeration of type Size with values
that define predefined sizes. The values available are large, medium, mini,
and small.
contentInsets—This property sets or returns a value that 
determines the padding around the button’s content. It is a structure
of type NSDirectionalEdgeInsets. The structure includes the following For this application to work, we need to define two Actions in the view
initializer to set the padding for each side: NSDirectionalEdgeInsets(top: controller, one for the Add button and another for the Remove button, as
CGFloat, leading: CGFloat, bottom: CGFloat, trailing: CGFloat). shown next.
Do It Yourself: Keep the label but remove the button from the
interface of the previous example. Add a Filled button and a Tilted
button below the label. Double-click the buttons to change their
titles to Add and Remove. Drag the squares around the elements to
expand their views and get an interface like the one in Figure 5-54. 
Remove the Action added to the view controller in the previous
example and create an Action called addToCounter() for the Add button The first option sets the type of button we want to create. It includes some
and another called removeFromCounter() for the Remove button. standard designs and provides compatibility with older systems. To
Complete the ViewController class with the code in Listing 5-28. Run customize our button, we need to set the type as System (Figure 5-56,
the application and press the buttons. You should see the value on number 1). Next, we can determine the style (Plain, Gray, Tinted, or Filled)
the label change. and the type of text we want to use for the title (Plain or Attributed). Next,
there are two fields to insert the button’s title and subtitle (Figure 5-56,
number 2). To include an image, there is an Image option below. The field
class ViewController: UIViewController {
can take custom images or images defined by the system. The images @IBOutlet weak var addButton: UIButton!
provided by the system are called SF Symbols. These symbols are identified @IBOutlet weak var mytitle: UILabel!
var counter: Int = 0
by a name and are scalable, which means that the image adapts to the size
of the text. For instance, the image added to the button in Figure 5-55 is @IBAction func addToCounter(_ sender: UIButton) {
called trash.fill. Once we select the image, it is added to the button in the counter += 1
mytitle.text = "Pressed: \(counter)"
position determined by the Placement option (we will learn more about SF
Symbols and how to include our own images into the project later in this var configuration = addButton.configuration
configuration?.title = "Adding \(counter)"
chapter). addButton.configuration = configuration
Of course, all this customization can be made from code. The UIButton class }
includes the following properties and methods to update the configuration @IBAction func removeFromCounter(_ sender: UIButton) {
counter -= 1
of a button. mytitle.text = "Pressed: \(counter)"
}
}
configuration—This property sets or returns a Configuration structure 
with the button’s configuration.
configurationUpdateHandler—This property sets or returns a In this example, we get the button's current configuration, modify the
value of the title property with a string that includes the current value of
closure used to update the button’s configuration. The closure
the counter property, and then assign the configuration back to the button.
receives a value with a reference to the button.
As a result, every time the button is pressed, the title is updated to display
setNeedsUpdateConfiguration()—This method asks the system to the state of the counter.
update the button’s configuration. Notice that when the Remove button is pressed, the Add button is not
updated. When changes in the configuration depend on actions unrelated
All we need to do to change the button's configuration is to get the current to the button, we can define the configuration from the
configuration from the configuration property, modify the properties we want configurationUpdateHandler property and then call the setNeedsUpdateConfiguration()
to change, and assign the configuration back to the button. If the button is method to force an update. The closure assigned to the
added to the scene in the Storyboard, as in this example, we need an configurationUpdateHandler property is executed every time the button is
Outlet to access it. The following example includes an Outlet connected to pressed and when the setNeedsUpdateConfiguration() method is called, as shown
the Add button of the interface in Figure 5-54 and change its title when the in the following example.
button is pressed.
Listing 5-30: Updating the button’s configuration
Listing 5-29: Modifying the button's configuration 
 import UIKit
import UIKit
class ViewController: UIViewController { The closure assigned to the configurationUpdateHandler property is executed
@IBOutlet weak var addButton: UIButton!
@IBOutlet weak var mytitle: UILabel! every time the Add button is pressed, so there is nothing we need to do
var counter: Int = 0 from the addToCounter() method to update the button, but to update the
Add button when the Remove button is pressed, we must call the
override func viewDidLoad() {
super.viewDidLoad() setNeedsUpdateConfiguration() method on the Add button, as we did in the
addButton.configurationUpdateHandler = { [unowned self] button in removeFromCounter() method of Listing 5-30. The result is shown below.
var current = button.configuration
current?.title = "Add Me"
current?.showsActivityIndicator = counter > 0 ? true : false Figure 5-57: New button configuration
current?.imagePlacement = .trailing
current?.imagePadding = 15
button.configuration = current
}
}
@IBAction func addToCounter(_ sender: UIButton) {
counter += 1
mytitle.text = "Pressed: \(counter)" 
}
@IBAction func removeFromCounter(_ sender: UIButton) {
counter -= 1 Do It Yourself: Connect the Add button to the view controller with
mytitle.text = "Pressed: \(counter)" an Outlet called addButton. Update the ViewController class with the
addButton.setNeedsUpdateConfiguration()
} code in Listing 5-30. Run the application and press the Add Me
} button. You should see the number on the label change and the

activity indicator appearing on the button’s right-hand side (see
The closure assigned to the configurationUpdateHandler property receives a Figure 5-57, right). Press the Remove button to reduce the value to 0
reference to the button that we can use to get the current configuration, or less. The indicator should be removed.
perform the changes, and assign the configuration back to the button, as
done before. In this example, we change the button’s title to “Add Me” and IMPORTANT: The closure in Listing 5-30 accesses the counter
add an activity indicator to the button depending on the value of the counter property defined in the ViewController class. To access this property,
property. If the value of counter is greater than 0, the indicator is added to the closure must reference the ViewController object with self. Using self
the button, otherwise it is removed. inside a closure can create a strong reference cycle. To avoid it, you
The Activity Indicator is an animated image that looks like a rotating can mark self with the unowned keyword between square brackets, as
wheel, so we can configure it the same way we do with images. In this we did in this example. If self is not required inside the closure, this
example, we assign the value trailing to the imagePlacement property, which declaration can be removed.
positions the indicator on the button’s right-hand side and assign a padding
of 15 points between the indicator and the title. Although most of the time the buttons will be added to the scenes in the
Storyboard, there are times when an application needs to define a button
and its action from code. The buttons are created from the UIButton buttonConfig.title = "Press Me"
buttonConfig.subtitle = "Do it now!"
initializer introduced before, and the actions are created from the UIAction buttonConfig.baseBackgroundColor = .systemBrown
class. When a button on a scene is connected to the view controller with
let mybutton = UIButton(configuration: buttonConfig, primaryAction: UIAction(handler: {
the @IBAction attribute, Xcode and the compiler take care of creating the [unowned self] action in
UIAction object for us, but when the button is defined from code, we must print("Value of counter is: \(counter)")
provide this object ourselves. For this purpose, the class includes the counter += 1
}))
following initializer. mybutton.frame = CGRect(x: 20, y: 50, width: 150, height: 50)
view.addSubview(mybutton)
}
UIAction(title: String, image: UIImage?, identifier: Identifier?, }
state: State, handler: Closure)—This initializer creates an action 
for a control. The title and image argument are the text and the image
The button in this example is configured with the title “Press Me”, the
we want to use to represent the control. The identifier argument is a
subtitle “Do It Now!”, and a brown background. After the Configuration
structure defined in the UIAction class that can be initialized with a
structure is ready, we create the UIButton object with this value and a
string to identify the action. The state argument is an enumeration
UIAction object with a closure that prints a message and increments the
that specifies the action's initial state. The possible values are on, off, value of the counter property when the button is pressed. Finally, the button
and mixed. And the handler argument is the closure or the function to is added to the main view with the addSubview() method and presented on
execute when the action is performed. The closure or function the screen (see Listing 5-7).
receives one value with a reference to the UIAction object.
Figure 5-58: Button added to the interface from code
Most of the arguments are optional. For instance, if we already have a
configuration for the button, we only need to provide the closure that
defines the action to perform when the button is pressed, as in the
following example.
IMPORTANT: In our example, before adding the button, we had to Figure 5-59: Button configured from an Action
specify the position and size by assigning a CGRect value to the frame
property (the UIButton class inherits this property from the UIView
class). This is necessary for the system to know where to position the
button, but it is not the recommended practice. We use it here for
didactic purposes, but the elements on the interface should be
positioned and sized with constraints or added as the content of
Stack Views, as we will see in Chapter 6.

If all we want is to create a simple button with a title and an image, we can
provide the button’s configuration from the UIAction object, as in the The UIButton class is not a subclass of UIView but an intermediate class called
following example. UIControl (see Figure 5-6). The UIControl class provides all the functionality
required by controls like buttons, switches, sliders, and more. By
Listing 5-32: Configuring a button with an Action implementing the properties and methods defined by this class, we have
 more options to manage the controls. The following are the most
import UIKit frequently used.
class ViewController: UIViewController {
var counter: Int = 0 isEnabled—This property sets or returns a Boolean value that
override func viewDidLoad() { indicates whether the element is enabled or not.
super.viewDidLoad()
let mybutton = UIButton(configuration: .filled(), primaryAction: UIAction(title: "Press isSelected—This property sets or returns a Boolean value that
Here", handler: { [unowned self] action in indicates whether the element is selected or not.
print("Value of counter is: \(counter)")
counter += 1 addAction(UIAction, for: UIControl.Event)—This method adds an
}))
mybutton.frame = CGRect(x: 20, y: 50, width: 150, height: 50) action to a control. The first argument defines the action, and the for
view.addSubview(mybutton) argument is a property of a structure called Event that specifies the
}
} event that triggers the action. The properties available are touchDown,
 touchDownRepeat, touchDragInside, touchDragOutside, touchDragEnter,
touchDragExit, touchUpInside, touchUpOutside, touchCancel, valueChanged,
In this example, we create the button with the configuration by default
menuActionTriggered, primaryActionTriggered, editingDidBegin, editingChanged,
returned by the filled() method and then define the button’s title and action
editingDidEnd, and editingDidEndOnExit.
from the UIAction object.
removeAction(UIAction, for: UIControl.Event)—This method console), and the event touchUpInside, which is fired after the button is
removes from the control the action specified by the first argument pressed and released, so the closure is going to be executed only when
and for the event specified by the for argument. these actions occur.
Because UIButton is a subclass of UIControl, all these properties and methods Do It Yourself: Remove any element in the scene and add a Filled
are available from the objects that represent the buttons. For instance, we button, as in the interface of Figure 5-54. Connect the button to the
can call the addAction() method on a button in a scene in the Storyboard to ViewController class with an Outlet called myButton. Update the class
define the button’s action from code. with the code in Listing 5-33 and run the application. Press the
The following example assumes that we have included a button to the button. You should see a message printed on the console.
scene from the Library and connected that button to the view controller
with an Outlet called myButton. Using this Outlet, we can add an action to IMPORTANT: The touchUpInside event is the most common for
the button, as shown next. buttons, but other controls usually implement the valueChanged event
that is fired every time the value managed by the control changes, as
Listing 5-33: Adding an action to a button from code we will see later.

import UIKit The UIControl class also includes the isEnabled property to enable or disable
class ViewController: UIViewController { the button. By default, all buttons are enabled, but we can assign the value
@IBOutlet weak var myButton: UIButton! false to this property to disable them.
var counter: Int = 0
property can be used to turn a standard button into a toggle button (a override func viewDidLoad() {
button that represents two states, on or off). The UIButton class offers the super.viewDidLoad()
myButton.changesSelectionAsPrimaryAction = true
following property for this purpose. myButton.isSelected = false
}
@IBAction func turnOption(_ sender: UIButton) {
changesSelectionAsPrimaryAction—This property sets or returns if sender.isSelected {
a Boolean value that indicates whether the button is going to track a myLabel.text = "Is On"
} else {
selection (true) or perform the action (false). myLabel.text = "Is Off"
}
}
If we assign the value true to this property, the button toggles the value of }
its isSelected property every time it is pressed, but it doesn't perform any 
other action (the action assigned to the button is ignored), so we can use it
to allow the user to select between two states. The following example We can use any style we want to create a toggle button, but if we use a
defines a toggle button and displays the current state on the screen. Plain button, as we did in the interface of Figure 5-60, it will reflect the
current state.
Figure 5-60: Interface to test toggle buttons
Figure 5-61: Toggle button
The menu is created from a UIMenu object, and the options are created Figure 5-62: Contextual menu
from UIAction objects. The UIAction objects require a title or an image, and
the closure or the function that is going to be executed when the option is
selected. In the following example, all the options execute the same
function, but we can assign different functionality to each option if
necessary.
We can also select an option from code. All we need to do is to get the
UIAction object that represents the option from the children property and
modify the action's state property. For example, the following method gets
the object that represents the second option (the option at index 1), casts
the object to a UIAction object, and assigns the value on to the action's state
 property to select that option. Now, every time the button is pressed, the
second option in the Pop Up button is selected.
The UIMenu class includes properties to manage the menu and the options.
The most useful are the children property to access the options and the Listing 5-40: Selecting an option from code
selectedElements property to return an array with objects that represent the 
selected options. The following is an action for an additional button that @IBAction func processOptions(_ sender: UIButton) {
let action = myButton.menu?.children[1] as? UIAction
will print the title of the selected option when the button is pressed. action?.state = .on
}

Listing 5-39: Getting the selected option

@IBAction func processOptions(_ sender: UIButton) {
if let selected = myButton.menu?.selectedElements.first as? UIAction {
Outlet Collections indicator on the left of the @IBOutlet property to the next element on the
list, as shown below.

Figure 5-65: Connecting multiple elements to the same Outlet
An Outlet Collection is a type of Outlet that can reference one or more
elements. It is useful when several elements of the interface must be
modified at the same time. The property created for the Outlet is an array
containing references to each element. By iterating over the array, we can
reach every element connected to the Outlet and access their properties.
The following example presents an interface with three buttons. The Say
Hello and Say Goodbye buttons print messages on the console and the 
Disable button is used to disable the first two.
After the Outlet Collection is defined and the first two buttons are
Figure 5-64: Interface to test Outlet Collections connected, we must create the Actions. Listing 5-41, next, shows the view
controller with the connections and all the Actions required for this
example.
In the interface, the bar looks like a line with two colors, one to indicate
the progress and another to provide a reference to how much is left to
process.
import UIKit
The progress is set with values from 0.0 to 1.0. By default, the value is 0.5,
which sets the progress to 50%. This value can be changed from the
Attributes Inspector panel or in code, as shown next.
UIActivityIndicatorView(style: UIActivityIndicatorView.Style)
—This initializer creates a UIActivityIndicatorView object with the style set
by the argument. The argument is an enumeration called Style
included in the UIActivityIndicatorView class. The values available are large
(large indicator) and medium (standard indicator).
color—This property sets or returns the color of the indicator.

hidesWhenStopped—This property sets or returns a Boolean value
that determines if the indicator is going to be hidden when the The view controller must include Outlets for the indicator and the button,
animation stops. and an action for the button to activate and deactivate the indicator when
isAnimating—This property returns a Boolean value that determines pressed.
whether the indicator is animating or not.
Listing 5-43: Working with an Activity Indicator
startAnimating()—This method starts the animation. 
stopAnimating()—This method stops the animation. import UIKit
} Segmented Control
@IBAction func turnOnOff(_ sender: UIButton) {
if activity.isAnimating { 
activity.stopAnimating()
myButton.setTitle("Turn On", for: .normal)
} else { A Segmented Control creates a bar of interconnected buttons (if one
activity.startAnimating()
myButton.setTitle("Turn Off", for: .selected) button is pressed, the other buttons are deactivated). The control is
} defined by the UISegmentedControl class, which includes the following
}
}
initializer.

UISegmentedControl(frame: CGRect, actions: [UIAction])—
The code in Listing 5-43 configures the button as a toggle button with the This initializer creates a UISegmentedControl object that performs a
changesSelectionAsPrimaryAction property and then assigns a red color to the
specific action for each button. The frame argument determines the
indicator. When the app is launched, the indicator is not active, but if the position and size of the view, and the actions argument is an array of
button is pressed, the turnOnOff() method is called and the Activity Indicator
objects that define the buttons and the actions.
is activated. In this method, we check the current condition by reading the
isAnimating property, and then start or stop the indicator accordingly.
Each button of the segment is identified by an index starting from 0. The
properties and methods in the class use this index to access and modify
Do It Yourself: Delete previous elements in the scene. Drag an the buttons.
Activity Indicator and a Plain button from the Library to the scene
and connect them to the ViewController class with Outlets called numberOfSegments—This property returns the number of buttons
myButton and activity. Connect the button with an action called in the control.
turnOnOff(). Complete the view controller with the code in Listing 5-43
selectedSegmentIndex—This property sets or returns the selected
and run the application. Press the button. You should see the
button in the control.
indicator spinning.
setAction(UIAction, forSegmentAt: Int)—This method sets a new
action for a button. The first argument represents the action we want
to assign to the button, and the forSegmentAt argument determines
the index of the button to be modified.
insertSegment(action: UIAction, at: Int, animated: Bool)—This
method inserts a new button with the action and in the position
specified by the arguments. The action argument determines the
action for the button, the at argument specifies the index in which the
button will be inserted, and the animated argument determines
whether the process will be animated or not.
removeSegment(at: Int, animated: Bool)—This method removes
the button at the index specified by the at argument. The animated
argument indicates if the process will be animated.
setEnabled(Bool, forSegmentAt: Int)—This method sets the
condition of the button at the index specified by the forSegmentAt
argument. The first argument determines whether the button is 
enabled (true) or disabled (false).
The way a Segmented Control works is by keeping track of the selected
As with any other control, the Library offers an option to add a Segmented segment. An index value is assigned to each segment, from left to right,
Control to a scene in the Storyboard. starting from 0. When a new segment is selected, the value of its index is
assigned to the selectedSegmentIndex property of the UISegmentedControl object
Figure 5-71: Segmented Control in the Library that represents the element and the Value Changed event is fired. By
creating an Action that responds to this event, we can check the
selectedSegmentIndex property and execute the corresponding code according
 to its value.
By default, the control is added to the scene with two buttons, but we can Listing 5-44: Responding to the Value Changed event of a Segmented
define more from the Attribute Inspector panel and change their titles with Control

a double-click. For instance, the following interface includes a Segmented
Control with the two buttons by default but new titles, and a label we are import UIKit
going to modify according to the button selected by the user. class ViewController: UIViewController {
@IBOutlet weak var mytitle: UILabel!
Figure 5-72: Segmented Control with two buttons @IBAction func changeOption(_ sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0 {
mytitle.textColor = .label
} else if sender.selectedSegmentIndex == 1 {
mytitle.textColor = .systemRed
}
}
}

options.setEnabled(false, forSegmentAt: 0)
}
@IBAction func changeOption(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex { 
case 0:
mytitle.textColor = .label
case 1:
mytitle.textColor = .systemRed
case 2:
mytitle.textColor = .systemBlue
default:
mytitle.textColor = .label
}
}
}

The view controller in Listing 5-46 defines the actions for the segments as
we did before, but then inserts a new segment at the end of the list. To
know the index, we read the numberOfSegments property. This property
returns the number of segments in the control, so we can use it as index to
add another one at the end.
To respond to the selection, we replace the if else statement from previous
examples with a switch statement and three cases, one per button. Notice
that the first segment is disabled with the setEnabled() method, so only the
Red and Blue buttons are available.
Switch we can replace the Segmented Control from the previous interface with a
Switch to change the label’s color between two values.

Figure 5-75: Interface to test a Switch
As its name indicates, the Switch control is a switch on the screen that the
user can turn on and off. UIKit includes the UISwitch class to create these
controls. The class doesn’t implement any initializer, but we can use the
UIView initializer to create a Switch from code or drag the option from the
Library.
The Slider is usually applied to analog systems where a specific value is not
Sliders allow the user to select a value from a range of values. The control
required (e.g., volume control, brightness, etc.). For the following example,
is presented as a horizontal bar with a round indicator that points to the
we implement a Progress View along with a Slider with values by default.
selected value. It is defined by the UISlider class. We can create it from code
with the UIView initializer or add it to a scene from the option in the Library.
Figure 5-78: Progress View and Slider working together
UISlider(frame: CGRect)—This initializer creates a UISlider object
with the position and size specified by the argument.
 
By default, Sliders are created with a range of values between 0.0 and 1.0 Listing 5-49: Incrementing or decrementing a value with a Slider
and the indicator pointing at the value 0.5, but we can adapt the control to 
our needs from the Attributes Inspector panel or the properties and import UIKit
value—This property sets or returns the selected value. When the @IBAction func updateProgress(_ sender: UISlider) {
value is set, the indicator is moved to the corresponding position. It progress.progress = sender.value
}
takes a value of type Float. }

minimumValue—This property sets or returns the minimum value.
It is of type Float. When the user moves the pointer, the value in the value property changes,
maximumValue—This property sets or returns the maximum value. the Value Changed event is fired, and the Action assigned to the Slider is
It is of type Float. performed. In this example, we defined the action with a method called
updateProgress(). In this method, we take the current value of the Slider from
setValue(Float, animated: Bool)—This method sets the selected
value as the value specified by the first argument and moves the
the value property and assign it to the progress property of the UIProgressView @IBOutlet weak var slider: UISlider!
object to update the progress bar. override func viewDidLoad() {
super.viewDidLoad()
slider.minimumValue = 0.0
Do It Yourself: Delete the elements in the scene. Add a Progress slider.maximumValue = 10.0
View and a Slider from the Library (as illustrated in Figure 5-78). slider.value = 0.0
Create an Outlet for the progress bar called progress and an Action for slider.minimumTrackTintColor = UIColor.systemRed
the Slider called updateProgress(). Update the view controller with the slider.maximumTrackTintColor = UIColor.systemYellow
}
code in Listing 5-49. The progress bar should change according to the @IBAction func updateProgress(_ sender: UISlider) {
position of the Slider. let currentValue = sender.value / 10
progress.progress = currentValue
}
As we have seen above, we can specify different values for the Slider from }

the Attributes Inspector panel and from code, but the class also provides
properties to customize the Slider, change colors, and add images on the
In this example, we connect the Slider to the view controller with an Outlet
sides to represent minimum and maximum values.
called slider to modify its values and appearance. Now the Slider is
presented with different colors and runs from 0.0 to 10.0. The rest of the
minimumTrackTintColor—This property sets or returns the color
application works the same but notice that we couldn’t assign the value of
of the bar on the left side of the indicator. It is an optional of type
the Slider directly to the Progress View, as we did before. By default, the
UIColor.
Progress View and the Slider work with the same range of values (0.0 to
maximumTrackTintColor—This property sets or returns the color 1.0). If we change the range for the Slider, we must adapt the number
of the bar on the right side of the indicator. It is an optional of type returned by this element to the Progress View range to be able to display
UIColor. the correct level of progress. In our example, the value of the Slider goes
from 0 to 10, so we divide the value of the value property by 10 to get a
minimumValueImage—This property sets or returns the image
number between 0.0 and 1.0 that we can assign to the progress bar.
that represents the minimum value. It is an optional of type UIImage.
maximumValueImage—This property sets or returns the image
that represents the maximum value. It is an optional of type UIImage.
Stepper The class also includes properties to configure the control’s behavior and
appearance.

autorepeat—This property sets or returns a Boolean value that
A Stepper includes two buttons with the minus and plus symbols that users
indicates if the value is incremented automatically. If it is set to true,
can press to increment or decrement a value. It is defined by the UIStepper
class. We can create a Stepper from code with the UIView initializer or add it the value of the Stepper is incremented repeatedly while the user
to a scene from the option in the Library. holds the button down.
isContinuous—This property sets or returns a Boolean value that
UIStepper(frame: CGRect)—This initializer creates a UIStepper indicates if the updated values are reported during the user
object with the position and size specified by the argument. interaction or only when the user interaction ends.
wraps—This property sets or returns a Boolean value that
Figure 5-79: Stepper in the Library determines how the value is processed when it reaches the minimum
or maximum values allowed. If true, when the value goes beyond the
limit the opposite limit is assigned to it and the value keeps
 decrementing or incrementing in a loop. On the other hand, when the
property is false, the value stops decrementing or incrementing when a
A Stepper is set with values by default. The minimum is set to 0.0, the limit is reached.
maximum to 100.0, the initial value to 0.0, and the incremental value to tintColor—This property sets or returns the color of the control.
1.0, but we can adapt the control to our needs from the Attributes
Inspector panel or the class properties.
A Stepper works the same way as other controls we have already
introduced. When the user presses a button, the control fires a Value
value—This property sets or returns the current value of the Stepper. Changed event, so we can define an Action to respond to this event and
minimumValue—This property sets or returns the minimum value. process the new value or perform a task. For instance, we can show the
It is of type Double. current value with a label, as in the following example.
maximumValue—This property sets or returns the maximum value.
Figure 5-80: Interface to test the Stepper
It is of type Double.
stepValue—This property sets or returns the number by which the
current value of the Stepper will be incremented or decremented. It is
of type Double.
connected to the view controller with an Action called increment(), so every
time the user presses a button, the new value is assigned to the label and
displayed on the screen.
The view controller defines an Outlet for the Stepper and change its values
when the view is loaded. We set an initial value of 0, a minimum value of 0,
a maximum value of 10, and declare the step with a value of 1.0, so the
user can select a value from 0 to 10 in steps of 1. The Stepper was also
The code in Listing 5-52 does not introduce anything new, but thanks to
the Text Field we are now able to process input from the user. When the
user taps on the Save button, the changeTitle() method is executed and the spaces, tabulations, or characters that represent new lines. The following
label’s text is replaced with the text inserted by the user. The first thing we example implements the whitespaces property to remove the space
do is to confirm that the Text Field is not empty comparing its text property characters at the beginning and the end of the string inserted by the user.
with an empty string (titleInput.text != ""). When there is text to process, the Notice that this time we take advantage of the isEmpty property defined by
value of this property is assigned to the label’s text property (titleLabel.text = the String structure to check if the string is empty or not. This produces the
titleInput.text), effectively replacing the text on the screen with the text same result as before but can simplify our code in some circumstances.
inserted by the user. Finally, the Text Field is cleared to receive new input.
Listing 5-53: Trimming text
Do It Yourself: Delete the elements in the scene. Add a label with 
the word "Title", a Text Field with the placeholder "Insert title here", import UIKit
and a button called "Save" (Figure 5-82). Create Outlets for the label
class ViewController: UIViewController {
and the Text Field called titleLabel and titleInput. Create an Action for @IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var titleInput: UITextField!
the Save button called changeTitle(). Update the view controller with
the code in Listing 5-52. Run the application, insert a text, and press @IBAction func changeTitle(_ sender: UIButton) {
var text = titleInput.text!
the Save button. You should see the text replacing the title. text = text.trimmingCharacters(in: .whitespaces)
In the example of Listing 5-52, we compare the value inserted by the user if !text.isEmpty {
titleLabel.text = text
with an empty string. If the string is not empty, we process it. But titleInput.text = ""
sometimes users unintentionally add space characters at the beginning or }
}
the end of the string. The String structure includes a convenient method }
defined specifically to remove these unwanted characters. 
trimmingCharacters(in: CharacterSet)—This method erases the The UITextField class, as well as other classes that create visual controls on
characters indicated by the in argument at the beginning and end of the screen, is a subclass of the UIControl class and the UIControl class is a
the string and returns a string with the result. The argument is a subclass of the UIView class, therefore, UITextField objects have access to all
their properties and methods, such as the backgroundColor property to
CharacterSet structure with the set of characters we want to erase.
change the background color or the isEnabled property to enable or disable
the field. The following example modifies the isEnabled property to disable
The trimmingCharacters() method takes a structure of type CharacterSet to
the Text Field after the first input.
know what to remove. This structure creates sets of characters for
searching operations. The definition includes a few type properties to
Listing 5-54: Disabling the Text Field
create structures with standard sets. The most useful are whitespaces and 
whitespacesAndNewlines, which return sets that contain invisible characters, like
import UIKit content.
class ViewController: UIViewController { textFieldShouldEndEditing(UITextField)—This method is called
@IBOutlet weak var titleLabel: UILabel!
by the Text Field on the delegate object when the user tries to switch
@IBOutlet weak var titleInput: UITextField!
focus to another element. The method must return a Boolean value
@IBAction func changeTitle(_ sender: UIButton) { that indicates if edition should stop or not.
var text = titleInput.text!
text = text.trimmingCharacters(in: .whitespaces) textFieldDidEndEditing(UITextField)—This method is called by
if !text.isEmpty {
the Text Field on the delegate object after the element loses focus.
titleLabel.text = text
titleInput.text = "" textField(UITextField, shouldChangeCharactersIn: NSRange,
titleInput.placeholder = "Text Field Disabled"
titleInput.isEnabled = false replacementString: String)—This method is called by the Text Field
} on the delegate object every time the user inserts or deletes a
}
}
character or a string of characters. The shouldChangeCharactersIn
 argument determines the range of characters on the field affected by
the operation, and the replacementString argument is the new
The changeTitle() method in Listing 5-54 assigns the new text to the label as character or string of characters inserted by the user. The method
before, but now it also modifies the isEnabled property to disable the field must return a Boolean value that indicates whether the text should be
and shows a new placeholder to report the situation to the user. Once the replaced or not.
Text Field is disabled, the user cannot type anymore. textFieldShouldClear(UITextField)—This method is called by the
These properties provide some level of customization, but they are not Text Field on the delegate object to know if the Text Field should be
enough to control the user’s input. Sometimes we need to determine what cleared when the Clear button is pressed. The method must return a
the user is allowed to insert or how the Text Field should respond. For this Boolean value that indicates whether the Text Field should be cleared
purpose, the UITextField class includes the delegate property to designate a or not.
delegate to the Text Field. The UITextField object calls methods on this
delegate object to report the state of the process. The object assigned as
textFieldShouldReturn(UITextField)—This method is called by the
Text Field on the delegate object when the user taps the Return key
the Text Field’s delegate must conform to the UITextFieldDelegate protocol,
on the keyboard. The method must return a Boolean value that
which includes the following methods.
indicates if the action should be considered or not.
textFieldShouldBeginEditing(UITextField)—This method is called
Usually, the object assigned as the Text Field’s delegate is the same view
by the Text Field on the delegate object to know if edition should be
controller that controls the scene to which the Text Field belongs. In the
allowed. The method must return a Boolean value that indicates if
following example, the ViewController object is assigned to the delegate
edition is allowed or not.
property of the Text Field and then the textFieldShouldBeginEditing() method is
textFieldDidBeginEditing(UITextField)—This method is called by implemented. This method is another way to enable or disable a Text Field.
the Text Field on the delegate object when the user begins editing its
The UITextField object calls it as soon as the user taps on the Text Field. If object and report changes in the state of the Text Field by calling the
the value returned by the method is false, the user is not allowed to insert corresponding methods. For example, when the user taps on the Text Field
text. to start writing on it, the UITextField object calls the textFieldShouldBeginEditing()
method to know if the user is allowed to type on it (in our example, we
Listing 5-55: Assigning a delegate to the Text Field and declaring its return false, indicating that the user is not allowed).
methods The methods of the UITextFieldDelegate protocol are optional, which means
 we can implement only the ones we need. If the method does not exist
import UIKit inside the delegate, the Text Field uses the values by default. For example,
if we do not declare the textFieldShouldBeginEditing() method, as we did in
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var titleLabel: UILabel! Listing 5-55, the Text Field considers the value to be true and allows the
@IBOutlet weak var titleInput: UITextField! user to edit the field.
override func viewDidLoad() { The textFieldShouldBeginEditing() method is a very simple method, but we can
super.viewDidLoad() declare others to control more aspects of the interaction between the user
titleInput.delegate = self
and the Text Field. For example, if we want to control what the user is
}
@IBAction func changeTitle(_ sender: UIButton) { allowed to type in the field, we can implement the
titleLabel.text = titleInput.text textField(shouldChangeCharactersIn:, replacementString:) method. Every time the
}
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool { user types a character, the UITextField object calls this method to know
return false whether it should include the entry or not. Implementing this method, we
}
}
can process the input while it is entered.

Listing 5-56: Determining the number of characters allowed
The first thing we need to do in our view controller is to declare that the 
class conforms to the UITextFieldDelegate protocol, so the UITextField object import UIKit
knows that this object implements the protocol methods. This is done by
class ViewController: UIViewController, UITextFieldDelegate {
adding the name of the protocol after the name of the class, separated by @IBOutlet weak var titleLabel: UILabel!
a comma (see Protocols and Delegates in Chapter 3). Next, we must @IBOutlet weak var titleInput: UITextField!
declare the view controller object as the delegate of the Text Field, so the override func viewDidLoad() {
UITextField object knows what object implements the protocol methods. super.viewDidLoad()
This is part of the Text Field’s configuration, so it has to be done before titleInput.delegate = self
}
anything else, and that is why we assign the self keyword to the delegate @IBAction func changeTitle(_ sender: UIButton) {
property inside the viewDidLoad() method (self is a reference to the object titleLabel.text = titleInput.text
}
that the code belongs to, in this case, the ViewController object). Now the func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange,
UITextField object can use the delegate property to access our view controller replacementString string: String) -> Bool {
if let text = textField.text { if (Int(string) != nil && textField.text != "0") || string == "" {
let total = text.count + string.count - range.length return true
if total > 10 { }
return false return false
} }
} 
return true
}
} To determine if the user inserted a number we use the Int() initializer. If the
 value returned by Int() is not nil, it means that the string parameter contains
a number. But there are a few other things we must contemplate to create
The textField(shouldChangeCharactersIn:, replacementString:) method receives three this filter. The first is that, although the number 0 is a valid integer number,
values from the Text Field: a reference to the UITextField object, an NSRange an integer number of two digits or more cannot begin with 0. Therefore,
value that represents the range of characters in the Text Field that are we ought to validate the entry only if the current value in the Text Field is
going to be replaced by the new entry, and a String value with the new not equal to 0. These two values, the value created by the initializer and
entry. Based on these values, we must determine if the entry should be the current value of the text property, determine two of the conditions we
included in the field or not and return a Boolean value to communicate our need to check to validate the entry, but there is one more. When the user
decision. For example, a common task is to limit the number of characters presses a key that does not produce any character, such as the Delete key,
allowed in the field. We add the number of characters currently stored in the value assigned to the string parameter is an empty string and therefore
the text property to the number of characters the user wants to introduce we also need to check this value to determine if the entry is valid or not.
minus the length of the range and return false if the value exceeds the Inside the method of Listing 5-57, we create a logical sequence to check all
maximum allowed. This procedure is followed in the example of Listing 5- these conditions. If string contains a number and the current value of the
56. We count the characters in the text property and the string parameter, text property is not 0, or the value of string is an empty string, the logical
add both values, subtract the length of the array, which represents the sequence evaluates to true, and the entry is validated.
number of characters currently selected by the user, and finally store the
total in the total constant. The value of total is then compared to 10 (the Do It Yourself: Replace the textField(shouldChangeCharactersIn:,
maximum number of characters we want to allow in the Text Field for this method of Listing 5-56 by the method of Listing 5-
replacementString:)
example) and the value false is returned if total is greater than this number, 57. Run the application. You should only be allowed to type
rejecting the new entry.
numbers. If you type the number 0 first, the system will not let you
The possibilities of control are limitless. We can filter the input in any way
insert any additional number.
we want. For instance, we could let the user insert only numbers.
Because we are performing the same action for both, the Save button and
the Return key, we process the text in a new method called assignTitle() and
then call this method every time the button or the key are pressed. When
the user presses the Return key, the textFieldShouldReturn() method is called.
This method executes the assignTitle() method to replace the label’s text and
then returns the value true to tell the system to implement the default 
behavior for the Return key.
Other methods of the UITextFieldDelegate protocol are called when the editing In this example, the original Text Field is used to introduce the title and the
begins or ends. For example, we can implement the textFieldDidBeginEditing() new Text Field is used for the subtitle, so now the Save button must assign
method to highlight the active Text Field. The following example changes both values to the label.
the Text Field’s background color to a light gray when the user taps on it to
start typing. Listing 5-60: Highlighting two Text Fields

import UIKit Outlet called subtitleInput. Update the ViewController class with the code
class ViewController: UIViewController, UITextFieldDelegate { in Listing 5-60. Run the application and tap on one Text Field and
@IBOutlet weak var titleLabel: UILabel! then the other to see how their background colors change.
@IBOutlet weak var titleInput: UITextField!
@IBOutlet weak var subtitleInput: UITextField!
Working with two or more Text Fields in the same view and connected to
override func viewDidLoad() {
super.viewDidLoad() the same delegate presents a challenge. The methods called on the
titleInput.delegate = self delegate are always the same, no matter which Text Field performed the
subtitleInput.delegate = self
}
call. This is the reason why all the methods defined in the UITextFieldDelegate
@IBAction func changeTitle(_ sender: UIButton) { protocol include a parameter with a reference to the UITextField object that
if titleInput.text != "" && subtitleInput.text != "" { performed the call. We can use this reference to modify the object, as we
titleLabel.text = titleInput.text! + " - " + subtitleInput.text!
titleInput.text = "" did before, but also to identify the Text Field we are working with. One
subtitleInput.text = "" simple way to do this is to assign a value to the object’s tag property. A
}
}
different value is assigned to the tag property of every Text Field from the
func textFieldDidBeginEditing(_ textField: UITextField) { Attributes Inspector panel and then the property is read from code to
textField.backgroundColor = UIColor.systemGray4 know which Text Field called the method. For example, the following view
}
func textFieldDidEndEditing(_ textField: UITextField) { controller implements the textField(shouldChangeCharactersIn:, replacementString:)
textField.backgroundColor = UIColor.white method to establish different limits to the number of characters the user
}
} can type on each field.

Listing 5-61: Identifying Text Fields
This view controller includes an Outlet called subtitleInput for the new Text 
Field and assigns itself as the delegate for both Text Fields. Every time the import UIKit
user types on any of the Text Fields, the protocol methods are called in our
class ViewController: UIViewController, UITextFieldDelegate {
view controller. In this example, we have implemented the @IBOutlet weak var titleLabel: UILabel!
textFieldDidBeginEditing() and the textFieldDidEndEditing() methods to change the @IBOutlet weak var titleInput: UITextField!
background colors. When the user taps on one of the Text Fields to start @IBOutlet weak var subtitleInput: UITextField!
typing, its background becomes gray, as it did in the previous example, but override func viewDidLoad() {
now when the user taps on the other Text Field, the first one loses focus super.viewDidLoad()
titleInput.delegate = self
and it calls the textFieldDidEndEdition() method. From this method, we change subtitleInput.delegate = self
the Text Field’s background color back to white to reflect the new state. }
@IBAction func changeTitle(_ sender: UIButton) {
if titleInput.text != "" && subtitleInput.text != "" {
Do It Yourself: Add a new Text Field to the interface, as shown in titleLabel.text = titleInput.text! + " - " + subtitleInput.text!
titleInput.text = ""
Figure 5-83. Connect this element to the view controller with an
This example assumes that the values of the Text Fields’ tag properties were
defined as 1 and 2, respectively. The textField(shouldChangeCharactersIn:,

replacementString:) method checks this value to know which Text Field is
making the call (textField.tag == 1), sets the value of the maximum variable, and
The following are the most frequently used properties included in the
then compares the result with the total amount of characters allowed to
UITextView class.
validate the entry (total > maximum).
text—This property sets or returns the text in the view. It is an
Do It Yourself: Select each Text Field in the Storyboard and assign
optional of type String.
the corresponding values to their tag properties from the Attributes
Inspector panel (1 for the title and 2 for the subtitle). Update the attributedText—This property sets or returns an NSAttributedString
object with the Text View's attributed text.
ViewController class with the code in Listing 5-61. Run the application.
The title should be limited to 10 characters and the subtitle to 15. font—This property sets or returns the font used by the Text View to
display the text. It is an optional of type UIFont.
textColor—This property sets or returns the color of the text. It is an
optional of type UIColor.
textAlignment—This property sets or returns the text’s alignment. It textViewDidEndEditing(UITextView)—This method is called by
is an enumeration of type NSTextAlignment with the values left, center, the Text View on the delegate object when the element loses focus.
right, justified, and natural. textView(UITextView, shouldChangeTextIn: NSRange,
isEditable—This property sets or returns a Boolean value that replacementText: String)—This method is called by the Text View
determines whether the user is allowed to edit the text inside the Text on the delegate object every time the user inserts or deletes a
View or not. character or a string of characters. The shouldChangeTextIn argument
isSelectable—This property sets or returns a Boolean value that determines the range of characters on the view affected by the
determines whether the user is allowed to select text inside the Text operation, and the replacementText argument is the new character or
View or not. string inserted by the user. The method returns a Boolean value that
indicates whether the text should be replaced or not.
selectedRange—This property sets or returns the range of the text
selected inside the Text View. It is of type NSRange. textViewDidChange(UITextView)—This method is called by the
Text View on the delegate object when the user changes the text or
scrollRangeToVisible(NSRange)—This method scrolls the view to
any of its attributes.
show on the screen the text that corresponds to the range specified
by the argument. textViewDidChangeSelection(UITextView)—This method is
called by the Text View on the delegate object when the user selects
The UITextView class can also designate a delegate to report the state of the text.
process. The object assigned as the Text View’s delegate must conform to
the UITextViewDelegate protocol, which includes the following methods. As always, we can provide the values for all the properties from the
Attributes Inspector panel, including the text to show inside the view.
textViewShouldBeginEditing(UITextView)—This method is called When a Text View is selected, the panel includes an option at the top to
by the Text View on the delegate object to know if edition should be indicate the type of text we want to assign to the view: Plain or Attributed.
allowed. The method returns a Boolean value that indicates if edition If the Attributed option is selected, additional buttons appear to assign the
is allowed or not. attributes.
textViewDidBeginEditing(UITextView)—This method is called by
Figure 5-85: Attributed text for the Text View
the Text View on the delegate object when the user begins editing its
content.
textViewShouldEndEditing(UITextView)—This method is called
by the Text View on the delegate object when the user tries to switch
focus to another element. The method returns a Boolean value that
indicates if edition should stop or not.


The view controller for this interface is simple, we just need an Outlet to
From the Attributes Inspector panel, we can change every aspect of the reference the Text View and an Action for the button. Because we want to
text or parts of it, but this only sets the Text View’s initial value. If we want modify the attributes of the text (change the color of the characters
to modify the view’s content or replace it when the app is running, we selected by the user) we must convert the NSAttributedString object returned
must do it from code. The UITextView class offers two properties to access by the attributedText property of the UITextView object into an AttributedString
the object’s content: text and attributedText. With the text property, we can set structure with the structure's initializer defined for this purpose, as
plain text with common attributes, and with the attributeText property we explained before (see Listing 5-22).
can assign attributed text to the view.
The following example introduces a new interface with a button and a Text Listing 5-62: Adding attributes to the attributed text of a Text View
View. The purpose of the button is to assign new attributes to the 
characters selected by the user. import UIKit
convert it to the right type (see Ranges in Chapter 4). Once the attributed for pos in chars.indices {
string and the range are ready, the procedure is the same implemented in let distance = chars.distance(from: pos, to: chars.endIndex)
let endPos = chars.index(pos, offsetBy: min(distance, 4))
previous examples. We add the attribute to the range of characters and
then assign the resulting string back to the Text View. let word = String(chars[pos..<endPos])
if word == "John" {
newText[pos..<endPos].foregroundColor = .systemBlue
Do It Yourself: Delete the elements in the scene. Add a button }
called Change Color and a Text View to get an interface like Figure 5- }
message.attributedText = NSAttributedString(newText)
86. Connect the Text View to the view controller with an Outlet message.selectedRange = currentPos
}
called message and the button with an Action called selection(). Update }
the ViewController class with the code in Listing 5-62. Run the }
}
application, double click a word to select it, and press the Change 
Color button to change its color to red.
The code in Listing 5-63 works with the same interface used before, but
Of course, the characters selected by the user are not the only one we can this time we assign the view controller as the Text View’s delegate and
modify. Any range of characters can be changed at any moment. For implement the textViewDidChange() method. This method is called by the
example, we could search for specific words in the text and change the UITextView object every time the content of the Text View changes. This way,
attributes for only those characters. In the following example, we search when the user types or erases a character, our code is aware of it and can
for the string "John" and change the color of these characters to blue as process the new value.
the user types in the Text View. There are several steps we must take to find the words we want to modify.
After turning the NSAttributedString object returned by the Text View into an
Listing 5-63: Adding attributes to a range of characters AttributedString structure, we store the value of the selectedRange property. This
 is to capture to cursor's current position and set it back in the same
import UIKit position after the new attributed string is assigned to the Text View. Next,
we assign the structure to a variable to be able to modify its attributes, get Keyboard
the collection of characters from the characters property, and finally iterate

through the indexes of this collection to get the index of each character in
the string and process it. Each cycle of the loop gets the index of a
iOS has a particular way to control the keyboard. It does not consider the
character in the string. To find a word, we must create a range between
keyboard as a tool, but rather a way to detect events produced by the user,
this index and the one that indicates the end of the word. In this case, the
and therefore it keeps it on screen for as long as the element that made it
word we are looking for is "John" and therefore the last index is 4 positions
appear is still capable of processing the input. To dismiss the keyboard, we
after the initial index. First, we get the distance between the initial position
must tell the system that the element is no longer able to process the
and the end of the string to make sure the indexes are not out of range.
events, and this is done by altering the chain of response.
Then, we get the index of the last character with the index(offsetBy:) method.
When an event occurs, such as a tap on the screen, the system determines
And finally, we use these values to get the range of characters in that
which element is going to process it. The position of the elements on the
position and compare it to the word "John". If the strings match, we assign
screen does not always reflects this responsibility. To figure out which
the foregroundColor attribute to that portion of the string, changing the
element is responsible to process an event, the system considers the
characters color to blue.
elements hierarchy and creates a virtual chain in which some elements in
certain positions, called Responders, receive the event, and then decide
Do It Yourself: Update the ViewController class with the code in
whether to process it or deliver it to the next element in the chain. Some
Listing 5-63 and run the application. Tap on the text to activate the
events, like key events from the keyboard, are sent to specific elements
cursor and write the text “John”. You should see the color of the called First Responders.
word change to blue. First Responders are designated by the system or our code. This is what the
system does when the user taps on a Text Field or a Text View; it declares
the element as the First Responder, so all the events are first sent to it. And
this is also how the system manages the keyboard. The keyboard opens
when an element that is capable of handling key events becomes the First
Responder, and it is closed when the element ceases to be the First
Responder (it resigns as First Responder).
The UIKit framework defines the UIResponder class to manage Responders
and respond to events. The following are its most frequently used
properties and methods.
IMPORTANT: The simulator includes the same keyboard of a real The touchesBegan() method implemented in Listing 5-65 is called on the view
device, but it can also work with the computer’s keyboard. To select controller when the user touches the screen. We override this method in
one keyboard or another, open the I/O menu at the top of the our view controller to perform custom tasks. In this case, we use it to call
screen, go to Keyboard and select the option of your preference. the resignFirstResponder() method on the Text Field to dismiss the keyboard.
As a result, the keyboard is opened when the user taps on the Text Field
The becomeFirstResponder() method is not always necessary. Usually, the user and closed when the user taps somewhere else (except on the Save
taps on the element when it needs to type something on it and this action button, because the button provides its own response to the event).
automatically makes the element the First Responder, compelling the Calling the resignFirstResponder() method on an element only removes the
system to open the keyboard. But the execution of the resignFirstResponder() status of First Responder for that element. If we have several elements that
method is required every time we want to dismiss the keyboard because can become First Responders, such as the multiple Text Fields we had in
there is no automatic action to do it. In our previous example, we called the example of Figure 5-83, we must call the method on each one of them.
this method when the Save button was pressed, but a more intuitive To simplify our work, the UIView class includes a special method that looks
interface will also close the keyboard when the user taps somewhere else at the view and its subviews to find the current First Responder and ask the
on the screen. These types of events are detected by the event-handling element to resign.
methods included in the UIResponder class, as shown next.
endEditing(Bool)—This method looks for the First Responder in a
Listing 5-65: Dismissing the keyboard when the user touches the screen view and its subviews and asks it to resign its condition. The argument
 indicates whether the element should be forced to resign or not.
import UIKit
The following example implements the same view controller of Listing 5-65
class ViewController: UIViewController {
@IBOutlet weak var titleLabel: UILabel! but instead of calling the resignFirstResponder() method on each Text Field it
@IBOutlet weak var titleInput: UITextField! calls the endEditing() method on the main view (the view property). This
@IBAction func changeTitle(_ sender: UIButton) { method finds the current First Responder inside the view and forces it to
if titleInput.text != "" { resign its condition. It is the same process, but now we do not have to call
titleLabel.text = titleInput.text
the resignFirstResponder() method for every Text Field in the view.
titleInput.text = ""
}
} Listing 5-66: Finding the First Responder
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event) 
titleInput.resignFirstResponder() import UIKit
}
} class ViewController: UIViewController {
 @IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var titleInput: UITextField!
To identify each row and component, the picker creates indexes starting
from 0. The first value of a row will be at index 0, the second at index 1,
and so on. The items in a picker are organized this way so we can associate
it with arrays in our code and easily present and retrieve values. The class
defines the following properties and methods to interact with the picker.
selectedRow(inComponent: Int)—This method returns a value of the forComponent argument. The method must return an
type Int that represents the index of the selected row in the NSAttributedString object.
component specified by the inComponent argument. pickerView(UIPickerView, didSelectRow: Int, inComponent:
reloadAllComponents()—This method forces the picker to update Int)—This method is called by the UIPickerView object on the delegate
the values of its components. when the user selects a row (moves the wheel to get a new value at
reloadComponent(Int)—This method forces the picker to update the center). The didSelectRow and inComponent arguments contain
the values of a component. The argument indicates the index of the the indexes of the selected row and component.
component we want to update.
On the other hand, the object assigned to the dataSource property must
selectRow(Int, inComponent: Int, animated: Bool)—This
conform to the UIPickerViewDataSource protocol, which includes the following
method selects the row with the index specified by the first argument
methods.
in the component specified by the inComponent argument. The
animated argument determines whether the selection should be
numberOfComponents(in: UIPickerView)—This method is called
animated or not. The method rotates the wheel until the selected row
by the UIPickerView object on the data source delegate to know the
is positioned at the center.
number of components to display. The method must return an Int
value with the number of components we want.
The UIPickerView class also defines two properties, delegate and dataSource, to
assign delegate objects that will configure the picker and provide the pickerView(UIPickerView, numberOfRowsInComponent: Int)
values for the rows. The object assigned to the delegate property must —This method is called by the UIPickerView object on the data source
conform to the UIPickerViewDelegate protocol, which includes the following delegate to know how many rows to display in a component. The
methods. numberOfRowsInComponent argument specifies the index of the
component (the method is called for every component in the picker).
pickerView(UIPickerView, titleForRow: Int, forComponent:
Int)—This method is called by the UIPickerView object on the delegate Picker Views are usually combined with other controllers to process the
when it needs a value for the row indicated by the titleForRow values selected by the user. The interface in Figure 5-88, below, includes a
argument in the component indicated by the forComponent picker that we are going to use to show a list of years and a label that is
argument. The method must return a string with the value for the going to display the year selected by the user.
row.
Figure 5-88: Interface to test a Picker View
pickerView(UIPickerView, attributedTitleForRow: Int,
forComponent: Int)—This method is called by the UIPickerView
object on the delegate when it needs a value for the row indicated by
the attributedTitleForRow argument in the component indicated by
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) ->
Int {
return years.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component:
Int) -> String? {
return years[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component:
Int) {
let year = years[row]
showYear.text = "The Year is: \(year)"
 }
}

When the picker is added to a scene from the Library, Xcode shows it with
some values by default, as illustrated in Figure 5-88, but the real values are This view controller defines Outlets for the label and the Picker View, and
provided by the delegate object assigned to the dataSource property. This is a an array called years to store the values we want to assign to the picker. In
normal delegate, but because its purpose is to provide data it is usually the viewDidLoad() method, we use the Outlet to declare the view controller
called data source (hence the name of the property). As always, the as the picker’s delegate and data source, and also initialize the years array
delegates could be any objects we want, but it is common practice to with a list of values.
declare the view controller in charge of the picker to be the delegate as After the properties are initialized, we must configure the picker and load
well as the data source, as in the following example. the data from the delegate and data source methods. We first implement
the numberOfComponents() method to tell the picker that we want one
Listing 5-67: Providing values for the picker component to show the list of years (one column). The next method,
 pickerView(numberOfRowsInComponent:), performs a similar function, but this
import UIKit time for the rows. Inside this method, we count how many values are in
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource { the years array and return that number to indicate how many rows we need
@IBOutlet weak var showYear: UILabel! to show them all. Next is the pickerView(titleForRow:, forComponent:) method,
@IBOutlet weak var pickerYears: UIPickerView!
var years: [String]! called by the picker to get the value for each row. The first call will be for
the row at index 0, then for the row at index 1, and so on. Using these
override func viewDidLoad() {
indexes, we get the corresponding value from the years array and return it.
super.viewDidLoad()
pickerYears.delegate = self Notice that this method returns a String value. If we have an array of
pickerYears.dataSource = self integers, as in this case, before returning each value we must convert it to
years = ["1944", "1945", "1946", "1947", "1948", "1949", "1950"]
} a String using string interpolation or the String() initializer (e.g., String(number)).
In the getYear() method of Listing 5-68, we call the selectedRow() method to get With a few modifications, we can easily create a picker with more than one
the index of the selected row for the component at index 0 (we only have component. The following example includes two components to let the
one component in our picker) and then use the value of the row constant to user select a year and a city.
retrieve the year from the array and assign it to the label.
Besides the user rotating the wheel, there is also a method that allows us Listing 5-70: Creating a picker with multiple components
to select a value from code. This is particularly useful when we want the 
picker to suggest a value to the user. The following example initializes the
import UIKit
picker with the year 1945.
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
@IBOutlet weak var showYear: UILabel!
Listing 5-69: Selecting the initial value @IBOutlet weak var pickerYears: UIPickerView!
 var cities: [String]!
var years: [String]!
override func viewDidLoad() {
super.viewDidLoad()
override func viewDidLoad() {
pickerYears.delegate = self
super.viewDidLoad()
pickerYears.dataSource = self
pickerYears.delegate = self
years = ["1944", "1945", "1946", "1947", "1948", "1949", "1950"]
pickerYears.dataSource = self
cities = ["Mountain View", "Sunnyvale", "Cupertino", "Santa Clara"]
if let index = years.firstIndex(of: "1945") {
years = ["1944", "1945", "1946", "1947", "1948", "1949", "1950"]
pickerYears.selectRow(index, inComponent: 0, animated: false)
}
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
}
return 2

}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) ->
Because we want to initialize the picker with this value, we call the method Int {
if component == 0 {
from the viewDidLoad() method. First, we search for the value "1945" in the return cities.count
} else {
return years.count
}
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component:
Int) -> String? {
if component == 0 {
return cities[row]
} else {
return years[row]
}
}
@IBAction func getYear(_ sender: UIButton) {
let rowCity = pickerYears.selectedRow(inComponent: 0)
let rowYear = pickerYears.selectedRow(inComponent: 1)
showYear.text = "The Year is: \(years[rowYear]) in \(cities[rowCity])"
}
}


This view controller provides values for two components. The years array for
the years and the cities array for the cities. To tell the picker that we want
two components, the value 2 was returned from the numberOfComponents()
method. From this point on, we must consider the component every time
we interact with the picker. If the index of the component is 0, we are
working with the cities array, and when the index is 1, we are working with
the years array. The getYear() method was also expanded to read the selected
value from each component and assign them to the label. The result is
shown below.
and times. It is defined by the UIDatePicker class (a subclass of UIPickerView). minuteInterval—This property sets or returns an integer value that
With this class, we can create objects that show dates, dates and times, determines the interval at which the minutes should be shown (1 by
and a countdown timer. This picker can be created from code, using the default).
UIView initializer or by dragging the option from the Library. countDownDuration—This property sets or returns a value of type
that indicates the seconds from which the timer starts
TimeInterval
UIDatePicker(frame: CGRect)—This initializer creates an object of counting down.
the UIDatePicker class with a position and size determined by the frame
setDate(Date, animated: Bool)—This method selects a new date.
argument.
The first argument is a Date value that specifies the date we want to
select, and the animated argument is a Boolean value that
Figure 5-91: Date Picker option in the Library
determines whether we want the selection to be animated or not.
The class also includes three properties to determine the type of data to
 show. If the properties are not defined, they take the default values set on
the device.
The class provides the following properties to configure the picker.
calendar—This property sets or returns the calendar to use by the
datePickerMode—This property sets or returns the picker’s mode. picker. It is an optional of type Calendar.
It is an enumeration called Mode with the values time, date, dateAndTime, locale—This property sets or returns the locale (regional information)
and countDownTimer. to use by the picker. It is an optional of type Locale.
preferredDatePickerStyle—This property sets or returns the style timeZone—This property sets or returns the time zone of the date
of the picker. It is an enumeration called UIDatePickerStyle with the shown by the picker. It is an optional of type TimeZone.
values automatic (selected according to the platform), compact (label that
expands into a calendar), inline (editable field or calendar, depending There are three types of Date Pickers: Compact, Wheels, and Inline, as
on the platform), and wheels (standard wheel picker). shown below.
maximumDate—This property sets or returns the maximum date
the picker can show. It is a conditional of type Date. Figure 5-92: Date Picker styles
we are going to create an interface like the one used for Picker Views, as
shown below.
By default, the Style is set to Automatic, which means that the system is
going to select the appropriate style according to the space available on
the interface. If we want the keep a consistent style, we can set a specific
value from code or from the Attributes Inspector panel. For instance, in the
following example we have selected the Wheel style, to show the picker as
a wheel, the Date mode to show only dates, and set a minimum and a
maximum date.
The view controller for this interface only needs to include two Outlets for
the picker and the label, and an Action for the button.
The code in Listing 5-72 implements a DateComponents structure and the date()
method of the Calendar structure to create a date. Because the date()
method returns an optional, we unwrap it before assigning it to the date
property. Once we add the viewDidLoad() method to the previous example,
the initial values of the picker will be August 13, 2010.
5.7 Images One solution to this problem is to scale up a small image in devices with
higher resolution or scale down a big image in devices with lower

resolution. For example, we can expand an image of 300 x 400 pixels to
600 x 800 pixels and make it look like the same size in a screen with a scale
The screens of Apple devices have different resolutions and scales. In some
of 2x (a space of 300 x 400 points represents 600 x 800 pixels at this scale),
devices, one point represents one pixel and in others more. At this
or we could start with an image of 600 x 800 pixels and reduce it to 300 x
moment, three scales have been defined: 1x, 2x, and 3x. The 1x scale
400 pixels for devices with half the scale. One way or another, we have a
defines one point as one pixel, the 2x scale defines 1 point as a square of 2
problem. If we expand a small image to fill the screen, it loses quality, and
pixels, and the 3x scale defines one point as a square of three pixels. For
if we reduce it, it occupies unnecessary space in memory because the
this reason, every time we want to show images in our interface, we must
image is never shown in its original resolution. Fortunately, there is a more
consider the conversion between pixels and points. For example, if we have
efficient solution. It requires us to include in our project three versions of
an image of 300 pixels wide and 400 pixels tall, in a device with a scale of
the same image, one for every scale. Considering the image of our
1x the image will almost fill the screen, but in a device with a scale of 2x
example, we will need one picture of the husky in a size of 300 x 400 pixels
the image will look half its size. The image is occupying the same space,
for devices with a scale of 1x, another of 600 x 800 pixels for devices with a
300 by 400 pixels, but because of the higher resolution these pixels
scale of 2x, and a third one of 900 x 1200 for devices with a scale of 3x.
represents a smaller area on the screen in devices with scales of 2x or 3x,
Now, the images can be shown in the same size and with the same quality
as shown below.
no matter the device or the scale.


Providing the same image in different resolutions solves the problem but
introduces some complications. We must create three versions of the same
image and then select which one is going to be shown depending on the
scale of the device. To help us select the right image, Apple systems detect
the scale that corresponds to the image by reading a suffix on the file’s
name. What we need to do is to provide three files with the same name
but with suffixes that determine the scale for which they were designed.
The file containing the image for the 1x scale (300 x 400 pixels in our
example) only requires the name and the extension (e.g., husky.png), the
name of the file with the image for the 2x scale (600 x 800) must include
the suffix @2x (e.g., [email protected]), and the name of the file with the
image for the 3x scale (900 x 1200) must include the suffix @3x (e.g.,
[email protected]). Every time the interface requires an image, the system
reads the suffixes and loads the one corresponding to the scale of the
screen.

IMPORTANT: There is a useful app in the App Store for Mac When we drop the files into our Xcode project, a window asks for
computers called Prepo that can take an image of a scale of 3x and
information about the destination and the target. If we want the files to be
reduce it to create the versions for the rest of the scales. It can also
copied to the project’s folder (recommended), we must activate the option
help you generate the icons for your app.
Copy items if needed, as shown in Figure 5-98 below. We also must indicate
that the files are going to be added to the target created for our project
There are two ways to incorporate images into our project. One is to drag
selecting the target in the Add to targets option (the project used in this
the files to the Navigator Area and the second alternative is to use a tool
example was called Test).
called Assets Catalog. The first option is simple. We must create the images
for the resolutions we want to provide and then drag them from Finder to
Figure 5-98: Options to add files to the project
the Navigator Area, as shown in Figure 5-97.
Assets Catalog
After these options are selected and the Finish button is pressed, Xcode

includes the files with the rest of the files in our project.
Although we could include all the images for our application by adding the
files to the project, as we did in Figure 5-97, this is not practical.
Sometimes dozens, if not hundreds, of images are necessary to create an
app. No matter how we name or classify these files, it will still be very
difficult to keep them organized. For this purpose, the UIKit framework
defines a class called UIImageAsset that creates a container called Assets
Catalog to organize the project’s resources, including images, icons, colors,
and more. Templates include an Assets Catalog for our app and Xcode
offers a simple interface to edit its content. To open the interface, we must
click on the file in the Navigator Area called Assets.xcassets. Figure 5-99
shows what we see in the Editor Area.
The interface includes two columns: the column on the left presents a list
with the sets of resources available and the column on the right displays
the content of the selected set (e.g., the three images for each scale). The
list also includes two predefined sets called AccentColor and AppIcon to
define the default tint color for the controls and the app's icons, as we will
see later.
New sets can be added from the Add New Asset option in the Editor menu
at the top of the screen or by pressing the + button at the bottom of the
left column. For instance, Figure 5-100 shows what we see when we create
a new Image set.

Figure 5-100: New set of images
Once the images are loaded to the catalog, the set is ready to use. We do
not need to copy the files to the project’s folder anymore or anything like
it. All we need to do is to load the image from our app using the name
defined for the set ("husky" in our example). But this process is still a little
bit tedious. An easy way to create a new set is to drag the three images for
the set to the Assets Catalog and let Xcode define everything for us. For
 instance, we can drag and drop the files door.png, [email protected], and
[email protected] and Xcode takes care of creating the set, assigning each
The name of the set is the name we must use to get the image from code. image to the corresponding scale, and give it the name "door".
Xcode calls the new set Image but we can click on it and press Enter to
change it any time we want. By default, a set for an image includes the Figure 5-102: New door set
three versions of the image, one for each scale. Once the set is created, we
can drag the files to the corresponding squares. For example, the file
husky.png from our previous example goes inside the 1x square, the file
[email protected] goes inside the 2x square, and the file [email protected] goes
inside the 3x square. Figure 5-101, below, shows the Editor Area after the
images are dragged from Finder to the Assets Catalog and the name of the
set is changed to "husky". 
Figure 5-101: Husky set is ready Besides the possibility of including a version of the image for every scale,
we can also add versions for different devices and parameters. When a set
is selected, the Attributes Inspector panel on the right shows a list of
properties, including Devices, Appearances, and more. By default, the set is
shown in any device (Universal) and for any type of interface, but we can
change it by modifying the values of these attributes. For example, if we
select the iPad option from the list of devices (Figure 5-103, number 1), Image
new placeholders are added to the interface (Figure 5-103, number 2).

Figure 5-103: Set for specific devices
Because the purpose of images is not only to be presented on the screen
but also to be used in the construction of patterns and customized
controls, the UIKit framework provides a class called UIImage to load
images. The UIImage class can load one image at a time but includes
multiple initializers to load the image from a variety of sources. The
following are the most frequently used.
The UIImage class includes a few type properties to return objects with
standard images. The properties available are add, remove, actions, checkmark,
and strokedCheckmark. There are also properties and methods to get
information about the image and process it. The following are the most Listing 5-73: Adding a background pattern to the main view
frequently used. 
import UIKit
size—This property returns a CGSize value with the size of the image. class ViewController: UIViewController {
scale—This property returns a CGFloat value with the scale of the override func viewDidLoad() {
super.viewDidLoad()
image. if let mypattern = UIImage(named: "oranges") {
view.backgroundColor = UIColor(patternImage: mypattern)
imageOrientation—This property returns a value that identifies the }
orientation of the image. It is an enumeration called Orientation }
}
included in the UIImage class. The values available are up, down, left, right, 
upMirrored, downMirrored, leftMirrored, and rightMirrored.
cgImage—This property returns the image in the Core Graphic The UIImage() initializer implemented in Listing 5-73 looks for an image in
the Assets Catalog with the name "oranges" and returns a UIImage object if
format. It is of type CGImage, a Core Graphic data type.
the image is found and successfully loaded, or the value nil otherwise. In
pngData()—This method converts the image into raw data in the this example, we use the image to create a background pattern with a
PNG format and returns a Data structure with it. UIColor object. When a UIColor object is initialized with an image, the object
jpegData(compressionQuality: CGFloat)—This method converts takes that image and fills the area with it, as shown below.
the image into raw data in the JPEG format and returns a Data
structure with it. The compressionQuality argument is a value Figure 5-104: Main view with a background pattern
between 0.0 and 1.0 that determines the level of compression.
Working with images from code is very simple. We must create the UIImage
object with the source we want and then apply it to the elements on the
interface or show it on the screen. Many elements include properties that
take a UIImage object. For instance, the UIColor class can take a UIImage
object to create a pattern that later can be applied to the background of a
view. In the following example, we load an image called oranges.png and
apply it to the background of the scene's main view. The example assumes
that we have created a set in the Assets Catalog called oranges with the
files oranges.png, [email protected], and [email protected] (the files are 
available on our website).
 Custom buttons can present more images depending on their state. If, for
example, we want the button to show a different image when it is pressed,
The button is added to the scene from the Library as always, but with a we must add it for the Highlighted state, as shown in Figure 5-107 below
Custom Type, a Default Style, no title, and the image we want to show, as (number 1). Once this option is selected, the Image field is emptied to let
illustrated in Figure 5-106 below. (This example uses the images us pick the new image for that state. This example uses the images
buttonnormal.png, [email protected], and [email protected], buttondown.png, [email protected], and [email protected],
available on our website.) available on our website.
Figure 5-106: Configuring the button from the Attributes Inspector panel Figure 5-107: Adding an image for the Highlighted state
Attributes Inspector panel, and set the Type as Custom, the style as
Default, erase the title, and select the buttonnormal image for the
Default state and the buttondown image for the Highlighted state
(Figures 5-106 and 5-107). Run the application. When you press the
button, the image should change, as illustrated in Figure 5-108
(right).
When the app is executed, the UIImage objects that represent the images
we have added for the button are created and shown on the screen. The
interface displays the button with the image defined for the Default state
and replaces it by the second image when the button is pressed.
Figure 5-108: Custom button with a different image when pressed (right)
This example loads a symbol with the name “trash.circle”, which represents
the image of a trash can surrounded by a circle.

Figure 5-111: Button with a variant of an SF Symbol
Do It Yourself: Delete the elements in the scene. Add a Filled
button from the Library. Connect the button to the ViewController class
with an Outlet called mybutton. Update the class with the code in
Listing 5-74 and run the application. You should see a button like the
one in Figure 5-110.
SF Symbols come in different versions. For instance, the symbol with the
name “trash” implemented in our example has a version with a circle 
around the can, another with a line across, and more. These are called
variants. Symbol’s names follow a pattern we can use to identify a variant. SF Symbols adopt the size of the text or the elements they are applied to,
First, we write the base name, and then specify the variants with dot but when the symbol is used independently, there is no reference for the
notation. system to set the size or the appearance. In cases like this, or when we
want to specify a different configuration than the one set by the system,
Listing 5-75: Selecting a variant of an SF Symbols we can provide a configuration object. These objects are created from the
 SymbolConfiguration class. The class includes multiple initializers. The
Not all symbols are multicolor. In fact, most symbols are monochrome. To
know which symbols can handle multiple colors, we can use the SF
Symbols app. Once we find the symbol we want, the configuration is done
as before. The following example illustrate how to configure the bell.circle
symbol with two colors and a custom size. 
myButton.configurationUpdateHandler = { button in
var current = button.configuration
current?.title = "Add Comment"
current?.image = UIImage(systemName: "bell.circle.fill", withConfiguration: config)
current?.imagePadding = 15
button.configuration = current
}
}
}

Image View
Figure 5-115: Adding an Image View to a scene

 To assign an image to the view, we can modify its image property from code
or select it from the Attributes Inspector panel, as shown below.
The class includes properties to retrieve the image and configure the view.
Figure 5-116: Assigning an image to the Image View
image—This property sets or returns the image. It is an optional of
type UIImage.
isUserInteractionEnabled—This property sets or returns a Boolean
value that determines if the Image View responds to user events. By
default, it is set to false.
Image Views are views that contain images and therefore they are added
to the interface as any other view. The following interface includes a
button at the top and an Image View at the bottom to display the selected 
image.
Image Views have their own size, independent of the size of the image they Image Views, as any other element, may be connected to the view
contain. When we load an image, we must tell the system how to controller with Outlets and then modified from code. For example, we
accommodate the image inside the view. This is done by modifying the could replace the image when the button is pressed.
value of the contentMode property provided by the UIView class. This property
can take several values, with the most useful being Scale to Fill, Aspect Fit, Listing 5-78: Replacing the picture when the button is pressed
and Aspect Fill. Scale to Fill expands or contracts the image to fit the size of 
the view, Aspect Fit scales the image to fit the size of the view, maintaining import UIKit
its aspect ratio, and Aspect Fill scales the image to always fill the view but
class ViewController: UIViewController {
also keeping its aspect ratio, which means some parts of the image may lay @IBOutlet weak var pictureView: UIImageView!
outside the boundaries of the view and, depending on the configuration, var door: UIImage!
they may be hidden. Figure 5-117 shows what the image of the husky looks override func viewDidLoad() {
like when the view is set to different modes. super.viewDidLoad()
door = UIImage(named: "door")
}
Figure 5-117: Different modes applied to the same Image View @IBAction func changePicture(_ sender: UIButton) {
pictureView.image = door
}
}

The view controller includes an Outlet to the Image View and a property to
store the image. When the view is loaded, we create the UIImage object
with the image of the door and assign it to the door property. Once we have
this object, we can assign it to the image property of the Image View any
 time we want to show the image on the screen. In fact, the image can be
replaced at any time. For instance, we can store two UIImage objects with
Do It Yourself: Delete the elements in the scene. Add a Filled different images and show one or the other depending on a condition, as
button and an Image View from the Library. Expand the views to in the following example.
resemble the interface in Figure 5-115. Download the husky.png,
[email protected] and [email protected] files from our website and add Listing 5-79: Switching pictures
them to the Assets Catalog. Select the Image View and assign the 
husky.png image to it from the Attributes Inspector panel (Figure 5- import UIKit
116). Try different Content Mode settings to see how the image is class ViewController: UIViewController {
affected. @IBOutlet weak var pictureView: UIImageView!
var currentActive: Bool = false
var husky: UIImage!
var door: UIImage! Do It Yourself: Add the images of the husky and the door to the
override func viewDidLoad() { Assets Catalog (the images are available on our website). Connect
super.viewDidLoad() the Image View to the ViewController class with an Outlet called
husky = UIImage(named: "husky")
door = UIImage(named: "door") pictureView and the button with an Action called changePicture(). Update
}
@IBAction func changePicture(_ sender: UIButton) {
the class with the code in Listing 5-79 and run the application. Press
if currentActive { the button to change the picture on the screen.
pictureView.image = husky
} else {
pictureView.image = door
}
currentActive.toggle()
}
}

The Image View is the same as before, but now we have two UIImage
objects, one with the image of the husky and another with the image of
the door. When the button is pressed, we check the value of the
currentActive property and assign one of the images to the view. At the end,
we toggled the value of this property so next time the other image is
assigned instead. The result is shown next. Every time the button is
pressed, the image on the screen changes.

Processing Images image is a very common requirement. For instance, the resolution of
photographs taken by the camera is too high for database storage. In cases

like this, we can use the preparingThumbnail(of:) method to reduce the size of
the image or create thumbnails. As an example, we can use it to get a
To save storage space and bandwidth, images are compressed in formats
thumbnail of our husky image.
such as Jpeg and PNG. When it is time to display the image on the screen,
the system decompresses the data and resizes the image to fit inside the
Listing 5-80: Reducing the size of an image
view according to the content mode set for the Image View. Letting the

system take care of these tasks for us is fine when we are working with just
import UIKit
a few images or displaying one image at a time, but it can affect
performance when multiple images are shown on the screen at the same class ViewController: UIViewController {
@IBOutlet weak var pictureView: UIImageView!
time, as it happens when working with Table Views and Collection Views var husky: UIImage!
(see Chapter 10 and Chapter 11). To ensure that performance is not
affected by these processes, we should always provide images of a size that override func viewDidLoad() {
super.viewDidLoad()
matches their screen size and decompress them beforehand. For this husky = UIImage(named: "husky")
purpose, the UIImage class includes the following methods. }
@IBAction func changePicture(_ sender: UIButton) {
let thumbnail = husky.preparingThumbnail(of: CGSize(width: 100, height: 100))
preparingThumbnail(of: CGSize)—This method returns a new pictureView.contentMode = .top
pictureView.image = thumbnail
image created from the original and with a size determined by the of }
argument. }

prepareThumbnail(of: CGSize, completionHandler: Closure)—
This method creates a new image from the original and calls a closure This example works with the same interface introduced in Figure 5-115.
with the result. The of argument determines the size of the image. The image is connected to the pictureView outlet, and the button to the
The closure receives a UIImage object with the result. changePicture() action. When the button is pressed, we call the
preparingThumbnail(of:) method to create an image of a maximum of 100
preparingForDisplay()—This method decompresses the original
points. Notice that we change the view's content mode to top. This is to
image and returns a new one ready to be shown on the screen.
make sure that the image is shown on the screen in its original size. (By
prepareForDisplay(completionHandler: Closure)—This method default, the content mode of an Image View is set to Aspect Fit, which
decompress the original image and calls a closure when the process is causes the image to stretch to take up the available space.)
over. The closure receives a UIImage object with the result.
Figure 5-119: Thumbnail
The process of preparing an image to be shown on the screen is often only
necessary in high performance applications but reducing the size of an
Visual Effects

The Library includes two options to add an effect view with a blur effect to
a scene.
and another Image View on top of the Visual Effect View to get the
Figure 5-120: Visual Effect View options in the Library interface in Figure 5-121, right.
These options create a view that we must place over the views we want to
affect. For instance, we can add a Visual Effect View with Blur over a
background image. We add the Image View for the background first, then
the effect view, and finally the rest of the interface, as shown below.
The Attributes Inspector panel includes the option to select the style. The
value defines different levels of opacity. In our example, we used the Ultra
Thin Material effect.
Dark Appearance There are two types of resources we need to adapt to these appearances:
colors and images. Although we could perform these changes from code,

the Assets Catalog allows us to set different resources for each appearance.
For instance, we can specify different images for Any, Light, or Dark. The
The interface of Apple devices can be presented with two appearances:
option is called Appearance (Figure 5-123, number 1).
Light and Dark. The user sets the preferred appearance from the Settings
app and the applications must adapt their interfaces to it. The appearance
Figure 5-123: Set of images for the Any and Dark appearances
by default is Light, but users can change it from the Display & Brightness
option, as illustrated below.
The images for the Dark appearance are added as before, by dragging the
file for each resolution to the squares on the screen. For instance, we can
complete this example to define a button for the Light appearance and
another for Dark.

Once we have images for both appearances, we can test them from the
Storyboard or the simulator. The option in the Storyboard is available from
the Device Configuration panel (see Figure 5-21), and in the simulator the
option is available in the Features menu at the top of the screen (Toggle
Appearance). For instance, this is how the buttons look like on the
Storyboard when we change the appearance from Light to Dark. 
Figure 5-125: Button in Light and Dark appearances In this example, we have created a color set called MyColor with the color
white for the Light appearance and orange for the Dark appearance. This
color set can be later assigned to any view as we do with a normal color.
For instance, if we assign the MyColor set to the main view in the interface
of Figure 5-125, the background turns orange in Dark appearance.
Figure 5-127: Background with custom colors for Light and Dark
appearances

Icons are the little images that the user taps or clicks to launch the app. By Along with the AppIcon set, the Assets Catalog also includes a set called
default, the Assets Catalog includes a set called AppIcon to manage the AccentColor to define the interface's accent color (also known as tint
icons we must provide for the application. The set includes placeholders color). This is the color used by some controls, such as buttons and sliders.
for every icon we need and for every scale and device available. The color by default is blue, but we can modify the AccentColor set to
Icons may be created with any image edition software available in the define a new one. For instance, below, we assign an orange color to the
market. A file must be created for every size required. For example, the Universal Accent Color. From that moment on, all the controls in every
first two placeholders require images of 20 points, which means that we device will be orange, unless we specify something else later.
need to create an image of a size of 40x40 pixels for the 2x scale and an
image of a size of 60x60 pixels for the 3x scale. After all the images are Figure 5-129: Accent Color
created, we must drag them to corresponding squares, as we do with any
other image. Figure 5-128 shows what the AppIcon set may look like once
all the images are added (sample files are available on our website).
Figure 5-128: AppIcon set with the icons for iPhones and iPads

Launching Image

The launching image is the image that is shown when the app is launched.
No matter how small the app, it always takes a few seconds to load. These
images are required to give the user the impression that the app is
responsive. Xcode provides an additional file with a single scene to create a
launching image. This file is called LaunchScreen.storyboard, and it is
included in the Navigator Area along with the rest of the files created by
the template.
Apple’s guidelines recommend creating a launching image that gives the
user the impression that the app is already running. For example, we can
add an Image View with an image that simulates the app’s interface. Figure
5-130, below, shows a possible launching screen for the app created in our
last example. We added an Image View to the main view with the
background.jpg image set in the Aspect Fill mode. Because the background
is the same as the app’s background, the transition between this image
and the app’s interface will look smooth to the user. 
Figure 5-130: Launching Image for our app IMPORTANT: Although we could add any element from the Library
to this scene, only some of them are appropriate. The content of this
view is shown before the app is launched, so there is no code we can
process at that moment.
This example shows what happens when we determine the position and
size of a view in portrait mode and then rotate the device. In landscape
mode, half of the gray box generated by the view disappears at the bottom 
of the screen because there is not enough vertical space available to show
The rules applied to the button in Figure 6-2 are saying things like "this Constraints
button always has to be 20 points from the top of its container" and "this

button always has to be 20 points from the left edge of its container".
Every rule determines one aspect of the relationship between the button
Computers cannot understand human expressions like "position the view
and its container to help the system determine the position and size of the
50 points from the edge of the container". For this reason, Auto Layout
button every time the space available changes. Because of this, we no
defines a set of specific rules that we can configure and combine to
longer need to declare explicit values to lay out the elements; they are
organize the interface. Auto Layout's rules are called Constraints, and they
calculated by the system after all the rules have been considered.
are comprehensive enough to allow us to create any design we want.
There are two main groups of constraints: Pin constraints and Align
IMPORTANT: When we use Auto Layout, the values assigned to the
constraints. Pin constraints define space, such as the space between a view
frame property of every view in the Storyboard are not considered by
and the edge of its container, while Align constraints define how the views
the system anymore. Assigning new values to this property has no
should be aligned with respect to other views or their containers. The
effect on the position and size of the element on the screen, but
following list describes the Pin constraints available.
there are situations in which the values of this property are still
useful. We will see some examples in further chapters.
Top Space—This constraint defines the space between the top of the
element and its nearest neighbor or the top edge of the container
(superview).
Bottom Space—This constraint defines the space between the
bottom of the element and its nearest neighbor or the bottom edge
of the container (superview).
Leading Space—This constraint defines the space between the left
side of the element and its nearest neighbor or the left edge of the
container (superview).
Trailing Space—This constraint defines the space between the right
side of the element and its nearest neighbor or the right edge of the
container (superview).
Width—This constraint defines the width of the element.
Height—This constraint defines the height of the element.
Aspect Ratio—This constraint defines the aspect ratio of the
element.
Equal Widths—This constraint declares that two elements will have side of the elements are called Leading and Trailing because they are
the same width. If the width of one of the elements changes, the applied to a side according to the human language set in the system. In
same value is assigned to the other element affected by the English, for example, the leading space is on the left side of the element
constraint. and the trailing space is on the right, but in Arabic languages is the
Equal Heights—This constraint declares that two elements will have opposite.
the same height. If the height of one of the elements changes, the
same value is assigned to the other element affected by the
constraint.
Assigning Constraints

 
These buttons have different functions, from adding constraints to helping These menus list all the types of constraints described before, but they
us solve the issues that arise along the way. The button on the left (1) adapt the options to the types and number of elements selected. For
updates the values of the view’s frame if they do not match the values of example, the constraints that require two elements are disabled when only
the constraints, the next button (2) opens a menu with options to add one element is selected. After we activate the constraint that we want to
constraints for alignment (Align menu), the following button (3) opens a add to the element, the Add Constraints button is highlighted. Clicking this
menu to add constraints for space (Pin menu), the next one (4) opens a button, adds the constraint to the view. Figure 6-5 shows the process of
menu with options to help us solve issues with constraints or let the adding a Top Space constraint to a view.
system place the constraints for us, and the last button on the right (5)
embeds the view or the scene in containers like Stack Views or Navigation Figure 6-5: Adding a Top Space constraint to a single view
Controllers, as we will see later.
To add a constraint to an element, we select the element in the scene and
then click the button to open the Align or Pin menus. Figure 6-4 shows the
menus displayed by these buttons.
In this example, the position and size of the view are determined by the 
Top Space (1), Height (2), Leading Space (3), and Trailing Space (4)
constraints. The Leading Space and Trailing Space constraints tell the The example in Figure 6-12 implements an Aspect Ratio constraint to
system that the view must be at a certain distance from the edge of the determine the view’s height. The width is determined by the Leading Space
container, and it should shrink or expand to always be at that distance. and Trailing Space constraints, but the height is defined to be proportional
The view in Figure 6-11 has an absolute height determined by the Height to the current width by the Aspect Ratio constraint. When the view
constraint, but we could also change that assigning a Bottom space expands because of the Space constraints on the sides, it also gets
constraint or an Aspect Ratio constraint, as in the following example. proportionally taller. Because of these constraints, the view always has the
same rectangular shape.
Figure 6-12: Aspect Ratio constraint
Figure 6-13: Same aspect ratio in portrait and landscape

Editing Constraints
Do It Yourself: To reproduce the interface in Figure 6-13, you can

select and erase the Height constraint from the previous example
and add an Aspect Ratio constraint instead (see Figure 6-12). Click on Every constraint has associated values to determine its properties, such as
the Orientation button to change the orientation of the scene to the distance, the width of the element, etc. Assigning new values to the
landscape. You should see something like Figure 6-13 (right). constraints changes how they affect the elements and how the interface is
built. The Align and Pin menus only allow us to add new constraints, not to
IMPORTANT: The aspect ratio is a value that determines how many edit the ones already created. Xcode offers different alternatives to access
points on one side correspond to the points on the other side. For and edit the constraints. The simplest is to select the element and open
example, a ratio of 1:2 determines that for each point on one side, the Size Inspector panel in the Utilities Area (Figure 5-22, right). The
the other side should have two. The Aspect Ratio constraint sets this constraints associated with the element are listed at the bottom.
value according to the current aspect ratio of the view. Next, we will
see how to modify this value and specify a custom ratio. Figure 6-14: List of constraints on the Size Inspector panel
The constraints are listed at the bottom of the panel. Figure 6-14 shows the
four constraints added to the view of our last example (Trailing Space,
Leading Space, Top Space, and Aspect Ratio). Each constraint on the list (Figure 6-14), select the constraint from the Storyboard, or click on the
includes an Edit button to edit the values. Figure 6-15, below, shows the item that represents the constraint inside the Document Outline panel.
window that opens when we click on this button. Changing the values in When a constraint is selected, its values are shown in the Utilities Area.
this window automatically modifies the constraint (this menu also opens if Figure 6-16, below, shows this panel after the Top Space constraint from
we double-click the constraint in the Storyboard). our example is selected. From this panel, we can change all the constraint’s
values and some aspects of the elements the constraint is connected to.
Figure 6-15: Popup window to edit the values of a constraint
Figure 6-16: Constraint editor in the Utilities Area
Constraints have their own panel with more configuration options. To open
this panel, we can double click the constraint on the Size Inspector panel
Safe Area

The system defines a layout guide called Safe Area where we can place the
content of our interface. This is the area determined by the space
remaining inside the scene’s view after all the toolbars and special views
are displayed by the system. For example, if we don’t specify otherwise,
the system displays a status bar at the top of the screen. This bar can adapt
to different devices and orientations, and it might be completely removed
at any time. To make sure that our content never overlaps the bar, we can
pin it to the edges of the Safe Area. 
Figure 6-17: Safe Area By default, the pin constraints assigned to the view are attached to the Safe
Area, so the view will be drawn below the status bar. If instead we want
our view to be pinned to the top edge of the main view, we must change
the constraint’s configuration from the Size Inspector panel.
When we select a constraint, the Size Inspector panel shows the two
elements the constraint is attached to. To connect the top of our view to
the top of the main view, we must change the second item of the
relationship to Superview, as shown below.
For example, the following interface includes a view that is positioned and
sized with pin constraints with a value of 0. This expands the view to the
edges of the scene.
Figure 6-20, below, shows what we see in the simulator with the Top The menu displays a list of all the elements available to attach to the
constraint attached to the Safe Area (left), and what happens when we constraint. In this example, only the Safe Area and the main view are
attach it to the superview (right). available, but there may be others, as we will see later.
The option to select the element attached to the constraint is also available
in the Pin menu. When we select a Space constraint to add it to a view, we
can click the arrow on the right-hand side of the value and select the
destination view, as shown below.

Resolving Auto Layout Issues
The interface in Figure 6-22 presents a simple view with a Top Space

constraint and Width and Height constraints to determine its size. The
horizontal position was specified by a Leading Space constraint, but the
As mentioned before, Xcode shows the constraints in different colors
view was moved to the right and therefore Xcode shows dashed lines
depending on the information they provide. We have seen the lines in red
around the area in which it should be according to the constraints. This is
when there were not enough constraints to position or size the element
just an indication, and we could keep adding or modifying the constraints
and blue when the system had all the information required to place the
until the design is over, but if we need the Storyboard to represent the final
element on the screen. But there is another situation. Sometimes the
interface, we must solve the issue. One option is to update the view’s
constraints are enough, but Xcode cannot determine the position and size
frame to match the constraints’ values. This is done by selecting the view
of the views in the Storyboard and therefore there is a mismatch between
and clicking on the Update Frames button at the bottom of the Editor Area
what we see in the Storyboard and what the user will see after the
(Figure 6-3, number 1). Once we press this button, the values of the view’s
constraints are applied. This situation may occur because Xcode decides
frame are updated, and the view is moved to the place determined by the
that it is better to show the view in the current position and with the
constraints.
current size to make easy for us to edit the interface, or it just cannot
determine the new position and size under the present conditions. To help
Figure 6-23: Element in the position determined by the constraints
us identify the problem, Xcode shows the constraints in orange with
dashed lines that delimit the area where the view should be.
For complex issues, Xcode offers the Resolve Auto Layout Issues menu
(Figure 6-3, number 4). The Resolve Auto Layout Issues menu not only
allows us to solve problems with the constraints, but it can also help us
determine what the right constraints are for our interface. The following

are the options available (the options are available twice, one for the
selected views and another for all of them).
Xcode offers tools with more specific information to help us identify and
solve complex problems with constraints. An easy way to get this
information is from the Error button at the top of the Editor Area (Figure 6-
24, number 1).
Figure 6-26: Getting the list of issues from the Document Outline panel
includes another button (Figure 6-27, number 1) to open a popup menu
with suggestions and solutions.
 Some of the options in this menu are available in the Resolve Auto Layout
Issues menu, but they are adapted to the issue we are dealing with.
When we click this button, the list of views and constraints is replaced by
the list of issues our interface currently has.
All the issues are listed with detailed information, such as the current and
expected values, the constraints needed, etc. Every item of the list also
Intrinsic Content Size Listing 6-1: Reading the intrinsic content size


import UIKit
Elements like labels and buttons generate their own content. This content class ViewController: UIViewController {
@IBOutlet weak var mytitle: UILabel!
defines a minimum size that the element's view adopts when no size is
determined by the constraints. For example, the intrinsic content size of a override func viewDidLoad() {
label is determined by the size of its text. Because of this property, super.viewDidLoad()
mytitle.text = "Description: My New Label"
sometimes all we need for an element is to declare enough constraints to
determine its position. let labelSize = mytitle.intrinsicContentSize
print("New Width: \(labelSize.width)")
print("New Height: \(labelSize.height)")
Figure 6-29: Label defined with intrinsic content size }
}

The following example connects a label with an Outlet called mytitle, assigns
a new text to the label, and prints the new values of its intrinsic content
size on the console.
Multiple Views Constraints

The menu shows multiple options depending on what Xcode thinks we are
trying to do. For example, when we control-drag a horizontal line from one
view to another, the menu shows the Horizontal Spacing option at the top
to let us add a Space constraint between the views.
 Although this technique is useful to connect views together, we can also
use it to add all the same constraints applied before. For example, by
When the mouse button is released, Xcode shows a popup menu over control-dragging a line from a view to its superview we can add a Top
the target to select the constraint we want to add. Space constraint.
Figure 6-31: Quick menu to add constraints Figure 6-32: Adding a Top Space constraint to the superview
 
IMPORTANT: If you hold down the Command key or the Shift key When working with two views connected with each other, we need to
when the menu is open, you can select multiple constraints. After all make sure that the constraints provide enough information for the system
the constraints are selected, press the Return key to add them to the to position and size both views, but there are different options available,
views. depending on what we want to achieve. For instance, we can define the
width for only one view and let the system determine the width of the
When we drag and drop a line inside the same view, the menu displayed by other. In the example of Figure 6-34, below, we pinned the views to the
Xcode includes options like Width, Height, and Aspect Ratio. Figure 6-33 sides and with each other, but only added a Width constraint for the view
shows the options available after a horizontal line is dragged from and into on the left.
the view on the left.
Figure 6-34: Adapting multiple views to the space available
Figure 6-33: Popup menu to add a Width constraint
Because we defined a fixed width for the left view only, that view is always
of the same size while the view on the right expands or contracts to fill the
space available. If we had defined both views with a fixed size, the system
would not have been able to adapt the interface to the size of the screen
and it would have returned an error message.
Besides the Vertical Center constraint applied in the last example to center
the labels vertically with the view, there are other alignment constraints
available. These constraints are added from the Align menu and their
application is straightforward, as shown below.
except for the label at the top. The width of this label is determined by a
Figure 6-36: Alignment constraints for multiple views Trailing Edge constraint connected to the button (called Trailing on the
quick menu). Because of this, the label at the top always expands to reach
the right side of the button (Figure 6-37, number 1).
This example includes two labels, "Description" and "Please, press the",
and a Plain button with the title "Button". All the elements have Space
constraints to the sides of the container and between them to establish
their positions. Their sizes are determined by their intrinsic content size,
Relations and Priorities and a view. The label was defined with a Width constraint of 100, a Leading
Space constraint that pins it to the left side of the main view, and a Vertical

Center constraint that keeps it vertically centered with the view. The view
has a Height constraint to define its height and Space constraints to define
The constraints we have defined so far are constant. This means that their
its position and the distance between its left side and the label, but its
values do not change, and the system must satisfy every one of them or
width is determined by the space available. The result is that the view
return an error in case of failure. But some interfaces require more
expands or contracts, but the label has always the same width (100 points).
flexibility. Sometimes constraints need only an approximate value or they
are not required all the time. For these situations, constraints include two
Figure 6-38: Label with a fixed width of 100 points
more configuration values called Relation and Priority. The Relation
determines the relationship between the constraint and its constant value,
while the priority determines which constraint is more important.
Important constraints are satisfied first and then the system tries to satisfy
the rest as close as possible. The possible relations are the following:
There are multiple situations in which these values become useful. For
example, we may have a label that we want to be of a specific size, but to
change if new text is assigned to it. The following interface includes a label
 
What we want is for the label to stay at a size of 100 points but to grow Do It Yourself: Delete the elements in the scene. Add a label on
when the space is not enough to show the full text. We need the value of the left side of the main view with the text "My Title" and a size of
the Width constraint to be Greater Than or Equal to 100 points. To change 24 points. Insert a view on the right with a gray background (Figure
the relation, we must select the constraint, click the button on the right 6-38). Add a Leading Space constraint and a Trailing Space constraint
side of the Relation value, and select the option from the quick menu, as to the label. Add a Width constraint to the label with a value of 100.
shown next. Add a Top Space constraint and a Trailing Space constraint to the
view. Add a Height constraint to the view to define its height. And
Figure 6-40: New Relation for the constraint
lastly, add a Vertical Center constraint between the label and the
view (Figure 6-38). Assign the text "Description Label" to the label
from the Attributes Inspector panel (Figure 6-39). Select the label's
Width constraint and change its relationship to Greater Than or
Equal, as shown in Figure 6-40. You should see something like Figure
 6-41.
After the Greater Than or Equal relation is selected, the system tries to There are times when two or more constraints must be constant, but they
keep the label's width at 100 points whenever possible, but it lets the label cannot be always satisfied. In cases like this, the system needs to know
expand if necessary. Now the text is not truncated when it is longer than which one is more important. In the interface of Figure 6-42, below, two
100 points and the view fills the remaining space. views are on the sides of a label. The views expand to occupy the space
around the label, but they cannot reduce their sizes further than 50 points.
Figure 6-41: The label expands to display the full text
Figure 6-42: Testing conflicting constraints

In our example, the label was defined with a Width constraint equal to 250
points. The views are pinned to the main view and to the label with Space 
constraints and they also have a Width constraint but this time Greater
Than or Equal to 50. The views are also connected to each other by an IMPORTANT: In these situations, the app will not crash but the
Equal Width constraint to make their widths equal. This means that the system will print an error on the console with the message "Unable
label will always have the same size, but the views will expand or contract to simultaneously satisfy constraints" followed by a list of all the
to fill the space available. When there is plenty of space, the constraints constraints that couldn’t be satisfied.
work as expected. The views expand to fill the space between the label and
the edge of the main view (Figure 6-42). A problem arises when there is By default, the Width constraint of the label is equally important than the
not enough space to simultaneously satisfy all the Width constraints. The Width constraints of the views and the system does not know what to do
label must always be 250 points wide, and the views’ width cannot be less when there is not enough space to satisfy them all. The solution is simple.
than 50 points. The constraints are fine when there is enough space, but We must tell the system that it is important for us that the width of the
when there is no room for all the elements, they are displayed in red, as views is never less than 50 points, but we do not care as much about the
shown below. width of the label. This is achieved by reducing the priority of the label’s
width.
Figure 6-43: Unable to satisfy constraints The constraints with a higher-priority value are satisfied first, and then the
system tries to satisfy the rest as close as possible. Figure 6-44, below,
shows what we see if we assign a priority of, for example, 750 to the label’s
Width constraint. Now the label's width is reduced to make room for the
views and the constraint that is not satisfied is shown with a dashed line.


The Content Hugging priority determines how much the element wants to
IMPORTANT: Besides Constant, Relation, and Priority, there is an stay of the size of its content and avoid expanding (it hugs its content). On
additional value available to configure constraints called Multiplier. the other hand, the Content Compression Resistance priority determines
The multiplier is multiplied by the value of the constant to get a how much the element wants to stay of the size of its content and avoid
proportional value. For example, when the Multiplier value of an contracting (resist compression). Figure 6-46, below, presents an example
Equal Width constraint is set to 2, the element will be twice as wide of two labels with a visible background to demonstrate how this works.
as the original. The system also uses the Multiplier to set the value
for the Aspect Ratio constraint. Figure 6-46: Interface to test content priorities
There are two more types of priorities that are useful for elements with
intrinsic content size, such as labels, buttons, and images. They are called
Content Hugging priority and Content Compression Resistance priority.
Figure 6-45 shows the controls available on the Size Inspector panel to
modify their values.
The labels are pinned to the main view and to each other with Space
constraints. Their widths were not defined, so they will expand or contract
according to the space available. Which label expands, and which one
contracts is decided by the system considering their priorities. For this
example, we have decided to assign to the label on the right higher values
for its priorities. Its horizontal Content Hugging priority was declared as IMPORTANT: The priorities can be set for both the horizontal and
750 and its Content Compression Resistance priority was declared as 1000. vertical axis. The horizontal values usually apply to elements like
This will not let the label on the right expand or contract, so it is always the labels and buttons, while the vertical values are frequently used with
label on the left that adapts to the space available. Text Fields and images.
If, for example, we want the label on the right to contract when there is not
enough space, we can change its Content Compression Resistance priority
to a value smaller than the value of the label on the left. Figure 6-48 shows
the result of assigning a value of 250 to the Content Compression
Resistance priority to the label on the right and a value of 750 to the label
on the left. The label on the left continues to expand when more space is
available, but now it is the label on the right that contracts to make room
when there is not enough space for both.
Stack Views size of the container. The trick is to assign Space constraints to the sides
and at the top and bottom of the content. The system calculates the width

and height of the content from these constraints and expands or contracts
the container to match the size of its content. In the following example, we
Constraints were developed to affect single properties, such as a specific
assigned Leading, Trailing, Top and Bottom space constraints to the label.
dimension of an element or the distance from one element to another.
From these constraints and the label's intrinsic size, the system can
Because of this, designing an interface, even a simple one, requires an
calculate the size of the container view and therefore we no longer need
intricate network of constraints interconnecting the elements together.
the Width and Height constraint for the view (the size of the content
Fortunately, this elaborated system can be simplified by including our
determines the size of its container).
views inside other views that act as containers, which distributes the
constraints in hierarchy levels.
Figure 6-50: The size of the container is defined by the content
In the following example, we have positioned a view at the top of the main
view and added a label inside. The constraints for the view are connected
to the main view (left), but the constraints for the label are connected to
its container (right).
which means that the views will expand to occupy all the space available,
but we can also align them to the center, left or right by changing the Stack
View’s configuration, as shown below (we assigned a gray background to
the buttons to be able to see the space occupied by their views).
The value of the property may be defined from code through an Outlet
connected to the Stack View, or from the Attributes Inspector panel. To see
the values, we must select the Stack View from the Document Outline

panel or by holding down the Shift key and clicking the mouse’s right
button over the Stack View’s content. The options include the values for
In this example, we assigned a value of 15 to the spacing property. We can
alignment, distribution, and spacing. The latter generates a space between
also change the distribution to define how the space between views is
the views.
distributed. In the following example, we pinned the Stack View to the
bottom of the Safe Area and apply some of the values of the distribution
Figure 6-54: Space between views
property to it (Fill, Fill Equally, and Equal Spacing, in that order).

Stack Views can only manage one row or one column of views
(horizontal or vertical), but they can be nested, providing an alternative for
the creation of more complex arrangements. For example, we can add a
horizontal Stack View at the top of our vertical Stack View to show two
images side by side.
 The interface in Figure 6-57 contains an empty vertical Stack View with
Top, Leading and Trailing constraints to define its position and width, and a
In Figure 6-56, we added a horizontal Stack View on top of the buttons Height constraint to define its height. Now we can incorporate any view we
with a fillEqually distribution and a spacing of 10, and then put two Image want to the interface by adding it as the content of this Stack View.
Views inside (Figure 6-56, center). The horizontal Stack View positions the
Image Views side by side, with a width determined by the space available Listing 6-2: Adding a view to the Stack View programmatically
and a height determined by the height of the images. Therefore, to give 
the horizontal Stack View a specific height, we had to assign a Height import UIKit
constraint (Figure 6-56, number 1).
class ViewController: UIViewController {
The advantage of using Stack Views to organize the interface is clear; we @IBOutlet weak var myStack: UIStackView!
define the constraints to place the Stack View and the view takes care of
override func viewDidLoad() {
assigning the constraints to its content. Depending on our design, we may super.viewDidLoad()
use Stack Views to organize most of our interface or work with constraints, let myButton = UIButton(configuration: .filled(), primaryAction: UIAction(title: "Press Here",
handler: { action in
as we did so far. But Stack Views are also useful when we need to add to print("Hello")
the interface a view from code, as in the following example. }))
myStack.addArrangedSubview(myButton)
}
Figure 6-57: Empty Stack View }

This view controller defines an Outlet called myStack to access the Stack Constraint Objects
View and then adds a UIButton to the stack with the addArrangedSubview()

method. Because the button is the only view contained by the Stack View,
it takes its position and size.
The information for the constraints is stored as XML code in the
Storyboard’s file along with the rest of the information for the interface.
Figure 6-58: Button added to a Stack View from code
Once the application is executed, the objects that represent the constraints
are created. These objects are instances of the NSLayoutConstraint class. The
objects defined in the Storyboard are automatically created from this class,
but we can also use it to create our own constraints from code. The class
includes the following initializer.
This initializer takes all the necessary values to configure the constraint,
including the views involved, the value of the Relation, Multiplier and
Constant, and the attributes of the views that are going to be controlled by
the constraint, such as the side, dimension, etc. The NSLayoutConstraint class
includes the following enumerations to set these attributes and their
relation.
The NSLayoutConstraint() initializer returns NSLayoutConstraint objects but do let constraint1 = NSLayoutConstraint(item: mylabel, attribute: .top, relatedBy: .equal, toItem:
not add the constraints to the view. The constraints are managed by view, attribute: .top, multiplier: 1, constant: 50)
methods of the UIVIew class. let constraint2 = NSLayoutConstraint(item: mylabel, attribute: .width, relatedBy: .equal,
toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 200)
let constraint3 = NSLayoutConstraint(item: mylabel, attribute: .centerX, relatedBy: .equal,
constraints—This property returns an array with the toItem: view, attribute: .centerX, multiplier: 1, constant: 0)
NSLayoutConstraint objects that represent the constraints managed by view.addConstraints([constraint1, constraint2, constraint3])
}
the view. }

addConstraint(NSLayoutConstraint)—This method adds a
constraint to the view.
This example adds the label to the main view and then defines its
addConstraints([NSLayoutConstraint])—This method adds an constraints. The Top Space constraint is added by establishing a relation
array of constraints to the view. between the label’s top attribute and the main view’s top attribute. The
removeConstraint(NSLayoutConstraint)—This method removes Width constraint, on the other hand, only affects one element. In this case,
from the view the constraint referenced by the argument. the values for the second element and its attribute are not necessary, so
removeConstraints([NSLayoutConstraint])—This method they are declared as null with the values nil and notAnAttribute, respectively.
removes from the view the constraints referenced by the argument.
IMPORTANT: When elements are introduced to the interface
The NSLayoutConstraint initializer takes one or two views, depending on the without constraints, Xcode uses an old system called Autoresizing to
type of constraint, the attributes we want to associate to the constraint, position and size them. The information generated by this system is
and its values. The following example applies a Top Space, a Width, and a later applied to create constraints for the elements and present the
Horizontal in Container constraints to a label. interface on the screen. If the constraints are added from code
though, the system doesn't know that there are constraints available
Listing 6-3: Laying out elements with constraints defined in code and tries to create its own. To tell the system that the Autoresizing
 constraints are not necessary, you must set the view's
import UIKit translatesAutoresizingMaskIntoConstraints property to false. Assigning the
value false to this property avoids the creation of automatic
class ViewController: UIViewController {
override func viewDidLoad() {
constraints and ensures that those you define in code will not
super.viewDidLoad() conflict with those defined by the system. The property must be
defined for every view added to the interface.
The following example creates a button at the center of the screen that
expands or contracts when it is pressed. The first group of constraints
determine its vertical position (50), horizontal position (centerX), and height
 (50), while the last constraint determines its width. The value of the Width
constraint is changed by the expandButton() method every time the button is
Besides adding and deleting constraints, we can also edit their values. For pressed.
this purpose, the NSLayoutConstraint class offers the following properties.
Listing 6-5: Updating constraints
firstItem—This property sets or returns a reference to the first 
element associated with the constraint. It is an optional of type import UIKit
AnyObject.
class ViewController: UIViewController {
secondItem—This property sets or returns a reference to the second var button: UIButton!
var buttonTitle = "Expand"
element associated with the constraint. It is an optional of type var constraintWidth: NSLayoutConstraint!
AnyObject.
override func viewDidLoad() {
firstAttribute—This property sets or returns the value of the first super.viewDidLoad()
element’s attribute. It is an NSLayoutConstraint.Attribute enumeration button = UIButton(configuration: .filled(), primaryAction: UIAction(handler: { [unowned self]
action in
value. self.expandButton()
}))
secondAttribute—This property sets or returns the value of the button.configurationUpdateHandler = { [unowned self] button in
second element’s attribute. It is an NSLayoutConstraint.Attribute var config = button.configuration
enumeration value. config?.title = buttonTitle
button.configuration = config
priority—This property sets or returns the constraint’s priority value. }
button.translatesAutoresizingMaskIntoConstraints = false
It is a structure of type UILayoutPriority with the properties required, view.addSubview(button)
defaultHigh, defaultLow, and fittingSizeLevel.
let constraintButton1 = NSLayoutConstraint(item: button!, attribute: .top, relatedBy: .equal,
toItem: view, attribute: .top, multiplier: 1, constant: 50)
let constraintButton2 = NSLayoutConstraint(item: button!, attribute: .height, relatedBy: .equal, leadingAnchor—This property returns an object that represents the
toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 50)
let constraintButton3 = NSLayoutConstraint(item: button!, attribute: .centerX, relatedBy: .equal, view's leading edge.
toItem: view, attribute: .centerX, multiplier: 1, constant: 0)
view.addConstraints([constraintButton1, constraintButton2, constraintButton3])
trailingAnchor—This property returns an object that represents the
view's trailing edge.
constraintWidth = NSLayoutConstraint(item: button!, attribute: .width, relatedBy: .equal, toItem:
nil, attribute: .notAnAttribute, multiplier: 1, constant: 150) widthAnchor—This property returns an object that represents the
view.addConstraint(constraintWidth) view's width.
}
func expandButton() { heightAnchor—This property returns an object that represents the
if constraintWidth.constant < 280 { view's height.
buttonTitle = "Contract"
constraintWidth.constant = 280 centerXAnchor—This property returns an object that represents the
} else {
buttonTitle = "Expand"
view's horizontal center.
constraintWidth.constant = 150 centerYAnchor—This property returns an object that represents the
}
} view's vertical center.
}

leftAnchor—This property returns an object that represents the
view's left edge.
IMPORTANT: We cannot only modify the constraints added from rightAnchor—This property returns an object that represents the
code but also those created in the Storyboard. All we need to do is view's right edge.
to control-drag a line from the constraint to the view controller to firstBaselineAnchor—This property returns an object that
create an Outlet. This Outlet is like any other we have created so far, represents the baseline of the top line of text in the view.
but instead of referencing a view it references the NSLayoutConstraint
lastBaselineAnchor—This property returns an object that
object that represents the constraint.
represents the baseline of the last line of text in the view.
Creating one constraint at a time with NSLayoutConstraint objects takes
These properties contain objects defined by subclasses of the
time and requires several lines of code. Apple provides two solutions to
this problem: a visual format and layout anchors. The latter is the most NSLayoutAnchor class called NSLayoutXAxisAnchor, NSLayoutYAxisAnchor, and
NSLayoutDimension. The following are the most frequently used methods
popular nowadays because it declares the constraints using properties. The
following are the properties defined in the UIView class for this purpose. included in these classes to add the constraints.
topAnchor—This property returns an object that represents the constraint(equalTo: NSLayoutAnchor, constant: CGFloat)—This
view's top edge. method returns an NSLayoutConstraint object with a constraint that
defines one anchor as equal to the other anchor, and with an offset
bottomAnchor—This property returns an object that represents the
determined by the constant argument.
view's bottom edge.
constraint(greaterThanOrEqualTo: NSLayoutAnchor, constant: view and implements the constraint(equalTo:, constant:) method to add Space
CGFloat)—This method returns an NSLayoutConstraint object with a constraints to every side.
constraint that defines one anchor as greater than or equal to the
other anchor, and with an offset determined by the constant Listing 6-6: Attaching constraints to the view’s anchors
argument. 
import UIKit
constraint(lessThanOrEqualTo: NSLayoutAnchor, constant:
CGFloat)—This method returns an NSLayoutConstraint object with a class ViewController: UIViewController {
constraint that defines one anchor as less than or equal to the other override func viewDidLoad() {
super.viewDidLoad()
anchor, and with an offset determined by the constant argument. let grayView = UIView(frame: CGRect.zero)
grayView.backgroundColor = UIColor.systemGray4
constraint(equalTo: NSLayoutDimension, multiplier: CGFloat) grayView.translatesAutoresizingMaskIntoConstraints = false
—This method returns an NSLayoutConstraint object with a constraint view.addSubview(grayView)
that defines the size attribute of the view equal to the specified
let constraint1 = grayView.leadingAnchor.constraint(equalTo: view.leadingAnchor,
anchor multiplied by the value of the multiplier argument (used to constant: 0)
defined equal widths and heights or to create Aspect Ratio let constraint2 = grayView.trailingAnchor.constraint(equalTo: view.trailingAnchor,
constant: 0)
constraints). let constraint3 = grayView.topAnchor.constraint(equalTo: view.topAnchor, constant: 0)
constraint(equalToConstant: CGFloat)—This method returns an let constraint4 = grayView.bottomAnchor.constraint(equalTo: view.bottomAnchor,
constant: 0)
NSLayoutConstraintobject with a constraint that defines the size view.addConstraints([constraint1, constraint2, constraint3, constraint4])
attribute of the view equal to the value of the equalToConstant }
}
argument. 
constraint(greaterThanOrEqualToConstant: CGFloat)—This
method returns an NSLayoutConstraint object with a constraint that The constraints in this example are created from the Leading anchor of our
defines the size attribute of the view greater than or equal to the view to the Leading anchor of the main view. The same for the Trailing
value of the greaterThanOrEqualToConstant argument. anchor, the Top anchor and the Bottom anchor. Finally, the constraints are
constraint(lessThanOrEqualToConstant: CGFloat)—This added to the view with the addConstraints() method, as we did before.
method returns an NSLayoutConstraint object with a constraint that Because we use a constant of value 0, the view expands to fill the main
defines the size attribute of the view less than or equal to the value of view's area.
the lessThanOrEqualToConstant argument.
Figure 6-60: View pinned from its anchors
Depending on the types of constraints we need, we may use one method
or another. For example, the following view controller creates an empty
}
}

In this example, we assign the value true to the isActive property of each
constraint using dot notation. The NSLayoutConstraint object is created first
with the constraint() method and then the value true is assigned to the
constraint’s property, activating the constraint. The code creates two
Alignment constraints to position a view at the center of the main view and
Width and Height constraints to determine its size. The result is shown
 below.
The NSLayoutConstraint class offers a Boolean property called isActive to Figure 6-61: Constraints activated with the isActive property
activate or the deactivate a constraint. Using this property, we can tell the
system which constraints are going to affect the layout, and because the
process of activating or deactivating a constraint automatically calls the
addConstraint() and removeConstraint() methods, we don't have to call these
methods anymore, as shown in the following example.
let grayView = UIView(frame: CGRect.zero) The constraints not only can be attached to the anchors of the views but
grayView.backgroundColor = UIColor.systemGray4 also to the anchors of layout guides like the Safe Area. The following are
grayView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(grayView)
some of the properties provided by the UIView class to access these layout
guides.
grayView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0).isActive =
true
grayView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = safeAreaLayoutGuide—This property returns a UILayoutGuide object
true with anchor properties we can use to define constraints between the
grayView.widthAnchor.constraint(equalToConstant: 125).isActive = true
grayView.heightAnchor.constraint(equalToConstant: 125).isActive = true views and the Safe Area.
readableContentGuide—This property returns a UILayoutGuide The code in Listing 6-8 defines a constant called safeGuide to store a
object with anchor properties we can use to define constraints reference to the Safe Area's Layout Guide, and then defines the constraints
between the views and the readable area of their superviews (the between the view’s anchors and the anchors of the guide. Because of this,
area in which the lines of text are short enough to be readable). the view does not overlap the status bar.
keyboardLayoutGuide—This property returns a
object (a subclass of UILayoutGuide) with anchor
UIKeyboardLayoutGuide
Figure 6-62: Constraints between a view and the Safe Area's Layout Guide
properties we can use to define constraints between the views and
the area occupied by the keyboard.
super.viewDidLoad()
let commentsView = UITextView(frame: CGRect.zero)
commentsView.backgroundColor = UIColor.systemGray5
commentsView.font = UIFont.preferredFont(forTextStyle: .body)
commentsView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(commentsView)
These methods must be called on the view property of the view controller
one after another to update the main view and its content. First we call the
setNeedsLayout() method to tell the system that the current layout is invalid
and then the layoutIfNeeded() method to force it to refresh the interface.
After the methods are executed, we can read any of the element’s
properties, such as frame, and we will get values that reflect what the user
sees on the screen. The UIView class also includes the following methods to
update the constraints.
The way Auto Layout organizes the interface is by telling the system how
every element should be laid out. In practice, this is only useful when the
space available preserves its original proportions. Things change when we
compare devices with very different screens, such as an iPhone and an
iPad, or the same device in different orientations. In these disparate
conditions, the interface must adapt in a way Auto Layout cannot handle.
To get the design we want in every possible configuration, the values of the 
elements and constraints must be drastically modified, and some of them
even removed or added. To know when to perform these changes, the The combinations presented in Figure 6-64 are the only four possible
system classifies the space available based on the magnitude of the combinations of Size Classes: Compact width and Regular height, Compact
horizontal and vertical dimensions. The size is called Regular if it is big width and Compact height, Regular width and Compact height, and
enough to fit a regular interface or Compact otherwise. This classification Regular width and Regular height. By selecting the appropriate Size Classes
is, of course, arbitrary. Apple’s developers decided what should be for the width and height, it is possible to adapt our interface to any space
considered Compact and what Regular based on the screen’s sizes of the available.
devices currently available in the market.
The Compact and Regular values conform a unit of measurement called
Size Classes (this has nothing to do with the classes defined to create
objects). Because of the rectangular shape of the screen, the interface is
defined by two Size Classes, one for the horizontal space and another for
the vertical space. For example, the Size Classes defining the space
available for iPhones in portrait mode are Compact for their horizontal
space and Regular for their vertical space because in this orientation the
screen’s width is constrained but its height has enough space to display a
normal user interface. Every device is assigned different Size Classes,
depending on the size of their screens and orientations.
Xcode offers tools to introduce modifications for specific Size Classes. For
example, we can add a label to an interface for the Size Classes Compact
width and Regular height and the label will be shown only on iPhones in
portrait mode or on a narrow split view in iPads. With these tools, we
cannot only add or remove elements but also modify some properties and
add, remove, or modify constraints for any combination of Size Classes we
want.
The simplest change we can perform for a Size Class is to modify the value

of a property. Some attributes like the background color or the font size
can be changed from the Attributes Inspector panel. When an element on
After the combination of Size Classes is selected and the Add Variation
the interface is selected, this panel shows the values of its properties. The
button is pressed, a new Font field is added to the Attributes Inspector
properties that can be adapted to a specific Size Class include a + button on
panel. Figure 6-67, below, shows what we see when a font is added for the
the side. Figure 6-65, below, shows the button available for the Font when
Size Classes Compact width and Regular height.
a label is selected.
Figure 6-67: New font size for a label in Compact width and Regular height
Figure 6-65: Adapting a font from the Attributes Inspector panel
When we click on the + button to add a font for a Size Class, Xcode shows a

popup menu to select the combination of Size Classes we want to associate
with the font, along with other values we will introduce later.
In this example, we have defined the font size for the label as 38 points Adapting Constraints
when the interface is presented on iPhones in portrait mode (Compact

width and Regular height). The label is displayed with a size of 38 points in
this configuration and with a size of 20 points anywhere else.
There are three things we can do with constraints: modify their values,
deactivate them, and add new ones. The values are not modified; instead,
Figure 6-68: Different font size in portrait and landscape
new values are added for a specific configuration. For example, if we want
our label to be 50 points from the top when the interface is presented in
iPhones in portrait orientation, we can select the label's Top Space
constraint and add a new value for Compact width and Regular height from
the Attributes Inspector panel (Figure 6-69, number 1).

Figure 6-69: Different value for the constraint in Compact width and
In this example, we have selected a combination of Size Classes to affect a Regular height
very specific configuration, but the system also contemplates the
possibility of adapting the interface for the Size Class corresponding to only
one dimension (horizontal or vertical) without considering the other. The
option is called Any and it is available from the Size Classes menu. For
example, we can select the Size Classes Regular width and Any height to
affect only iPads and large iPhones in landscape orientation.

IMPORTANT: The configuration by default is for the Size Classes
Any width and Any height, which means it will be applied to any
configuration of Size Classes unless another value has been provided
IMPORTANT: The Attributes Inspector panel shows an x button on
the left side of every additional value to remove it. When a value is
for a specific Size Class.
removed, the constraint takes back the value defined for the Size
Classes Any width and Any height.
Once the new option is added, we must uninstall the constraint by clicking
the checked button.
The constraint still exists, but it is not functional in Compact width and
Compact height. This means that in that configuration (small iPhones in
landscape mode), the label now needs new constraints. The new
constraints are added as we did before from the Auto Layout buttons, but
we must install them only for Compact width and Compact height to affect
 the label only on that configuration. After adding a Horizontally in
Container constraint to center the label, we add the Installed option for
The option to uninstall the selected constraint is shown at the bottom of Compact width and Compact height and uninstall the constraint for all the
the panel (Figure 6-70, number 1). The option available by default rest of the configurations, as shown in Figure 6-73.
corresponds to Any width and Any height, which means that the constraint
is applied in any combination of Size Classes. To uninstall this constraint,
Figure 6-73: Horizontal alignment constraint installed only for Compact Install this constraint for the Size Classes Compact width and
width and height Compact height and uninstall it for the rest (Figure 6-73). The label
should be displayed on the left anywhere except for small iPhones in
landscape mode.
Now, there is a Leading Space constraint that is installed for the label in any
configuration except Compact width and Compact height, and a
Horizontally in Container constraint (Center X) that is installed only in
Compact width and Compact height. The result is shown below; the label is
always pinned to the left side except in small iPhones in landscape mode.
The process to add or remove an element is the same we used for Every time the interface is displayed, each element receives a Trait
constraints, we select the element and install it or uninstall it for the Collection object with information about its own space on the screen,
configuration of Size Classes we want to affect. including the horizontal and vertical Size Classes. And this is how we can
recognize the current Size Classes from code. The objects are created from
Figure 6-75: Installed option for views the UITraitCollection class. The instantiation of these objects is automatic, but
we can also create our own using the initializers provided by the class.
UITraitCollection(horizontalSizeClass:
UIUserInterfaceSizeClass)—This initializer creates a UITraitCollection
object with the value specified by the argument. The argument is an
enumeration with the values unspecified, compact, and regular.
UITraitCollection(verticalSizeClass: UIUserInterfaceSizeClass)
—This initializer creates a UITraitCollection object with the value
specified by the argument. The argument is an enumeration with the
 values unspecified, compact, and regular.
The panel shows the four options available (Portrait, Upside Down, These are computed properties that only return a value, but we can
Landscape Left, and Landscape Right). If any of these options is override them in our view controller and make them return a different
deactivated, the interface of our app does not change when the device value to specify the behavior we want.
rotates to that orientation. The options are pre-set by Xcode according to
the selected device (iPhone or iPad). All the orientations are automatically Listing 6-13: Deactivating rotation for a view controller

activated for iPad and Universal, but the Upside Down orientation is
import UIKit
deactivated if we are creating a project for iPhones. This is to avoid the
user making the mistake of answering a call with the phone upside down. class ViewController: UIViewController {
override var shouldAutorotate: Bool {
return false
IMPORTANT: If you want to configure different orientations for }
each device, select only the device you want to modify and perform }

the changes. Once you finish, select again all the devices you want
your application to run in. The setup is preserved in a plist file you
The shouldAutorotate property indicates whether the main view should
can read and modify from the Info panel.
respond to the device’s rotation. When we return the value true from this
property, the main view is rotated in any of the orientations available
These options affect the entire app. The rotation of the initial view
(those activated in the app's settings), but when we return false, the main
controller and every view controller we add later to the Storyboard will be
view is presented in the current orientation and then never rotated.
limited to the options activated in this panel. If we want to define the
Another way to tell the system if the main view should rotate or not is by
orientation for each view controller, we must specify it in the definition of
overriding the supportedInterfaceOrientations property. With this property, we
can select exactly the orientations available for each view controller by
returning a value or a set of values of type UIInterfaceOrientationMask.
CHAPTER 7 - SCROLL VIEWS
In this example, we return the value landscape to let the interface adapt to
any of the landscape orientations (left or right). The main view of this view
controller will only adapt when the device is in landscape mode. If we want
to specify two or more possible orientations, we must declare them in a
set (e.g., [.portrait, .landscapeLeft]).
Figure 7-1: User scrolling through the Scroll View's content area
A Scroll View can be created from code with the UIView initializer or added
to the Storyboard from the option in the Library.

Customizing Scroll Views zoomScale—This property sets or returns the current scale of the
content. It is of type CGFloat with a default value of 1.0.

maximumZoomScale—This property sets or returns the maximum
Although we could define the whole interface inside a Scroll View, as we possible scale for the content. It is of type CGFloat with a default value
will see at the end of this chapter, working with constraints inside a Scroll of 1.0.
View introduces some complications. For that reason, the Scroll View and minimumZoomScale—This property sets or returns the minimum
its content are usually defined from code. The following are some of the possible scale for the content. It is of type CGFloat with a default value
properties and methods included in the UIScrollView class to manage the of 1.0.
view and its content. zoom(to: CGRect, animated: Bool)—This method zooms the
content to make visible the area specified by the to argument. The
contentSize—This property sets or returns the size of the content animated argument indicates whether the process will be animated or
area. It is a structure of type CGSize. not.
contentOffset—This property sets or returns the x and y scrollRectToVisible(CGRect, animated: Bool)—This method
coordinates that determine the positions of the piece of content the scrolls the content to make visible the area specified by the first
Scroll View is currently showing on the screen. It is a structure of type argument. The animated argument indicates whether the process will
CGPoint, with a value by default of 0, 0. be animated or not.
contentInset—This property sets or returns the content’s padding.
This is the space between the edges of the Scroll View and its content. Scroll Views can also designate a delegate for configuration and to report
It is a structure of type UIEdgeInsets with the properties top, left, bottom, changes on their state. The delegate must conform to the UIScrollViewDelegate
and right, and a value by default of 0, 0, 0, 0. protocol and implement its methods. The following are the most
isScrollEnabled—This property is a Boolean value that determines if frequently used.
scrolling is enabled.
viewForZooming(in: UIScrollView)—This method is called by the
isPagingEnabled—This property is a Boolean value that determines
Scroll View on its delegate to know which view is going to be zoomed
whether the Scroll View considers its content as pages or not.
in or out.
showHorizontalScrollIndicator—This property is a Boolean value
scrollViewDidScroll(UIScrollView)—This method is called by the
that determines whether the horizontal scroll indicator is visible or
Scroll View on its delegate when the user scrolls the content.
not.
scrollViewDidZoom(UIScrollView)—This method is called by the
showVerticalScrollIndicator—This property is a Boolean value that
Scroll View on its delegate when the user zooms the content in or out.
determines whether the vertical scroll indicator is visible or not.
There are two ways to add the Scroll View to the interface from code. We 
can create the UIScrollView object, add it to the main view or a container import UIKit
view, and then define the constraints from code, as explained in Chapter 6.
class ViewController: UIViewController {
But a better alternative is to add a Stack View to the main view and then @IBOutlet weak var stackContainer: UIStackView!
add the Scroll View as the content of the Stack View, as in the following
override func viewDidLoad() {
example. super.viewDidLoad()
let imgView = UIImageView(image: UIImage(named: "doll"))
let imgWidth = imgView.frame.size.width
Figure 7-5: Vertical Stack View to add a Scroll View from code
let imgHeight = imgView.frame.size.height
stackContainer.addArrangedSubview(mainScroll)
}
}

The same way we did with constraints, every time we add a Scroll View
from code, we must define the size of the Scroll View and the size of its
content area. Because we are embedding the Scroll View in a Stack View,
its size will be determined by the size of the Stack View in the Storyboard,
but the size of the content area must be defined in code from the contentSize
property. In the example of Listing 7-1, we create an Image View to add to
the content area and then declare the size of the area as the size of the
image (we did not have to declare the frame of the Image View because
the initializer takes the values from the image). After the content area is
ready, we create the UIScrollView object and add the Image View as its
 content with the addSubview() method. Finally, the Scroll View is added to
the Stack View with the addArrangedSubview() method. The result is the same
Once the Stack View is added to the interface, we must connect it to an shown in Figure 7-4.
Outlet in the view controller and then create and add the Scroll View and
its content from code, as shown next. Do It Yourself: Create a new project. Add a Stack View to the main
view and pin it to the sides (Figure 7-5). Create an Outlet for the
Listing 7-1: Adding a Scroll View from code Stack View called stackContainer and complete the view controller with
the code in Listing 7-1. Download the image doll.jpg from our stackContainer.addArrangedSubview(mainScroll)
}
website and add it to the Assets Catalog. Run the application and }
scroll the image. 
The content area of a Scroll View may contain as many views as we want.
The result of the previous example is the same achieved when the Scroll
The following example includes a second image with a logo at the top-left
View was added to the Storyboard (see Figure 7-4), but there is an issue
corner of the area (the image is called logo.png and is available on our
that requires our attention. Despite connecting the Stack View to the top
website).
of the main view, the image appears below the status bar. This is because
by default the Scroll View positions its content inside the Safe Area. To
Listing 7-3: Adding more views to the content area
modify this behavior, the UIScrollVIew class includes the following property.

import UIKit
contentInsetAdjustmentBehavior—This property determines
how the Scroll View is going to adjust the content's offsets. It is an class ViewController: UIViewController {
@IBOutlet weak var stackContainer: UIStackView!
enumeration of type ContentInsetAdjustmentBehavior with the values
automatic, scrollableAxes, never, and always. override func viewDidLoad() {
super.viewDidLoad()
If we want to make sure that the scrollable content is shown full screen, we let imgView = UIImageView(image: UIImage(named: "doll"))
can define this property with the value never, as shown next. let imgWidth = imgView.frame.size.width
let imgHeight = imgView.frame.size.height
Listing 7-2: Showing the content full screen let mainScroll = UIScrollView(frame: .zero)
 mainScroll.contentSize = CGSize(width: imgWidth, height: imgHeight)
mainScroll.addSubview(imgView)
import UIKit
let logoView = UIImageView(frame: CGRect(x: 25, y: 25, width: 249, height: 249))
class ViewController: UIViewController { logoView.image = UIImage(named: "logo")
@IBOutlet weak var stackContainer: UIStackView! mainScroll.addSubview(logoView)
let mainScroll = UIScrollView(frame: .zero) The example in Listing 7-3 creates the Image View for the logo with a frame
mainScroll.contentSize = CGSize(width: imgWidth, height: imgHeight) in the position 25, 25, and then adds it to the Scroll View. Because the
mainScroll.contentInsetAdjustmentBehavior = .never
mainScroll.addSubview(imgView) image with the logo is added after the image with the doll, it is shown at
the top, but it is fixed at the position 25, 25 in the content area and 
therefore it scrolls along with the rest of the content, as shown below. import UIKit
difficult to do when the content has to be positioned and sized relative to logoView.topAnchor.constraint(equalTo: mainScroll.frameLayoutGuide.topAnchor,
the sides of the content area or to the sides of the Scroll View. For these constant: 25).isActive = true
logoView.leadingAnchor.constraint(equalTo: mainScroll.frameLayoutGuide.leadingAnchor,
situations, the UIScrollView class offers two layout guides that allow us to set constant: 25).isActive = true
constraints between the views and the content area or between the views
and the Scroll View. The following are the properties included in the class stackContainer.addArrangedSubview(mainScroll)
}
to access these guides. }

Setting the size of the content area gives enough information to the Scroll
View to allow the user to scroll the content, but to zoom in and out we
must define the maximum and minimum scales allowed with the
minimumZoomScale and maximumZoomScale properties, and also conform to the

UIScrollViewDelegate protocol and implement its viewForZooming() method to
declare which view is going to participate in the process.
mainScroll.minimumZoomScale = 1.0
mainScroll.maximumZoomScale = 4.0
stackContainer.addArrangedSubview(mainScroll)
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imgView
}
}

mainScroll.contentInsetAdjustmentBehavior = .never
mainScroll.delegate = self
The view controller in Listing 7-5 conforms to the UIScrollViewDelegate mainScroll.addSubview(imgView)
protocol and declares itself as the delegate of the mainScroll view. It also
let scrollWidth = stackContainer.frame.size.width
declares a minimum scale of 1.0 and a maximum scale of 4.0, and stores a
let scrollHeight = stackContainer.frame.size.height
reference to the Image View in the imgView property to be able to return it let minScale = min(scrollWidth / imageWidth, scrollHeight / imageHeight)
from the viewForZooming() method. When the Scroll View detects that the let maxScale = max(minScale * 4.0, 1.0)
user is trying to zoom in or out, it calls this method on its delegate to know mainScroll.minimumZoomScale = minScale
which view in the content area is going to be affected by the action and mainScroll.maximumZoomScale = maxScale
begins to zoom until a limit is reached. stackContainer.addArrangedSubview(mainScroll)
The zoom scale is considered to be 1.0 at the initial state. If we set absolute }
values for the minimumScale and maximumScale properties when the initial func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imgView
image is larger than the area of the Scroll View, as in the previous example, }
the user will not be able to zoom out enough to see the whole image on }

the screen. There are different ways to accommodate these values,
depending on the effects we want to achieve in our application. For
To calculate what should be the minimum scale so the user can zoom out
instance, the following example calculates the minimum scale from the
to see the whole image on the screen, we need to obtain the current
initial size of the image.
values of the Scroll View’s width and height properties. Because the Scroll
View is going to have the same size of the Stack View, we can read the
Listing 7-6: Calculating the minimum and maximum scales values from this view, but the problem is that those values are not updated

when the main view is loaded but during the system’s updating cycles. To
import UIKit
force the system to update the values at the time we need them, we have
class ViewController: UIViewController, UIScrollViewDelegate { to call the main view’s setNeedsLayout() and layoutIfNeeded() methods (see
@IBOutlet weak var stackContainer: UIStackView! Chapter 6). If we do not execute these methods before reading the view’s
var imgView: UIImageView!
frame property, the values returned might be those defined in the
override func viewDidLoad() { Storyboard (this is not always required, it depends on when in the process
super.viewDidLoad()
we try to read the frames).
view.setNeedsLayout() The minimum scale is calculated in Listing 7-6 by dividing one of the Scroll
view.layoutIfNeeded()
View’s dimensions by the same dimension of the Image View. For example,
imgView = UIImageView(image: UIImage(named: "doll")) if we divide the width of the Scroll View by the width of the Image View,
let imageWidth = imgView.frame.size.width we get the scale necessary to let the user zoom out until the image fits the
let imageHeight = imgView.frame.size.height
width of the view. But because we want the user to be able to see the
let mainScroll = UIScrollView(frame: .zero) whole image, we have to calculate the scale for both dimensions and get
mainScroll.contentSize = CGSize(width: imageWidth, height: imageHeight)
the smaller value. We do this by comparing the result of both formulas Pages
with the min function (min(scrollWidth / imageWidth, scrollHeight / imageHeight)). This

will be our minimum scale. From this value we get the maximum scale
multiplying it by 4.
The UIScrollView class includes functionality to split the content area into
pages. The size of the pages is determined by the size of the Scroll View, so
IMPORTANT: If you want the image to be presented from the
when the user swipes a finger on the screen, the current visible portion of
beginning at the minimum scale, you can set the initial zoom scale
the content area is completely replaced by the section that represents the
with the zoomScale property. Add the statement mainScroll.zoomScale =
next page. To activate this mode, we assign the value true to the
minScale to the Scroll View configuration.
isPagingEnabled property and configure the content to represent the virtual
pages. The following is a simple example that presents three images,
spot1.png, spot2.png, and spot3.png, one per page.
view.setNeedsLayout()
view.layoutIfNeeded()
let scrollWidth = stackContainer.frame.size.width
let scrollHeight = stackContainer.frame.size.height
imgView.clipsToBounds = true The UIKit framework includes a special type of control called Page Control
mainScroll.addSubview(imgView) that is particularly useful in these kinds of applications. The control displays
posX = posX + scrollWidth dots on the screen that represent each page available and changes their
}
colors according to the visible page. There is an option available in the
stackContainer.addArrangedSubview(mainScroll)
} Library to add a Page Control to the Storyboard.
}

Figure 7-8: Page Control option in the Library
The Image Views need to be of the size of the Scroll View and positioned
side by side to represent the pages. This is the reason why in Listing 7-7 we
declare the frame of every Image View. The values for the frame’s width and 
height properties are determined by the size of the Scroll View (scrollWidth
and scrollHeight), and the coordinates for the position are calculated The control is created from the UIPageControl class. The class provides the
according to the page the view represents. The first Image View is added at following properties for configuration.
the coordinate 0, 0, but the second Image View is displaced to the right a
distance determined by the width of the Scroll View. To calculate this value,
currentPage—This property sets or returns the number of the
we use the posX variable. In every cycle of the loop, the variable is
current page.
incremented with the value of the scrollWidth property to establish the
horizontal position of the next view. As a result, the images are positioned numberOfPages—This property sets or returns the number of
on the content area one after another, from left to right. pages represented by the control (the number of dots shown on the
Notice that we also set the contentMode of each Image View to scaleAspectFill screen). It is of type Int.
to fill the entire Image View with the picture and the clipsToBounds property hidesForSinglePage—This property is a Boolean value that
to true to make sure that the image will never be drawn outside the view's determines whether the control should be hidden when there is only
boundaries. one page.
pageIndicatorTintColor—This property sets or returns the color of
Do It Yourself: This example assumes that you are still working the dots when the pages they represent are not visible. It is an
with the interface in Figure 7-5. Update the view controller with the optional of type UIColor.
code in Listing 7-7. Download the images spot1.png, spot2.png, and currentPageIndicatorTintColor—This property sets or returns the
spot3.png from our website and add them to the Assets Catalog. Run color of the dot that represents the visible page. It is an optional of
the application. When you scroll the content, the interface should type UIColor.
transition from right to left or left to right, from one image to backgroundStyle—This property sets or returns the style of the
another. control. It is an enumeration defined in the UIPageControl class called
BackgroundStyle. The possible values are automatic (selects the style
according to the state), prominent (shows a full background), and minimal
(shows a minimal background).
preferredIndicatorImage—This property sets or returns a UIImage
object with the image we want to assign to the indicators.
setIndicatorImage(UIImage?, forPage: Int)—This method sets
the image for the indicator of a specific page. The first argument is the
image we want to assign to the indicator, and the forPage argument is
the page for which the indicator is going to be shown.
To add a Page Control to the interface, we could reduce the height of the
Stack View to make room for it or put the control over the Stack View. For
the following example, we have decided to go with the last option and add
the Page Control on top of the Stack View. The control has a gray
background with an opacity of 0.4 to make it translucent.
imgView.clipsToBounds = true
The Page Control is configured by default for three pages, but we can mainScroll.addSubview(imgView)
change the initial values from the Attributes Inspector panel. This is usually posX = posX + scrollWidth
}
not necessary since the content to display with this type of control is stackContainer.addArrangedSubview(mainScroll)
loaded dynamically and its properties are configured from the view }
controller, as shown in the following example. func scrollViewDidScroll(_ scrollView: UIScrollView) {
let pageWidth = stackContainer.frame.size.width
let getPage = round(mainScroll.contentOffset.x / pageWidth)
Listing 7-8: Calculating the page let currentPage = Int(getPage)

page = currentPage
import UIKit
pageCounter.currentPage = page
}
class ViewController: UIViewController, UIScrollViewDelegate {
}
@IBOutlet weak var stackContainer: UIStackView!

@IBOutlet weak var pageCounter: UIPageControl!
var mainScroll: UIScrollView! To represent the pages on the screen with the Page Control, we not only
var page: Int = 0
have to configure the control but also get the number of the current visible
override func viewDidLoad() { page. The UIScrollView class does not offer any property or method to return
super.viewDidLoad()
this value; we must calculate it ourselves. Fortunately, the delegate
let images = ["spot1", "spot2", "spot3"]
pageCounter.numberOfPages = images.count protocol offers a method called scrollViewDidScroll() that is executed every
pageCounter.pageIndicatorTintColor = UIColor.black time the user scrolls the content. Also, the system automatically updates
pageCounter.currentPageIndicatorTintColor = UIColor.white
the Scroll View's contentOffset property with the current displacement of the
view.setNeedsLayout() content area every time the content is scrolled. By implementing these
view.layoutIfNeeded()
let scrollWidth = stackContainer.frame.size.width
tools, we can calculate the current page. The value is obtained dividing the
let scrollHeight = stackContainer.frame.size.height value of the x property of the contentOffset structure by the size of the page
(the width of the Scroll View). Because the result is a floating-point
mainScroll = UIScrollView(frame: .zero)
mainScroll.contentSize = CGSize(width: scrollWidth * CGFloat(images.count), height: number, we round it, turn it into an integer, and assign it to the Page
scrollHeight) Control's currentPage property to show the current page on the screen.
mainScroll.contentInsetAdjustmentBehavior = .never
mainScroll.isPagingEnabled = true
mainScroll.delegate = self Do It Yourself: Connect the Page Control to an Outlet called
var posX: CGFloat = 0 pageCounter.Complete your view controller with the code in Listing 7-
for img in images { 8 and run the application. The Page Control should indicate the
let imgView = UIImageView(frame: CGRect(x: posX, y: 0, width: scrollWidth, height:
scrollHeight)) current visible page.
imgView.image = UIImage(named: img)
imgView.contentMode = .scaleAspectFill
Letting the user zoom in or out the entire content area when the Scroll mainScroll.delegate = self
View is configured to work with pages would defeat the purpose of pages, var posX: CGFloat = 0
but it is possible to let the user zoom each page individually. Scroll Views for img in images {
let childScroll = UIScrollView(frame: CGRect(x: posX, y: 0, width: scrollWidth, height:
may be created inside other Scroll Views, and we can use this feature to
scrollHeight))
build complex interfaces where a view may have parts of its content childScroll.contentSize = CGSize(width: scrollWidth, height: scrollHeight)
presented with vertical scrolling and other parts with horizontal scrolling. childScroll.contentInsetAdjustmentBehavior = .never
childScroll.minimumZoomScale = 1.0
We can also use this feature to allow zooming a page in or out. All we have childScroll.maximumZoomScale = 4.0
to do is to embed the content of each page inside additional Scroll Views, childScroll.delegate = self
set their maximum and minimum scales, and implement the let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: scrollWidth, height:
viewForZooming() method of the UIScrollViewDelegate protocol to tell the Scroll scrollHeight))
Views that zooming is allowed (see Listing 7-6). imgView.image = UIImage(named: img)
imgView.contentMode = .scaleAspectFill
imgView.clipsToBounds = true
Listing 7-9: Zooming the pages in and out imageViews.append(imgView)
 childScroll.addSubview(imgView)
import UIKit mainScroll.addSubview(childScroll)
posX = posX + scrollWidth
class ViewController: UIViewController, UIScrollViewDelegate { }
@IBOutlet weak var stackContainer: UIStackView! stackContainer.addArrangedSubview(mainScroll)
@IBOutlet weak var pageCounter: UIPageControl! }
func scrollViewDidScroll(_ scrollView: UIScrollView) {
var mainScroll: UIScrollView! let pageWidth = stackContainer.frame.size.width
var imageViews: [UIImageView] = [] let getPage = round(mainScroll.contentOffset.x / pageWidth)
var page: Int = 0
let currentPage = Int(getPage)
override func viewDidLoad() { if currentPage != page {
super.viewDidLoad() let scroll = imageViews[page].superview as! UIScrollView
let images = ["spot1", "spot2", "spot3"] scroll.setZoomScale(1.0, animated: true)
pageCounter.numberOfPages = images.count page = Int(currentPage)
pageCounter.pageIndicatorTintColor = UIColor.black pageCounter.currentPage = page
pageCounter.currentPageIndicatorTintColor = UIColor.white }
}
view.setNeedsLayout() func viewForZooming(in scrollView: UIScrollView) -> UIView? {
view.layoutIfNeeded() return imageViews[page]
let scrollWidth = stackContainer.frame.size.width }
let scrollHeight = stackContainer.frame.size.height }

mainScroll = UIScrollView(frame: .zero)
mainScroll.contentSize = CGSize(width: scrollWidth * CGFloat(images.count), height:
scrollHeight)
The code in Listing 7-9 creates Scroll Views to embed every Image View
mainScroll.contentInsetAdjustmentBehavior = .never and then adds them as subviews of the mainScroll view. Notice that the
mainScroll.isPagingEnabled = true
minimum and maximum scales are set only for the nested Scroll Views, not pageCounter.currentPageIndicatorTintColor = UIColor.white
for the mainScroll view, because we only want to make the zoom available view.setNeedsLayout()
for the Scroll Views assigned to each page. view.layoutIfNeeded()
There is also an aesthetic change inside the scrollViewDidScroll() method. This
mainScroll = UIScrollView(frame: .zero)
method is executed every time the user scrolls the pages, but the scroll mainScroll.contentInsetAdjustmentBehavior = .never
does not always causes the current page to be replaced. We check this mainScroll.isPagingEnabled = true
mainScroll.delegate = self
condition comparing the current page with the previous one and set the
zoom scale back to 1.0 if the new page is different. This returns the images for img in images {
let childScroll = UIScrollView(frame: .zero)
to the initial state every time the user moves to another page. childScroll.contentInsetAdjustmentBehavior = .never
The examples we have studied so far create applications that let the user childScroll.minimumZoomScale = 1.0
transition from one image to another with the move of a finger. But this childScroll.maximumZoomScale = 4.0
childScroll.delegate = self
presents a problem. Because the size of the pages is determined from the
size of the Scroll View, the application does not work anymore if the device let imgView = UIImageView(frame: .zero)
imgView.image = UIImage(named: img)
is rotated, or the size of the Scroll View changes for some reason. To solve imgView.contentMode = .scaleAspectFit
this issue, we must implement the methods defined for Trait Collections to imgView.clipsToBounds = true
imageViews.append(imgView)
detect and report changes in size (see Chapter 6, Listing 6-10). The
following example implements the viewWillTransition() method to update the childScroll.addSubview(imgView)
frames of the Image Views and their Scroll Views when the device is mainScroll.addSubview(childScroll)
}
rotated. stackContainer.addArrangedSubview(mainScroll)
updateSize()
Listing 7-10: Adapting the content area to a new orientation }
 func updateSize() {
import UIKit let scrollWidth = stackContainer.frame.size.width
let scrollHeight = stackContainer.frame.size.height
class ViewController: UIViewController, UIScrollViewDelegate {
@IBOutlet weak var stackContainer: UIStackView! var posX: CGFloat = 0
@IBOutlet weak var pageCounter: UIPageControl! for imgView in imageViews {
let scroll = imgView.superview as! UIScrollView
var mainScroll: UIScrollView! scroll.frame = CGRect(x: posX, y: 0, width: scrollWidth, height: scrollHeight)
var imageViews: [UIImageView] = [] scroll.contentSize = CGSize(width: scrollWidth, height: scrollHeight)
var page: Int = 0 imgView.frame = CGRect(x: 0, y: 0, width: scrollWidth, height: scrollHeight)
var rotating = false posX = posX + scrollWidth
}
override func viewDidLoad() { mainScroll.contentSize = CGSize(width: scrollWidth * CGFloat(imageViews.count), height:
super.viewDidLoad() scrollHeight)
let images = ["spot1", "spot2", "spot3"]
pageCounter.numberOfPages = images.count let scrollView = imageViews[page].superview as! UIScrollView
pageCounter.pageIndicatorTintColor = UIColor.black mainScroll.contentOffset = CGPoint(x: scrollView.frame.origin.x, y: 0)
} Every time the system detects a rotation or changes on the interface, it
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if !rotating { calls the viewWillTransition() method. In this method, we call the animate()
let pageWidth = stackContainer.frame.size.width method of the transition coordinator to update the values of the frames as
let getPage = round(mainScroll.contentOffset.x / pageWidth)
let currentPage = Int(getPage)
soon as the transition is over (the statements were declared inside the
closure for the completion handler). There are two more important things
if currentPage != page { we do inside this method: we restore the zoom level of the current page
let scroll = imageViews[page].superview as! UIScrollView
scroll.setZoomScale(1.0, animated: true) back to its initial scale and modify the value of a Boolean property called
page = Int(currentPage) rotating to inform the rest of the code that the interface is rotating. This is
pageCounter.currentPage = page
}
because we do not want to calculate the number of the current page inside
} the scrollViewDidScroll() method until all the values for the frames are
} updated. The updateSize() method also updates the value of the contentOffset
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return imageViews[page] property of the mainScroll view at the end to scroll the content to the right
} position after the rotation (when the device is rotated, the value of this
override func viewWillTransition(to size: CGSize, with coordinator:
UIViewControllerTransitionCoordinator) { property is not updated by the system).
super.viewWillTransition(to: size, with: coordinator)
rotating = true
Do It Yourself: Copy the code you want to try inside your view
coordinator.animate(alongsideTransition: nil, completion: {(context: controller and run the application. In the example of Listing 7-10, we
UIViewControllerTransitionCoordinatorContext!) in
let scroll = self.imageViews[self.page].superview as! UIScrollView defined the value of the contentMode property as scaleAspectFit so the
scroll.setZoomScale(1.0, animated: true) user can see the entire images on the screen. Try other values to find
self.updateSize() out which one is better for your application.
self.rotating = false
})
}
}

Scroll Views allow us to display any content that does not fit entirely on the
screen, including the interface itself. There are times when our interface
contains so many elements and controls that there is not enough space to
show them all, some get hidden when the device is rotated, or are
overlapped by the keyboard. There are different ways to define the 
interface inside a Scroll View, but the simplest is to add a single view inside
the Scroll View and then use this view as the container for the rest of the Do It Yourself: Create a new project. Add a Scroll View to the scene
interface. Figure 7-10 illustrates this process. The Scroll View is added to and expand it to the edges of the main view (Figure 7-10, left). Select
the main view (left), then a single view is added to the Scroll View (center), the Scroll View, open the Pin menu and create Leading, Trailing, Top,
and finally the view is expanded to fill the Scroll View. and Bottom constraints to pin it to the Safe Area. Add an empty view
inside the Scroll View, expand it to occupy the Scroll View’s area
Figure 7-10: Scroll View with an empty view inside
(Figure 7-10, right), and create Leading, Trailing, Top, and Bottom
constraints to pin it to the sides of the Scroll View (make sure that
the destination is the Scroll View). The constraints are shown in red
because the system does not have enough information to determine
the size of the content area yet.
Now that the Scroll View and the container are ready, we must create the
 constraints necessary to determine the size of the scrollable content. The
vertical size is determined by the elements inside the view and their
Both views must be pinned to the edges of their respective containers with constraints, but the horizontal size is determined by the space available.
Top, Bottom, Leading and Trailing Space constraints. The Scroll View can be This requires the creation of two more constraints that pin the container
pinned to the Safe Area and the view inside the Scroll View must be pinned view to the left and right sides of the main view. Figure 7-12 illustrates the
to the edges of the Scroll View, as we did for the Image View in the process. We have to control-drag a line from the view inside the Scroll View
example of Figure 7-3. Figure 7-11, below, shows all the constraints to the main view in the Document Outline panel and create Leading and
required. Trailing constraints with a value of 0.
Figure 7-11: Initial constraints for the views Figure 7-12: Additional constraints for the container view
 
Every time the device is rotated or the space available changes, these These interfaces are usually bigger than the standard size of the scenes.
constraints change the width of the view inside the Scroll View and this Xcode offers the alternative to set a specific size for the scene, so we can
new size is communicated to the Scroll View by the Space constraints design longer interfaces in the Storyboard. The tool is called Simulated Size
added before. and it is available in the Size Inspector panel when the scene is selected. It
includes two options: Fixed and Freeform. When we select Freeform, the
Do It Yourself: Control-drag a line from the view inside the Scroll panel allows us to change the values of the main view’s width and height,
View to the item that represents the main view in the Document as shown next.
Outline panel (Figure 7-12). Create a Leading and a Trailing
constraint with a value of 0. Figure 7-14: Simulated Size tool
the screen returns are the position of the fingers, not information on how relative to the starting position, the method velocity(in: UIView?) to
the fingers were moved. This is the reason why Apple introduced gesture return the velocity of the gesture expressed in points per second, and
recognizers. A gesture recognizer is an object that performs all the the properties maximumNumberOfTouches and minimumNumberOfTouches to
necessary calculations to detect a gesture. The UIKit framework defines a set the number of fingers that must touch the view for the gesture to
base class called UIGestureRecognizer to provide the basic functionality for be recognized.
gesture recognizers and multiple subclasses with additional properties and UIScreenEdgePanGestureRecognizer—This class creates a
methods to create recognizers for specific gestures. gesture recognizer that recognizes a panning gesture that starts near
the edge of the screen. The class includes the edges property to
UITapGestureRecognizer—This class creates a gesture recognizer determine the edges that recognize the gesture. The property is a
that recognizes single or multiple taps. The class includes the integer structure of type UIRectEdge with the properties top, left, bottom, right,
properties numberOfTapsRequired and numberOfTouchesRequired to and all.
determine the number of taps that need to occur and the number of UILongPressGestureRecognizer—This class creates a gesture
fingers that have to participate for the gesture to be recognized. recognizer that recognizes a long-press gesture (the user presses one
UIPinchGestureRecognizer—This class creates a gesture or more fingers on the screen for a certain period of time). The class
recognizer that recognizes a pinching gesture (zoom in or out). The includes the properties minimumPressDuration (a Double value to indicate
class includes the properties scale and velocity to set a scaling factor and the minimum time the fingers must press the screen),
return the velocity of the pinch. The properties are of type CGFloat. numberOfTouchesRequired (the number of fingers that must touch the
UIRotationGestureRecognizer—This class creates a gesture screen), numberOfTapsRequired (the number of taps), and
allowableMovement (a CGFloat value that indicates the movement
recognizer that recognizes a rotating gesture. The class includes the
properties rotation and velocity to set the rotation in radians and return allowed).
the velocity in radians per second. They are of type CGFloat.
The UIGestureRecognizer class provides an initializer to create objects from
UISwipeGestureRecognizer—This class creates a gesture every subclass and also general properties and methods. The following are
recognizer that recognizes a swiping gesture. The class includes the
the most frequently used.
properties direction and numberOfTouchesRequired to set the direction
allowed and the number of fingers that must touch the screen for the
init(target: Any?, action: Selector?)—This initializer creates a
gesture to be recognized. The direction property is a structure (or a set
gesture recognizer configured with the target and the action specified
by the arguments. The target argument is the object where the action
will be executed, and the action argument is the method to be
executed when the gesture is detected.
state—This property returns the current state of the gesture
recognizer. It is an enumeration called State included in the
UIGestureRecognizer class. The values available are possible, began, changed,
ended, cancelled, failed, and recognized.
Figure 8-1: Gesture Recognizer options in the Library Gesture recognizers are added to the Storyboard as any other element. All
we need to do is to drag the gesture recognizer from the Library and drop
it on the view we want to recognize the gesture. Xcode adds an icon to the
top of the scene to represent it. Figure 8-3 shows what the icon looks like
after we add a Pan Gesture to an Image View on the interface.
Each gesture returns different kind of information. In the case of the pan
gesture, the object includes a few methods that calculate and return the
current position and velocity of the gesture. Every time a gesture begins, or
a change occurs on its state, the object calls the Action method with a
reference to itself (sender: UIPanGestureRecognizer). From this reference, we can
read and process the information provided by the gesture object and
perform custom tasks. In Listing 8-1, we call the translation() method to get

the value of the current position and calculate the difference between the
old horizontal position and the new one. From this difference and the
The view controller needs an Outlet for the Image View and an Action for
width of the image, we can get a proportional value that added to the
the gesture. To create the Action, we must control-drag a line from the
alpha value allows us to associate the movement of the finger with
gesture’s icon (Figure 8-3) or from the item that represents the gesture in
changes in the translucency of the picture.
the Document Outline panel.
Because the limits of the gesture’s position may vary, we check that the Gesture recognizers may be added to any view from code. The object is
alpha value obtained from those numbers does not go over 1.0 or below created first with the UIGestureRecognizer initializer and then configured and
0.1 (at 0.0 the Image View does not recognize the gesture anymore), and added to the view. The UIView class offers the following methods to manage
then assign it to the alpha property of the Image View to reflect the gesture recognizers.
changes. To keep track of the old position and be able to compare it with
the new one, we store it in the previous property. Every time the action is addGestureRecognizer(UIGestureRecognizer)—This method
called to report changes in the gesture, the new value is stored in this adds a gesture recognizer to the view. The argument is an object of a
property. But because the position of a pan gesture is considered to be 0 at UIGestureRecognizer subclass.
the point when the gesture begins, we cannot keep using the values of a removeGestureRecognizer(UIGestureRecognizer)—This
previous gesture and therefore the value of the previous property must method removes a gesture recognizer from the view. The argument is
return to 0 when the gesture is over. This is detected at the end by reading a reference to the gesture added before with the addGestureRecognizer()
the value of the gesture’s state property. method.
Do It Yourself: Create a new project. Download the husky.png Gesture recognizers implement an old protocol from Objective-C to define
image from our website and add it to the Assets Catalog. Add an the action to perform when the gesture is recognized. This protocol is
Image View to the main view and assign the image of the husky to it. called target/action because it requires the specification of the object that
In the same panel, activate the option called User Interaction is going to respond to the event (the target) and a method that will be
Enabled for the Image View (Figure 8-2). Drag the option Pan executed when the gesture occurs (the action). The target is a reference to
Gesture from the Library and drop it over the Image View. You the object, usually specified as self (the object in charge of the response is
should see the icon pictured in Figure 8-3. Create an Outlet for the the same object that initialized the gesture recognizer) and the method is
image called picture. Control-drag a line from the gesture’s icon to the specified with a selector. Selectors are declared with the instruction
#selector(method) and the syntax for the method must include the names of its
view controller to create an Action called fadingOut(). Complete the
parameters followed by a semicolon, as in #selector(showCounter(sender:)).
view controller with the code in Listing 8-1 and run the application.
Because this is an old Objective-C feature, it also requires the methods to
The picture should fade in and out when you move your finger from
be prefixed by the @objc keyword.
side to side. The following example adds a Tap gesture to a Scroll View to zoom in and
out to specific points of the image. This code assumes that we have added
IMPORTANT: Gesture recognizers cannot be added to different a Stack View to the initial scene and pin it to the Safe Area, as we did for
views. If we want to use the same recognizer for different views, we some of the examples in the previous chapter.
must create new objects, but several gesture recognizers of the
same type may be connected to the same Action. Listing 8-2: Adding gestures from code

import UIKit
and create a method called zoomPicture() to perform the action (Notice that
class ViewController: UIViewController, UIScrollViewDelegate {
@IBOutlet weak var stackContainer: UIStackView! the method was prefixed by the @objc keyword to be able to call it from a
selector).
var mainScroll: UIScrollView!
This example follows the procedure studied in Chapter 7 to configure the
var image: UIImageView!
var zooming = false Scroll View and allow the user to scroll and zoom the picture. When the
Scroll View is ready, the code creates a UITapGestureRecognizer object and
override func viewDidLoad() {
super.viewDidLoad() adds it to the view with the addGestureRecognizer() method. In consequence,
image = UIImageView(image: UIImage(named: "doll")) every time the user taps on the Scroll View, the zoomPicture() method is
mainScroll = UIScrollView(frame: .zero)
executed. In this method, we zoom in or out, depending on the value of
mainScroll.contentSize = CGSize(width: image.frame.size.width, height: the zooming property. To zoom in, the code gets the position of the tap in
image.frame.size.height) the view from the location() method and then zooms in to a rectangle in that
mainScroll.minimumZoomScale = 1.0
mainScroll.maximumZoomScale = 4.0 position. To zoom out, we just set the scale back to 1.0.
mainScroll.delegate = self
mainScroll.addSubview(image)
Do It Yourself: Create a new project. Add a Stack View to the main
let gesture = UITapGestureRecognizer(target: self, action: #selector(zoomPicture)) view and pin it to the Safe Area. Connect the Stack View with an
mainScroll.addGestureRecognizer(gesture)
Outlet called stackContainer. Download the doll.jpg image from our
stackContainer.addArrangedSubview(mainScroll) website and add it to the Assets Catalog. Complete the view
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? { controller with the code in Listing 8-2. Run the application and tap
return image on the image. The image should zoom in the area around your finger.
}
@objc func zoomPicture(sender: UITapGestureRecognizer) {
if !zooming {
let position = sender.location(in: mainScroll)
mainScroll.zoom(to: CGRect(x: position.x, y: position.y, width: 1, height: 1), animated: true)
zooming = true
} else {
mainScroll.setZoomScale(1.0, animated: true)
zooming = false
}
}
}

Apps that only require one scene and a simple interface are rare these
days. Because of the limited space, developing apps for mobile devices
demands the creation of multiple scenes to represent virtual screens that
replace one another in response to the user (see Figure 5-18). Practical
applications contain multiple scenes connected with each other following a
predetermined path that users can take to navigate and get the
information they need.
Xcode templates, like the App template we have been using so far to create
our projects, provide initial scenes to start from. The rest of the scenes are
added later to the Storyboard as needed from options included in the
Library. The option to add single scenes that are controlled by
UIViewController objects is called View Controller.
Dragging this option to the Storyboard creates a new scene with its own
main view. After the scenes are added, they can be dragged from the top
bar to different positions to represent what users will see when they
navigate the interface. Figure 9-2 shows what the Storyboard looks like
after we add a new scene and move it to the right side of the initial scene.
There are several options available. The UIViewController class is part of the
 Cocoa Touch API, therefore, to create a file with a UIViewController subclass
for the new scene, we must click on the Cocoa Touch Class option in the
We can add all the scenes we need to build our interface. Every scene Source panel (Figure 9-3, number 1 and 2). Another window opens to
represents a new screen, and their main views will be shown on the insert the name and select the class we want to create our subclass from.
device’s screen one at a time.
Figure 9-4: Creating the subclass
Do It Yourself: Create a new project. Drag the View Controller
option from the Library to the Storyboard. Arrange the scenes to
look like Figure 9-2.
The option to add a new scene to the Storyboard is called View Controller
because it adds a scene that is controlled by an object created from a 
subclass of the UIViewController class, but the view controller is not included
along with the scene, we ought to create the file ourselves. The first value is the name of the class (Figure 9-4, number 1). This value is
Files are created from the File menu. If we click on the File menu and go going to be used as the name of the subclass and the file. It is good
over the New option, a submenu shows the option File. Once we click on practice to start with a word that describes the purpose of the scene and
this option, a window is opened to select the type of file we want to create end with the words "ViewController" to reflect the type of class declared
(we can also use a shortcut pressing the Command + N keys). inside the file (for this example, we called it SecondViewController). The
next option is the superclass of our subclass. For scenes with a single view
this is the UIViewController class.
The last two options determine if the process will create a new scene
(this is not necessary because we have already created the scene in the
Storyboard), and the language Xcode is going to use to create the file (the 
last language we use is always shown by default). After the information is
ready, the file is created and included with the rest of the files in the Now, the second scene has its own view controller, and we are ready to go.
Navigator Area. The procedure to create the interface for this scene and connect it to our
At this point, we have the scene in the Storyboard and the file with its view code is the same as before. We must drag the elements we want to add to
controller, but they are not connected. Xcode does not assign the new file the main view and then connect them to the code in their respective view
to the scene automatically; we must do it ourselves from the Identity controllers (ViewController for the scene on the left and SecondViewController for
Inspector panel. This is one of the configuration panels available in the the scene on the right).
Utilities Area when the scene is selected. To open the panel, select the
scene and click on the fourth button at the top of the Utilities Area (circled Do It Yourself: Create the subclass for the second scene named
in Figure 9-5). SecondViewController.
Select the second scene by clicking on the bar at
the top or on the view controller icon (Figure 5-20). Open the
Figure 9-5: Identity Inspector panel Identity Inspector panel in the Utilities Area (Figure 9-5) and assign
the subclass you have just created to the scene (Figure 9-5, number
1). Open the Assistant Editor (Figure 5-29) and click on the scenes in
the Storyboard to select them. You should see the selected scene on
the left and the corresponding view controller on the right of the
Editor area.
The subclass that controls the scene is specified by the first option (Figure
9-5, number 1). We can write the name of the class or select it from the
list. Figure 9-6, below, illustrates what the panel looks like after we select
the SecondViewController class.
one scene to another, we must create a segue from the button Show Husky the scene. In this example, we have decided to use a Modal Segue (also
to the second scene. The process is illustrated below. known as Present Modally).
Figure 9-8: Segue from a button to the second scene Figure 9-10: Segue in the Storyboard
The line is control-dragged from the button to the second scene. When the

mouse button is released, Xcode opens a menu to select the type of segue
we want to create.
IMPORTANT: Modal and Popover Segues are implemented in small
applications or to open scenes that present additional information.
Figure 9-9: Popup menu listing the type of segues available
Later in this chapter, we will learn how to create more complex and
professional interfaces with Navigation Controllers and Show segues.
After adding the segue, the Storyboard shows two arrows, one pointing to
the scene on the left (Figure 9-10, number 1) and another connecting the
scenes (Figure 9-10, number 2). The single arrow on the left indicates what
scene is going to be shown first when the app is executed, and the arrow in
between is pointing to the scene that is going to be shown when the segue
is triggered (the button is pressed). The arrows illustrate the path or
possible paths the user can follow while interacting with our application,
 which is the main function of the Storyboard; to help us create a story.
Independent scenes like those implemented in this example can only be Do It Yourself: Add a label and two buttons called Show Husky and
connected using a Modal Segue (Present Modally) or a Popover Segue Show Door to the initial scene. Add a Tinted button called Close, the
(Present As Popover). What segue to implement depends on the label "Rating", a Slider, and an Image View to the second scene.
requirements of our application and the type of information contained in From the Utilities Area, set the value of the Slider to 0, the minimum
to 0, and the maximum to 5. Add the image husky.png to the Assets Transition menu with the types available. Selecting any of these options
Catalog and assign it to the Image View. Apply all the necessary changes the way the destination scene is presented.
constraints to get an interface like Figure 9-7 (remember to assign
higher Content Hugging and Content Compression priorities to the
button or the Image View if necessary). Control-drag a line from the
button Show Husky to the second scene (Figure 9-8). Select the
Present Modally segue. Run your app and press the Show Husky
button. You should see the second scene sliding from the bottom of
the screen to the top.
Modal Segues were introduced for the purpose of connecting single views
and presenting additional information, but still offer a few options for
configuration. Among those options is a list of transitions we can choose
from. The transition by default is called Cover Vertical and it slides the
destination view from bottom to top, but we can change it from the
Utilities Area. When we click on a segue in the Storyboard, the Utilities
Area presents a configuration panel to edit the segue.
From this panel, we can specify a name to reference the segue from code
(Identifier), set our own custom segue (Class), or select the type of segue,
presentation, and transition we want. The picture on the right shows the
Unwind Segues

Modal Segues allow us to move forward in the Storyboard, but we can also
move backward with a special kind of segue called Unwind Segue. To set up
these segues we must follow a few unconventional steps. First, we must
write an Action on the view controller that presents the next scene, and
then connect to that Action the element in the second scene that triggers 
the Unwind Segue. This is because the view controller responsible for
transitioning back to the previous scene is the one that presented the In Figure 9-12, the Close button inside the second scene is connected to
destination scene. In our example, this is the ViewController class. the Exit icon. When the mouse is released, a menu shows the Actions
So far, we have let Xcode generate the code for the @IBAction, but we can available in the ViewController class.
also write it ourselves and then create the connection, as shown next.
Figure 9-13: Menu to select the Action for the Unwind Segue
Listing 9-1: Adding the Action for the Unwind Segue

import UIKit
The Action in our ViewController class was declared with no statements. This import UIKit
is enough to perform the Unwind Segue, but we can take advantage of this class SecondViewController: UIViewController {
method and its parameter to get information about the view controllers @IBOutlet weak var sliderRating: UISlider!
var rating: Int = 0
that participate in the transition. The UIStoryboardSegue class includes the
following properties for this purpose. override func viewDidLoad() {
super.viewDidLoad()
sliderRating.value = Float(rating)
source—This property returns a reference to the view controller that }
@IBAction func changeRating(_ sender: UISlider) {
is displayed at the beginning of the transition. The value returned is a let value = round(sender.value)
generic UIViewController object that we must cast to the corresponding sliderRating.value = value
rating = Int(value)
type. }
destination—This property returns a reference to the view }

controller that is displayed at the end of the transition. The value
returned is a generic UIViewController object that we must cast to the The code in Listing 9-2 adds an integer property called rating to the
corresponding type. SecondViewController class to keep track of the rating set by the user. The code
identifier—This property returns the string we used in Interface also includes an Outlet for the Slider to set its initial value equal to the
Builder to identify the segue (see Figure 9-11). Using this property, we value of rating and an Action to update the value of this property every time
the Slider is moved (see UISlider in Chapter 5).
know what segue was triggered and can get information about the
Because the Slider works with consecutive floating-point values and our
scene that is going to be shown on the screen.
rating is established by integers between 0 and 5, before assigning the
selected value to the rating property we must round the number to the
nearest integer with the round() function and assign it back to the Slider to
reflect the right rating on the screen. This makes the indicator jump from previously selected by the user, we must send the value of the ratingHusky
one integer to another, helping the user identify the rating that wants to property in the ViewController object back to the SecondViewController object
assign to the picture. every time the Show Husky button is pressed. The UIViewController class
Now that we have the rating property, we can read it from the Unwind provides the following methods for this purpose.
Segue's Action in the ViewController class.
prepare(for: UIStoryboardSegue, sender: Any?)—This method is
Listing 9-3: Reading properties from the source called by the system in the view controller object when a segue is
 triggered and before the transition is initiated. We can override this
import UIKit method in our view controller to access the destination view
class ViewController: UIViewController { controller and modify its properties.
var ratingHusky: Int = 0
performSegue(withIdentifier: String, sender: Any?)—This
@IBAction func goBack(_ segue: UIStoryboardSegue) { method triggers the segue identified with the string specified by the
let controller = segue.source as! SecondViewController
ratingHusky = controller.rating withIdentifier argument. The sender argument is the object that
} triggers the segue (usually declared as self).
}

The method we need to implement to send the information to the second
From the source property of the UIStoryboardSegue object received by the view controller is prepare(). This is one of those methods that are called by
Action, we get a reference to the view controller that is being removed and the system in the view controller and can be overridden to perform custom
access its properties. In our example, this is the SecondViewController class. tasks. In this case, we can override it to access the view controller that is
We get the value, cast it as SecondViewController, and store it in the controller going to be opened and modify its properties before its main view is shown
constant. Now that we have access to the view controller of the second on the screen. The following example expands the code in our ViewController
scene, we read its rating property and store its value in the ratingHusky class to update the value of the rating property in the SecondViewController
property of the ViewController class. Copying the value to a property in the class before the transition is performed.
ViewController object is necessary because the SecondViewController object and
all its properties are destroyed as soon as the scene is removed. Listing 9-4: Sending values to the second view controller before its main
This is the first part of the process; we let the user select a rating and when view is loaded
it moves back to the menu screen, we preserve the selected value in the 
ratingHusky property. If we run the application at this moment and move the import UIKit
Slider to specify a rating, once we go back to the initial scene and tap the
class ViewController: UIViewController {
Show Husky button a second time, the Slider is again in its initial position. var ratingHusky: Int = 0
This is because the value of the Slider is always set as the value of the rating
@IBAction func goBack(_ segue: UIStoryboardSegue) {
property when the main view is loaded. To get the Slider to show the value let controller = segue.source as! SecondViewController
ratingHusky = controller.rating to the Storyboard and connect it to the Show Door button using another
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { Modal Segue, as shown below.
let controller = segue.destination as! SecondViewController
controller.rating = ratingHusky
Figure 9-14: Interface with three scenes
}
}

Now the process is complete. The prepare() method updates the value of the
rating property with the value of the ratingHusky property before the second
scene is shown on the screen, and this value is used by the
SecondViewController object to update the Slider as soon as the main view is
loaded. When the user taps the Close button, the goBack() Action is
executed in the ViewController class and the process is reversed (the 
ratingHusky property is updated with the current value of the rating
property). In consequence, the rating set by the user is always preserved in The process to add the third scene is the same as before; we drag the View
the ratingHusky property inside the view controller of the initial scene and it Controller option to the Storyboard and build the interface. Like the second
is used to update the Slider every time the second scene is opened. scene, this one also needs its own view controller. For this example, we call
it ThirdViewController. Because we use the same type of elements and
Do It Yourself: Copy the code in Listing 9-4 in your ViewController process the same information, the code for this view controller is the same
class. Create an Outlet called sliderRating and an Action called defined for the SecondViewController class in Listing 9-2, including the Outlet
changeRating() for the Slider in the second scene. Complete the and Action for the Slider. What changes is how the ViewController class
SecondViewController class with the code in Listing 9-2. Run the manages the information that is coming from and going to these view
application. The Slider should always reflect the rating set by the controllers. Now we have two segues, one that opens the scene with the
user. husky and another that opens the scene with the door, and therefore the
code in the ViewController class must recognize the segue that is being
IMPORTANT: Never try to access Outlets of one view controller triggered to know how to proceed. This is the reason why the segue’s
from another. Always transfer data through normal properties configuration panel includes an option called Identifier (Figure 9-11). The
because there is no guarantee that the Outlets will be connected to string provided to this option is assigned to the identifier property of the
UIStoryboardSegue object and that is how we know which segue was
the objects in the Storyboard until the main view is fully loaded.
triggered.
The same procedure can be applied to the second button of the menu to As explained before, to change the segue’s configuration we must select it
let the user select another image. All we need to do is to add a new scene and open the Utilities Area (Figure 9-11). For normal segues it is easy, we
select them on the Storyboard and modify their values from the Attributes
Inspector panel, but the only way to select an Unwind Segue is from the let controller = segue.source as! ThirdViewController
ratingDoor = controller.rating
Document Outline panel. The items in this panel that represent Unwind }
Segues are preceded by a round icon, as illustrated below (number 1). }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showHusky" {
Figure 9-15: Unwind segue in the Document Outline panel let controller = segue.destination as! SecondViewController
controller.rating = ratingHusky
} else if segue.identifier == "showDoor" {
let controller = segue.destination as! ThirdViewController
controller.rating = ratingDoor
}
}
}

The initial scene for this new application contains a Picker View and a
button. When the button is pressed, the code for this view controller
executes the performSegue() method to trigger a segue that transitions to the
second scene. There are different ways to create this segue. The simplest is
to control-drag a line from the view controller's icon to the second scene, ratings = [0, 0]
selectedPicture = 0
as shown below. }
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
Figure 9-17: Creating a segue from one scene to another
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) ->
Int {
return picturesList.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component:
Int) -> String? {
return picturesList[row]
 }
@IBAction func getPicture(_ sender: UIButton) {
selectedPicture = pickerPictures.selectedRow(inComponent: 0)
IMPORTANT: Segues that are not associated with an element must performSegue(withIdentifier: "showPicture", sender: self)
always have an identifier, so they can be referenced from code (see }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
Figure 9-11). For this example, we are going to call it "showPicture". let controller = segue.destination as! SecondViewController
controller.rating = ratings[selectedPicture]
controller.picture = picturesList[selectedPicture]
No matter how the segue is triggered, the system always calls the prepare() }
method before performing the transition. In the following view controller, @IBAction func goBack(_ segue: UIStoryboardSegue) {
let controller = segue.source as! SecondViewController
we use this method to report to the second scene which was the item
ratings[selectedPicture] = controller.rating
selected on the picker. With this information, the second scene loads the }
corresponding image and sets the Slider to display the rating. }

Listing 9-6: Storing the data Most of the code in Listing 9-6 is composed of the delegate methods that

configure the picker (see UIPickerView in Chapter 5), but we also have the
import UIKit
goBack() action implemented before for the Unwind Segue and the prepare()
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource { method to send the information to the second scene when the segue
@IBOutlet weak var pickerPictures: UIPickerView! created in Figure 9-17 is triggered. To store the data, we have created three
var picturesList: [String]!
var ratings: [Int]! properties: picturesList, ratings, and selectedPicture. The picturesList property is an
var selectedPicture: Int! array of strings to store the names of the available pictures, the ratings
override func viewDidLoad() { property is an array of integers to store the rating of each picture, and the
super.viewDidLoad() selectedPicture property is a single integer that stores the index of the
pickerPictures.delegate = self
selected picture. This last property is used to keep track of the picture we
pickerPictures.dataSource = self
are currently working with. When the prepare() method sends the
picturesList = ["Husky", "Door"]
information to the second scene, it takes the values from the picturesList and @IBOutlet weak var sliderRating: UISlider!
@IBOutlet weak var pictureView: UIImageView!
ratings arrays corresponding to the index determined by the selectedPicture var rating: Int = 0
property. Likewise, when the goBack() method processes the rating set by var picture: String!
the user, it stores its value in the position of the ratings array corresponding override func viewDidLoad() {
to the selectedPicture property. super.viewDidLoad()
The interface includes the Show Picture button to select the picture. This sliderRating.value = Float(rating)
pictureView.image = UIImage(named: picture.lowercased())
button has been connected with an Action called getPicture() that gets the }
value selected in the picker, assigns it to the selectedPicture property, and @IBAction func changeRating(_ sender: UISlider) {
let value = round(sender.value)
then executes the performSegue() method to trigger the segue and load the sliderRating.value = value
second scene (the segue was identified with the name "showPicture"). rating = Int(value)
Unwind Segues can also be created from the view controller to the Exit }
@IBAction func goBack(_ sender: UIButton) {
button and then triggered by code. To create an Unwind Segue that is not performSegue(withIdentifier: "goBack", sender: self)
connected to an element, we must control-drag a line from the view }
}
controller's icon at the top of the scene to the Exit button, as shown below. 
Figure 9-18: Creating an Unwind Segue for a view controller Besides triggering the Unwind Segue, the code in Listing 9-7 introduces
additional changes necessary to process the image selected in the initial
scene. The view controller includes a new property to store the name of
the image selected by the user and load its file (picture). This property is
used to load the file, create the UIImage object, and assign it to the Image
View. Notice that the value of the picture property was lowercased to match
 the names of the images.
The view controller for the second scene now has to execute the Do It Yourself: Create a new project. Add a second scene with a
performSegue() method to trigger the Unwind Segue when the user wants to
class called SecondViewController. Design the interface according to
go back to the previous scene. To this end, we have included an Action
Figure 9-16. The initial scene includes a Picker View and a button,
connected to the Close button in the SecondViewController class (the Unwind
and the second scene is the same implemented for previous
Segue was identified with the string "goBack").
examples. Create a Present Modally segue from the initial scene to
Listing 9-7: Showing the data the second scene (Figure 9-17). Assign the identifier "showPicture"
 to this segue. Create an Unwind Segue for the second scene as
import UIKit shown in Figure 9-18. Open the Document Outline panel, select this
segue, and give it the identifier "goBack". Copy the code in Listing 9-
class SecondViewController: UIViewController {
6 into the ViewController.swift file and the code in Listing 9-7 into 9.2 Navigation Controllers
the SecondViewController.swift file. Connect the Slider and the

Image View to their respective Outlets, and the Slider and the Close
button to their respective Actions. Run the application. You should The view controllers used so far are called Content View Controllers
be able to select the pictures from the picker and change their because they manage their own content. They are created from custom
ratings. subclasses of the UIViewController class and can present and control a single
scene. For this reason, these view controllers have limitations when it
comes to replacing their own scene by another or providing the means to
organize the interface. This is the reason why the UIKit framework includes
several subclasses of UIVIewController that introduce better alternatives to
work with multiple scenes. The view controllers created from these classes
are called Container View Controllers because they contain other view
controllers. The most widely used is the Navigation Controller, created
from the UINavigationController class. This class creates a container view
controller that organizes the scenes in sequential order, as shown below.
Navigation Controllers in the Storyboard Controller and a segue connects this controller to the initial scene. This
segue is called Root View Controller because it indicates which is the first

scene displayed by the Navigation Controller. When the app is executed,
the system loads the Navigation Controller first and then looks for its root
There are two ways to add a Navigation Controller to the Storyboard. If we
view controller to show the initial scene on the screen.
have already designed the interface, Xcode offers an option to embed the
scenes inside a Navigation Controller. We must select the scene that we
IMPORTANT: If you opt for the option to add the Navigation
want to be the initial scene for the Navigation Controller and then go to
Controller from the Library, there is an additional step you must
the Editor menu at the top of the screen, open the option Embed In, and
follow. When the initial scene is deleted and a new one is added to
select Navigation Controller (Editor/Embed In/Navigation Controller). The
the Storyboard, the arrow pointing to the initial scene disappears. To
other alternative is to drag the Navigation Controller option from the
get back the arrow, you must select the scene you want to be the
Library. This option creates the Navigation Controller along with its initial
initial scene (in this case, the Navigation Controller), go to the
scene. Figure 9-21 illustrates what a single scene looks like after it is
Attributes Inspector panel, and activate the option Is Initial View
embedded in a Navigation Controller.
Controller.
Figure 9-21: Initial scene embedded in a Navigation Controller
More scenes may be added to the interface as before, but instead of using
the Present Modally segue to connect the scenes, we must create segues
of type Show. Figure 9-22, next, illustrates what we see when we add a
second scene and connect it using this segue. The initial scene includes a
label with the text "Root View" and a button with the title "Open Second
View". The button was connected to the second scene with a Show segue
(the first option of the popup menu). When the user presses the button,
the second scene is shown on the screen transitioning from right to left.
The scene on the left represents the Navigation Controller and the scene

on the right is our initial scene. The arrow is now pointing to the Navigation
The Show segue performs the transition to move from one scene to Navigation Bar
another, but the Navigation Controller takes care of the rest. It adds a

Navigation Bar at the top that we can use to identify the scenes and a Back
button to each scene to allow the user to return to the previous one. All
As we just mentioned, the Navigation Controller introduces a Navigation
the functionality for this button is already implemented by the controller,
Bar at the top of every scene to help the user navigate. Navigation Bars are
so unless we want to customize the process, there is nothing else we need
just empty views. Their content is not generated by the bar itself but by
to do to allow the user to navigate back and forward.
objects of the UINavigationItem class. In turn, these objects are containers for
other views that represent things like the title of the scene and buttons, as
Do It Yourself: Create a new project. Select the scene, open the
shown below.
Editor menu at the top of the screen, go to Embed In, and select the
option Navigation Controller. The Storyboard should look like Figure Figure 9-23: Elements of the Navigation Bar
9-21. Drag the View Controller option from the Library to add
another scene to the Storyboard. Add a label and a button to the
initial scene and a label to the second scene, as illustrated in Figure
9-22 (Make sure that the elements are positioned below the
Navigation Bar at the top). Control-drag a line from the button in the
initial scene to the second scene to create a Show segue. Create a 
file with the view controller for the second scene called
SecondViewController, as we did before. Run the application and press Navigation Items are created automatically by the system along with the
the button. Press the Back button to go back to the initial scene. Navigation Bar, but we can remove them and add them again from the
Library if necessary.
Navigation items may include a title, a prompt, and buttons on the sides.
As we have seen in the previous section of this chapter, one button is
automatically generated by the Navigation Controller to help the user
navigate back to the previous scene. By default, this button has the same
title as the previous scene or the title Back if the scene has no title, but we Do It Yourself: Click on the Navigation Bar of the initial scene, open
can define our own, along with the scene's title and prompt, from the the Attributes Inspector panel, and assign the value "Menu" to the
Attributes Inspector panel. Title option and the value "Go Back" to the Back Button option
(Figure 9-25). You should see something like Figure 9-26. You can
Figure 9-25: Configuring the Navigation Item
also double-click the bar to change the title from the Storyboard.
 
The Navigation Bar was designed to work with scrollable content and The Navigation Bar is created from an object of the UINavigationBar class,
therefore it adapts to the content’s offset. If the content is not scrolled (the which includes properties to modify the appearance of the bar in all these
top of the content is visible to the user), the bar presents a scroll edge conditions.
appearance, but if the content has been scrolled, the bar presents a
standard appearance. By default, the scroll edge appearance is standardAppearance—This property sets or returns the
transparent, and that’s why we don’t see any bar at the top of our scenes appearance of the Navigation Bar when it is displayed with a standard
in the Storyboard, but in the standard appearance the bar is displayed in a height.
translucent gray, so we can see it when the content is scrolled. Figure 9-28
shows what we see when the scene contains a table, and the table is compactAppearance—This property sets or returns the
scrolled (We will study Table Views in Chapter 10). appearance of the Navigation Bar when it is displayed with a compact
let bar = navigationController?.navigationBar Do It Yourself: Update the ViewController class with the code in
bar?.scrollEdgeAppearance = standard
bar?.compactScrollEdgeAppearance = compact Listing 9-9. Run the application. You should see a red bar (Figure 9-
} 30, left). Rotate the device to landscape. Now the bar should be
}
 compact and yellow (Figure 9-30, right).
The standard and compact appearances are only applied when the content The UINavigationController class also includes properties and methods to
is scrolled. If we don't have scrollable content, as in this case, only the assign some functionality to the Navigation Bar.
scroll edge appearance is applied and that's all we need to configure the
bar. The view controller in Listing 9-9 creates two UINavigationBarAppearance hidesBarsOnTap—This property takes a Boolean value that
objects, one for the standard and another for the compact appearance. indicates whether the bars should be hidden when the user taps on
The configuration for the standard appearance includes a red background the screen.
while the background for the compact appearance is defined yellow. Once
the UINavigationBarAppearance objects are ready, we assign them to the bar's
hidesBarsOnSwipe—This property takes a Boolean value that
scrollEdgeAppearance and compactScrollEdgeAppearance properties to change the
indicates whether the bars should be hidden when the user swipes
configuration for each mode. the finger on the screen.
Notice that the attributes for the title are defined with an AttributeContainer hidesBarsWhenKeyboardAppears—This property takes a
structure, but the titleTextAttributes property takes a dictionary of attribute Boolean value that indicates whether the bars should be hidden when
isNavigationBarHidden—This property returns a Boolean value Figure 9-31: Navigation Bar removed when the user taps the screen
that indicates if the Navigation Bar is currently hidden.
setNavigationBarHidden(Bool, animated: Bool)—This method
hides or shows the Navigation Bar. The first argument indicates
whether the bar will be hidden, and the animated argument
determines if the process is going to be animated.
let standard = UINavigationBarAppearance() The UIBarButtonItemAppearance class defines the properties normal, disabled,
standard.backgroundColor = UIColor.systemRed
if let keys = try? Dictionary(container, including: \.uiKit) { highlighted, and focused to set the appearance for the buttons on every state.
standard.titleTextAttributes = keys To change the appearance of the Navigation Bar buttons, we must create
}
let compact = UINavigationBarAppearance()
the UIBarButtonItemAppearance object, then modify the properties for the
compact.backgroundColor = UIColor.yellow state we want to change, and finally assign the object to one of the
appearance properties (buttonAppearance or backButtonAppearance). The
let bar = navigationController?.navigationBar
bar?.scrollEdgeAppearance = standard
following example illustrates how to change the font of the back button in to the backButtonAppearance property of the UINavigationBarAppearance object
the normal state. and give the button a new image. Now the Back button is shown in big
letters.
Listing 9-11: Configuring the back button
 Figure 9-32: Custom appearance for the back button
import UIKit
The first thing we do in the view controller of Listing 9-11 is to create the
AttributeContainer structure with the attributes we want to assign to the
button's text. In this case, we define a dynamic font of type title1. Then, we
initialize the UIBarButtonItemAppearance object to configure the appearance
for the button. Depending on the state of the button we want to affect, we
must apply the appearance to the right property. In our example, we
modify the attributes of the text for the normal property, which affects the
button's normal state. When the button's appearance is ready, we assign it
Large Titles property to change this behavior. The option is also available for each
scene from the Attributes Inspector panel.

Figure 9-35: Large Titles option for each scene
Navigation Bars offer two formats to display the titles: small and large.
Small titles are shown by default, as illustrated by the previous examples,
but we can specify our preference using the prefersLargeTitles property
provided by the UINavigationBar class. The option is also available from the
Attributes Inspector panel when we select the Navigation Bar.
Now, the initial scene shows large titles, but the second scene displays the
 standard title with a small font.
The prefersLargeTitles property activates large titles for every scene in the Figure 9-36: Large and small titles
navigation stack, but the UINavigationItem class offers the largeTitleDisplayMode
Bar Buttons

Besides the Back button, we can also add custom buttons to the Navigation
Bar. These are not regular buttons; they are created from the
 UIBarButtonItem class with the following initializers.
The bar buttons may be added to the left or the right side of the bar, but if
we add them to the left in a scene that is not the initial scene, the buttons
replace the Back button. Figure 9-37 shows the interface of our example
after a Bar Button Item was dragged from the Library to the right side of
the Navigation Bar in the initial scene.

Figure 9-38: Bar Button Item in the Navigation Bar
The UINavigationItem class includes the following properties to add the
buttons from code.
Figure 9-39: Predefined Bar Button Item title—This property sets or returns the button’s title. It is an optional
of type String.
image—This property sets or returns the button's image. It is an 
override func viewDidLoad() { normal buttons if the Navigation Bar is configured with large titles.
super.viewDidLoad()
let button = UIBarButtonItem(title: "Close") All you need to do is to set the Prefers Large Titles option and drag
button.tintColor = UIColor.systemGreen the buttons from the Library to the Navigation Bar.
navigationItem.backBarButtonItem = button
}
}

Like buttons, bar buttons can also display contextual menus. The
UIBarButtonItem class includes the menu property to associate a menu with
the button. All we need to do is to set the primary action as nil and define
the menu as we did for the UIButton class in Chapter 5.
The Toolbar is not shown on the screen unless it includes at least one
button. The buttons can be added by dragging the Bar Button Item option
in the Library to the main view, as we did for Navigation Bars, or from code

implementing the methods described above. The following example shows
how to define a Toolbar with a custom appearance and one button.
The Toolbar is not visible in the scenes, but elements pinned to the bottom
of the Safe Area will move up and bar buttons added to the view will be
Listing 9-15: Configuring the appearance of the Toolbar
included in the Toolbar. The buttons are defined by the UIBarButtonItem

class. This is the same class used before to add buttons to the Navigation
import UIKit
Bar. The UIViewController class offers the following property and method to
manage these buttons from code. class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
toolbarItems—This property is an array of UIBarButtonItem objects let standard = UIToolbarAppearance()
standard.backgroundColor = UIColor.yellow
with the buttons. navigationController?.toolbar.scrollEdgeAppearance = standard
The application works as before, the system shows the initial scene with
the menu first and then when the user clicks on any of the buttons it
transitions to the second scene to show the selected picture, but because
this time we use a Navigation Bar, we do not have to worry about the import UIKit
button required to go back to the previous scene. @main
class AppDelegate: UIResponder, UIApplicationDelegate {
var picturesList: [String]!
Do It Yourself: Create a new project. Embed the initial scene in a var ratings: [Int]!
Navigation Controller. Add a second scene to the Storyboard. var selectedPicture: Int!
Connect the initial scene to the second scene with a Show segue func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
(Figure 9-17). Assign the string "showPicture" to the segue's [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
picturesList = ["Husky", "Door"]
identifier (Figure 9-11). Create a new file with a view controller ratings = [0, 0]
called SecondViewController and assign it to the second scene (Figures 9- selectedPicture = 0
return true
5 and 9-6). Click on the Navigation Bars and assign the title "Menu" }
to the initial scene and "Picture" to the second scene (Figure 9-25). func application(_ application: UIApplication, configurationForConnecting
connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) ->
Add buttons called Show Husky and Show Door to the initial scene UISceneConfiguration {
and assign the values 0 and 1 to their tag properties from the return UISceneConfiguration(name: "Default Configuration", sessionRole:
connectingSceneSession.role)
Attributes Inspector panel (this is to identify which button was }
pressed). Add an Image View, a label with the text "Rating", and a }

Slider to the second scene. Set a value of 0, a minimum of 0, and a
maximum of 5 for the Slider. Assign a higher vertical Content The picturesList property is an array with the names of the pictures available,
Hugging Priority and Content Compression Resistance Priority to the the ratings property is another array with the ratings assigned by the user to
Slider if necessary. Add the pictures husky.png and door.png to the each picture, and the selectedPicture property stores the index of the
Assets Catalog. The interface should look like Figure 9-45. currently selected picture. The first place where these values are required
is in the view controller of the initial scene. Here, we need to update the
The code is also like previous examples, but the picturesList, ratings, and selectedPicture property every time a button is pressed so that the rest of the
selectedPicture properties we used before to store the information are going application knows which picture was selected by the user.
to be defined in the application’s delegate, so every time the rating is
modified in the second scene, we do not need to send the data back to the Listing 9-18: Storing the index of the selected picture
initial view controller; all we need to do is access the app’s delegate and 
change the values of its properties. The following code illustrates how the import UIKit
properties should be declared and initialized in the delegate. class ViewController: UIViewController {
var mydelegate: AppDelegate!
Listing 9-17: Storing common data in the app’s delegate
override func viewDidLoad() {
 super.viewDidLoad()
let app = UIApplication.shared
mydelegate = app.delegate as? AppDelegate let picture = mydelegate.picturesList[selected]
} let rating = mydelegate.ratings[selected]
@IBAction func goToPicture(_ sender: UIButton) { sliderRating.value = Float(rating)
mydelegate.selectedPicture = sender.tag pictureView.image = UIImage(named: picture.lowercased())
performSegue(withIdentifier: "showPicture", sender: self) }
} @IBAction func changeRating(_ sender: UISlider) {
} let value = round(sender.value)
 sliderRating.value = value
let selected = mydelegate.selectedPicture ?? 0
mydelegate.ratings[selected] = Int(value)
The first thing we do in the ViewController class is to get a reference to the }
application’s delegate. We first get a reference to the UIApplication object }

created for the application and then read its delegate property. The property
we need to modify in the delegate is the selectedPicture property that stores
In this view controller, we read the three properties of the app’s delegate
the index of the selected picture. For this purpose, we have connected
to configure the view. First, we get the index of the selected picture and
both buttons, Show Husky and Show Door, to one Action called goToPicture().
use it to read the picture’s name and rating from the picturesList and ratings
When any of the buttons is pressed, we take the value of their tag property
arrays. This information is used next to update the Slider and the Image
and assign it to the selectedPicture property (the value 0 represents the husky
View before the interface is shown on the screen. The selectedPicture and
and the value 1 the door).
ratings properties are also used in the Action connected to the Slider to
At the end, we call the performSegue() method to transition to the second
update the value of the rating with the one selected by the user.
scene. Notice that this time we do not need to send any value to
SecondViewController because this view controller also takes the values from
Do It Yourself: Update the AppDelegate class in the
the app’s delegate, as shown next.
AppDelegate.swift file with the code in Listing 9-17. Connect the
Listing 9-19: Showing the selected picture and storing the rating in the Show Husky and Show Door buttons in the initial scene to an Action
app’s delegate called goToPicture(). Complete the ViewController class with the code in
 Listing 9-18. Connect the Slider and the Image View of the second
import UIKit scene to Outlets called sliderRating and pictureView, respectively. Create
an Action for the Slider called changeRating(). Complete the
class SecondViewController: UIViewController {
@IBOutlet weak var sliderRating: UISlider! SecondViewController class with the code in Listing 9-19. Run the
@IBOutlet weak var pictureView: UIImageView!
var mydelegate: AppDelegate!
application and modify the rating of the pictures to verify that the
values are preserved.
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.shared The use of the app’s delegate to store data is only recommended when the
mydelegate = app.delegate as? AppDelegate amount of data is not significant. For large amounts of data, it is better to
let selected = mydelegate.selectedPicture ?? 0 create global objects or structures that the application can access every
time necessary and where we can safely manage the information available. the Model is now the ApplicationData structure defined in Listing 9-20.
To do this in Swift is very easy. All the files are in the global space and are This model and the one created with the app's delegate before are
processed as soon as the application is launched. If we want to create a suitable for small to medium applications, but there are multiple
global object or structure that we can access from anywhere in our code, models and programming patterns available for professional
all we need to do is define it and initialize it in a new Swift file. applications. For instance, some applications implement singletons.
As any other files, Swift files are created from the File menu or pressing Singletons are defined by classes that make sure that one and only
Command + N on the keyboard, but instead of selecting the Cocoa Touch one object can be instantiated from the class. For more information,
Class option we click on the Swift File option (Figure 9-3). The name of the visit our website and follow the links for this chapter.
file in this case is just for reference, because except for a few comments
and import statements, no code is created for the template. Once the file is The structure declared in Listing 9-20 contains the same information as
added to the list of files in our application, we can define our custom before, but it provides a unique place to store and manage all the
classes or structures inside. For our example, we have created a file called information for our app. Now we can access this data from anywhere in
ApplicationData.swift and define a structure inside with the same name the code by just reading and writing the content of the AppData variable.
and all the properties necessary for our application. The following example shows how the ViewController of our application gets
the data from AppData.
Listing 9-20: Defining a global structure to store common data
 Listing 9-21: Accessing the data model from the ViewController class
import Foundation 
import UIKit
struct ApplicationData {
var picturesList: [String] class ViewController: UIViewController {
var ratings: [Int]
@IBAction func goToPicture(_ sender: UIButton) {
var selectedPicture: Int
AppData.selectedPicture = sender.tag
performSegue(withIdentifier: "showPicture", sender: self)
init() {
}
picturesList = ["Husky", "Door"]
}
ratings = [0, 0] 
selectedPicture = 0
}
} Since we do not have to get a reference to the app’s delegate anymore, all
var AppData = ApplicationData()

we need in the ViewController class is the Action for the buttons. When a
button is pressed, the goToPicture() method is executed as always, but this
IMPORTANT: The ApplicationData structure represents the model in time instead of accessing the selectedPicture property from the app’s delegate
our MVC, or Model-View-Controller (see Chapter 5). In our example, we do it from the AppData structure.
the Views (scenes) are created from the Storyboard, the Controllers The code for the SecondViewController can also be simplified. Getting the data
are the view controllers we associate with those views (scenes), and from a global structure makes everything easier.
9.3 Tab Bar Controllers
Listing 9-22: Accessing the data model from the SecondViewController class


import UIKit
A Tab Bar Controller is a simple container view controller that designates
class SecondViewController: UIViewController { two areas on the screen: one for the scenes and a smaller one at the
@IBOutlet weak var sliderRating: UISlider!
@IBOutlet weak var pictureView: UIImageView!
bottom for a bar with tabs that users can tap to select the scene they want
to see. Each tab is associated with only one scene and therefore we can
override func viewDidLoad() { use them to move from one scene to another.
super.viewDidLoad()
let selected = AppData.selectedPicture
let picture = AppData.picturesList[selected] Figure 9-46: Application based on a Tab Bar Controller
let rating = AppData.ratings[selected]
sliderRating.value = Float(rating)
pictureView.image = UIImage(named: picture.lowercased())
}
@IBAction func changeRating(_ sender: UISlider) {
let value = round(sender.value)
sliderRating.value = value
let selected = AppData.selectedPicture
AppData.ratings[selected] = Int(value)
}
}

and generates the bar (Figure 9-48, left). If there is not enough space on
the bar to place all the tabs available, the controller adds a tab called More
that the user can tap to select the tabs that are not visible.
The scenes managed by the Tab Bar Controller are connected with a segue
called View Controllers. If we want to incorporate another scene to the Tab
Bar Controller, we must add it to the Storyboard and create a View
Controllers segue from the Tab Bar Controller to the new scene.
We can add all the scenes we want. When a scene is added to the Tab Bar
Controller, the system automatically includes the corresponding tab at the
bottom of the scene. The Tab Bar Controller gets the tabs from the scenes
Tabs These options define the image and the name. If we want to provide our
own values, we must set the System Item option to Custom and specify the

information below. We can provide our own images or specify an SF
Symbol. Custom images must be provided with a size of 30 pixels by 30
The bar is managed by the Tab Bar Controller, but the tabs are provided by
pixels (60x60 for the 2x scale and 90x90 for the 3x scale) and a transparent
the view controllers to which they belong. To change the values and
background. For our example, we have created the images
appearance of the tabs, we can click on them and edit their values from
iconweather.png for the initial scene and iconsettings.png for the second
the Attributes Inspector panel.
scene. Figure 9-51, below, shows what the bar looks like when we select
these images and insert the titles "Weather" and "Settings".
Figure 9-49: Options to configure the tab
Figure 9-51: Custom tabs
Controllers. In addition to the properties defined by the UIBarItem class to the system. Figure 9-52 shows what we see when we run the app for the
configure the items, like title, image, and landscapeImagePhone, the UITabBarItem first time and after the button is pressed 12 times.
class includes the following.
Figure 9-52: Tab with a badge and a new title
badgeValue—This property sets or returns the value of the tab’s
badge (shown inside a circle at the top-right corner of the tab's icon).
It is an optional of type String.
The tabs are configured from the view controllers to which they belong.

The UIViewController class includes the tabBarItem property to access the view
controller’s tab. The next example adds an Action for a button to update
Do It Yourself: Add a button to the initial scene of the interface in
the badge when the button is pressed.
Figure 9-48. Connect the button to an Action called updateBadge().
Complete the ViewController class with the code in Listing 9-23. Run
Listing 9-23: Updating the badge from the view controller

the application and press the button. You should see something like
import UIKit
Figure 9-52.
This code gets a reference to the view's tab from the tabBarItem property,
updates the tab's title with the string "New Weather" and the badgeValue
property with the value of the counter property. The counter is incremented
every time the button is pressed to illustrate how the badge is modified by
Tab Bar Controller items in the standard configuration.
 compactInlineLayoutAppearance—This property sets or returns a
object that provides the appearance of the
UITabBarItemAppearance
View controllers manage their own tabs, but the bar and the list of view items in the compact configuration.
controllers available is managed by the Tab Bar Controller. The inlineLayoutAppearance—This property sets or returns a
UITabBarController class includes the following properties to store the view
UITabBarItemAppearance object that provides the appearance of the
controllers, keep a reference to the selected one, and access the bar.
items in the inline configuration.
tabBar—This property returns a reference to the UITabBar object that stackedItemPositioning—This property sets or returns a value that
represents the bar. determines the position of the buttons. It is an enumeration of type
ItemPositioning with the values fill, centered, and automatic.
viewControllers—This property sets or returns the view controllers
managed by the Tab Bar Controller. It is an array of UIViewController stackedItemSpacing—This property sets or returns a CGFloat value
objects. that determines the space between items.
selectedViewController—This property returns a reference to the stackedItemWidth—This property sets or returns a CGFloat value
view controller of the scene currently shown to the user. that determines the width of the items.
selectedIndex—This property returns the index of the view The UITabBarAppearance class defines the appearance of the bar. If we want
controller that is currently being shown to the user. It is a value of to modify the appearance of the items, we must assign an appearance
type Int. object to the stackedLayoutAppearance, compactInlineLayoutAppearance, or
inlineLayoutAppearance properties. These properties take an object of type
The bar is created from the UITabBar class, which includes the UITabBarItemAppearance, which in turn includes the normal, selected, disabled, and
standardAppearance and scrollEdgeAppearance properties to modify the bar's focused properties to configure the appearance of the items in every state.
appearance. These properties take an object of type UITabBarAppearance. The values are defined by the UITabBarItemStateAppearance class, which
This is a subclass of UIBarAppearance, which is also available for Navigation includes the following properties for configuration.
Bars and Toolbars. Therefore, a UITabBarAppearance object includes
properties we used before like backgroundEffect, backgroundColor, and titleTextAttributes—This property sets or returns the attributes of
backgroundImage, but also its own properties. The following are the most
the item's title. The value is a dictionary with NSAttributedString keys.
frequently used.
titlePositionAdjustment—This property sets or returns the
stackedLayoutAppearance—This property sets or returns a position of the item's title. It is an object of type UIOffset, which
UITabBarItemAppearance object that provides the appearance of the includes the horizontal and vertical properties to set the horizontal and
vertical offsets.
iconColor—This property sets or returns the color of the item's standard.inlineLayoutAppearance.normal.titleTextAttributes = attr
}
image. standard.stackedLayoutAppearance.selected.iconColor = .systemGreen
standard.compactInlineLayoutAppearance.selected.iconColor = .systemGreen
badgeTextAttributes—This property sets or returns the attributes standard.inlineLayoutAppearance.selected.iconColor = .systemGreen
of the text displayed by the badge. The value is a dictionary with if let attr = try? Dictionary(attributesSelected, including: \.uiKit) {
standard.stackedLayoutAppearance.selected.titleTextAttributes = attr
NSAttributedString keys. standard.compactInlineLayoutAppearance.selected.titleTextAttributes = attr
standard.inlineLayoutAppearance.selected.titleTextAttributes = attr
badgeBackgroundColor—This property sets or returns the badge's }
background color. self.tabBar.standardAppearance = standard
}
}
The UIViewController class includes the tabBarController property to access the 
Tab Bar Controller to which the scene belongs, but because there is usually
only one Tab Bar Controller per application it is better to create a subclass A Tab Bar can adopt three different configurations. The standard
of UITabBarController and manage the view controllers and the bar from it. configuration is when the bar is shown with a standard height and the text
For our example, we have created a file called MyTabViewController.swift below the image. The appearance for this configuration is defined by the
with a subclass of the UITabBarController called MyTabViewController and assign stackedLayoutAppearance property (the elements of the tab are stacked).
it to the Tab Bar Controller of the interface in Figure 9-48. Another possible configuration is compact. In this configuration, the height
of the Tab Bar is reduced to make room for the content. This appearance is
Listing 9-24: Modifying the appearance of the bar defined by the compactInlineLayoutAppearance property. Finally, there is a
 configuration in which the elements of the tab are shown side by side (the
import UIKit image on the left and the text on the right). This appearance is defined by
the inlineLayoutAppearance property. The configuration is determined by the
class MyTabViewController: UITabBarController {
override func viewDidLoad() { system according to the space available, but we can assign the same
super.viewDidLoad() appearance to all the three properties to make sure that the Tab Bar
var attributesNormal = AttributeContainer()
attributesNormal.foregroundColor = .systemGray
always looks the same, as we did in this example.
The process to configure the bar is like those implemented before to
var attributesSelected = AttributeContainer() configure the Navigation Bar and the Toolbar. In this case, we start by
attributesSelected.foregroundColor = .systemGreen
creating two AttributeContainer structures to define the attributes for the text,
let standard = UITabBarAppearance() then we create the UITabBarAppearance object and proceed to define the
standard.backgroundColor = .systemGray5
standard.stackedLayoutAppearance.normal.iconColor = .systemGray
appearances for every configuration (standard, compact, and inline).
standard.compactInlineLayoutAppearance.normal.iconColor = .systemGray Finally, we access the Tab Bar with the tabBar property provided by the
standard.inlineLayoutAppearance.normal.iconColor = .systemGray UITabBarController object and assign the appearance object to the
if let attr = try? Dictionary(attributesNormal, including: \.uiKit) {
standard.stackedLayoutAppearance.normal.titleTextAttributes = attr standardAppearance property of the bar to modify its appearance.
standard.compactInlineLayoutAppearance.normal.titleTextAttributes = attr
Do It Yourself: Create a new file with a subclass of the
class called MyTabViewController and assign it to the
UITabBarController
Tab Bar Controller introduced in Figure 9-48. Complete the subclass
with the code in Listing 9-24. The application now shows the items in
gray, but they change to green when selected.
By default, the items on the bar are expanded to fill the space available,
but we can center the items and then assign a custom width and space in
between with the rest of the properties provided by the UITabBarAppearance
class. 
Listing 9-25: Configuring the items There are more tasks we can perform from the UITabBarController subclass
 other than configuring the bar. Important things like establishing which
import UIKit scene will be shown first or setting the badges for each tab can be done
from this subclass. The following example modifies the selectedIndex property
class MyTabViewController: UITabBarController {
override func viewDidLoad() { to declare the second scene as the initial scene.
super.viewDidLoad()
let standard = UITabBarAppearance()
standard.stackedItemPositioning = .centered Listing 9-26: Initializing the Tab Bar Controller
standard.stackedItemWidth = 50 
standard.stackedItemSpacing = 50
import UIKit
self.tabBar.standardAppearance = standard
}
class MyTabViewController: UITabBarController {
}
override func viewDidLoad() {

super.viewDidLoad()
let list = viewControllers!
The stackedItemPositioning property can take two values, fill and centered. The let controller = list[0] as! ViewController
controller.tabBarItem?.badgeValue = String(20)
value by default is fill, which causes the items to expand proportionally to
fill the available space. This means that the size and space in between self.selectedIndex = 1
items is determined by the system. But if the value centered is assigned to }
}
this property, as we did in Listing 9-25, we can assign a custom size and 
space in between. In our example, we give the items a width and a space
of 50 points. The view controllers managed by the Tab Bar Controller are stored in an
array called viewControllers in the order they were connected in the
Figure 9-53: Items with custom size Storyboard. In our example, the view controller for the Weather scene was
stored at index 0 and the view controller for the Settings scene was stored Tab Bar Controller Delegate
at index 1. Therefore, to add a badge to the first scene, we get the first

element of the viewControllers array, cast it as ViewController (the array
contains elements of type UIViewController), and access its properties. On the
Tab Bar Controllers can use a delegate object to report when something
other hand, the Settings view controller is at index 1, so we assign the
happened or is about to happen with the tabs. The UIKit framework
value 1 to the selectedIndex property to declare this as the initial scene.
includes the UITabBarControllerDelegate protocol for this purpose. The
following are some of the methods defined by the protocol.
tabBarController(UITabBarController, didSelect:
UIViewController)—This method is called by the UITabBarController
object when a tab is selected. The didSelect argument is a reference
to the view controller of the selected tab.
tabBarController(UITabBarController, shouldSelect:
UIViewController)—This method is called by the UITabBarController
object to know whether it should let the user select a tab. The
method returns a Boolean value that communicates the decision
made by the app. The shouldSelect argument is a reference to the
view controller of the selected tab.
iOS tables can present the information on a list, like the table on the left of
Figure 10-1, or organized in groups, like the ones at the center and right of
Figure 10-1, and they can also display images and text, as in this example,
only text, or be completely customized, as we will see later.
will be animated. define a prototype cell and then ask the system to create the cells from this
prototype. This is so the system can keep in memory only the cells required
at any given moment and improve performance. The Library includes the
following option to add a prototype cell to the table.
text—This property sets or returns the cell's main text. It is an color—This property sets or returns the color. It is a value of type
optional of type String. UIColor.
attributedText—This property sets or returns the cell's attributed alignment—This property sets or returns the alignment. It is a
text. It is an optional of type NSAttributedString. TextAlignment enumeration with the values center, justified, and natural.
secondaryText—This property sets or returns the cell's secondary lineBreakMode—This property sets or returns the mode used to
text. It is an optional of type String. truncate the text. It is an NSLineBreakMode enumeration with the values
secondaryAttributedText—This property sets or returns the cell's byWordWrapping, byCharWrapping, byClipping, byTruncatingHead,
image—This property sets or returns the cell's image. It is an optional numberOfLines—This property sets or returns the maximum lines
of type UIImage. of text allowed. It is a value of type Int. The value 0 indicates unlimited
lines.
accessoryType—This property sets or returns the type of the
On the other hand, the attributes for the image are defined by an accessory view. It is an enumeration called AccessoryType with the values
ImageProperties structure. For this purpose, the structure includes the none, disclosureIndicator, detailDisclosureButton, checkmark, and detailButton.
following properties.
After the configuration object is defined, we must assign it to the cell. The
UITableViewCell class includes the following properties for this purpose.
In addition to the text, the secondary text, and the image, a cell can also
include a view on the side. These views, called accessories, are used to
reflect or propose user interaction. For instance, there is an accessory
called Disclosure Indicator used to indicate to the user that there is more
information available for the item. Accessories are defined by the cell. The
UITableViewCell includes the following property for this purpose.
Data Source
The UITableViewDiffableDataSource object generates the cells from a snapshot

of the data created by a NSDiffableDataSourceSnapshot object. The following are
some of the properties and methods defined by the
The data is provided to the Table View by two objects: an object of the
NSDiffableDataSourceSnapshot class to process the values.
UITableViewDiffableDataSource class, which takes care of defining the cells, and
an object of the NSDiffableDataSourceSnapshot class, responsible for keeping
numberOfItems—This property returns the number of values
the Table View up to date. The UITableViewDiffableDataSource class provides the
managed by the snapshot. There is also the numberOfSections property
following initializer and methods to generate the cells and apply the
changes. to return the number of sections.
itemIdentifiers—This property returns an array with the identifiers
UITableViewDiffableDataSource(tableView: UITableView, of the values managed by the snapshot. There is also the
cellProvider: Closure)—This initializer creates a data source for the sectionIdentifiers property for the sections.
Table View specified by the tableView argument. The cellProvider numberOfItems(inSection: SectionIdentifierType)—This
argument is a closure that is called every time a cell is required. The method returns the number of items in the section specified by the
closure receives three values: a reference to the Table View, the cell's inSection argument.
index path, and a reference to the data that must be shown by the
appendItems([ItemIdentifierType], toSection:
cell.
SectionIdentifierType?)—This method adds to the snapshot the
snapshot()—This method returns a reference to the current items specified by the first argument in the section specified by the
snapshot used by the data source to get the data. toSection argument. If there is only one section, the second argument
apply(NSDiffableDataSourceSnapshot, animatingDifferences: may be ignored.
Bool, completion: Closure)—This method applies the snapshot appendSections([SectionIdentifierType])—This method adds to
provided by the first argument to the data source object. It is used to the snapshot the sections specified by the argument.
update the Table View with new values. The animatingDifferences
insertItems([ItemIdentifierType], afterItem:
argument determines if the changes on the table will be animated,
ItemIdentifierType)—This method inserts the items specified by the
and the completion argument is a closure to be executed when the
first argument after the item specified by the afterItem argument.
process is over. These last two arguments may be ignored.
insertItems([ItemIdentifierType], beforeItem:
defaultRowAnimation—This property sets or returns a value that ItemIdentifierType)—This method inserts the items specified by the
determines how the insertion or deletion of cells will be animated. It
first argument before the item specified by the beforeItem argument.
is an enumeration of type RowAnimation with the values fade, right, left,
top, bottom, middle, automatic, and none.
insertSections([SectionIdentifierType], afterSection: moveSection(SectionIdentifierType, beforeSection:
SectionIdentifierType)—This method inserts the sections specified SectionIdentifierType)—This method moves the section specified
by the first argument after the section specified by the afterSection by the first argument to the position before the section specified by
argument. the beforeSection argument.
insertSections([SectionIdentifierType], beforeSection: reloadItems([ItemIdentifierType])—This method reloads the
SectionIdentifierType)—This method inserts the sections specified items specified by the argument. It updates the items with the current
by the first argument before the section specified by the values from the model.
beforeSection argument. reloadSections([SectionIdentifierType])—This method reloads
deleteItems([ItemIdentifierType])—This method removes from the sections specified by the argument. It updates the sections with
the snapshot the items specified by the argument. the current values from the model.
deleteSections([SectionIdentifierType])—This method removes
from the snapshot the sections specified by the argument.
reconfigureItems([ItemIdentifierType])—This method updates
the values for the items specified by the argument.
deleteAllItems()—This method removes all the items from the
snapshot.
moveItem(ItemIdentifierType, afterItem: ItemIdentifierType)
—This method moves the item specified by the first argument to the
position after the item specified by the afterItem argument.
moveItem(ItemIdentifierType, beforeItem:
ItemIdentifierType)—This method moves the item specified by the
first argument to the position before the item specified by the
beforeItem argument.
moveSection(SectionIdentifierType, afterSection:
SectionIdentifierType)—This method moves the section specified
by the first argument to the position after the section specified by the
afterSection argument.
Cells and sections are identified by their position in the Table View. provided by the argument.
Sections are assigned a consecutive index starting from 0, and then the itemIdentifier(for: IndexPath)—This method returns the identifier
cells inside each section are also identified with a consecutive index of the cell at the index path specified by the argument.
starting from 0. Therefore, to identify a cell, we need the index of the cell
and the index of the section the cell belongs to. Table Views store this
information in a structure of type IndexPath. The values are returned by the
following properties.
enum Sections {
case main
}
class ItemsData: Identifiable {
var id: UUID = UUID()
var name: String
var image: String
var calories: Int
var selected: Bool
items.sort(by: { $0.name < $1.name }) defines a typealias for the property's data type called ID (a typealias is an
}
} alternative name for a data type). By using this typealias we make sure that
init() { the data type assigned to the diffable data source is the same assigned to
items.append(ItemsData("Bagels", "bagels", 250, false))
items.append(ItemsData("Brownies", "brownies", 466, false))
the id property. This makes the code more readable and easy to update.
items.append(ItemsData("Butter", "butter", 717, false)) The items property includes the didSet method to sort the values
items.append(ItemsData("Cheese", "cheese", 402, false)) alphabetically every time changes are introduced by the user (the values
items.append(ItemsData("Coffee", "coffee", 0, false))
items.append(ItemsData("Cookies", "cookies", 502, false)) are modified, deleted, or new values are added).
items.append(ItemsData("Donuts", "donuts", 452, false)) With the model ready, now the view controller has all the information it
items.append(ItemsData("Granola", "granola", 471, false))
items.append(ItemsData("Juice", "juice", 23, false))
needs to configure the table and present the data on the screen.
items.append(ItemsData("Lemonade", "lemonade", 40, false))
items.append(ItemsData("Lettuce", "lettuce", 15, false)) Listing 10-2: Defining the diffable data source and the snapshot
items.append(ItemsData("Milk", "milk", 42, false))
items.append(ItemsData("Oatmeal", "oatmeal", 68, false)) 
items.append(ItemsData("Potatoes", "potato", 77, false)) import UIKit
items.append(ItemsData("Tomatoes", "tomato", 18, false))
items.append(ItemsData("Yogurt", "yogurt", 59, false)) class ViewController: UIViewController {
} @IBOutlet weak var myTable: UITableView!
}
var AppData = ApplicationData() override func viewDidLoad() {
 super.viewDidLoad()
myTable.register(UITableViewCell.self, forCellReuseIdentifier: "myCell")
The ItemsData class includes four properties for the values (name, image,
AppData.dataSource = UITableViewDiffableDataSource<Sections, ItemsData.ID>
calories, and selected), and the id property defined with a UUID value. This is a (tableView: myTable) { tableView, indexPath, itemID in
structure that always produces a unique value and that's what we are let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
going to use to identify each item on the table. if let item = AppData.items.first(where: { $0.id == itemID }) {
Next is the definition of the structure to contain the model. This is the var config = cell.defaultContentConfiguration()
config.text = item.name
same ApplicationData structure we used before for the examples in Chapter 9.
cell.contentConfiguration = config
The structure includes a property called dataSource to store the diffable data }
source, a property called items to store the values for each cell, and an return cell
}
initializer to provide initial values to work with. var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
Notice that the UITableViewDiffableDataSource class is generic, and therefore it snapshot.appendSections([.main])
snapshot.appendItems(AppData.items.map({ $0.id }))
must be declared with the data types of the values is going to store (the AppData.dataSource.apply(snapshot)
data types used to identify the sections and the items). For the sections it }
is easy, the identifier is a value of type Sections, but the data type of the }

items' identifiers depends on the data type of the id property. In this case,
we could have specified the UUID data type, but the Identifier protocol
A Table View needs a prototype cell. This is like a template the table uses and requires us to specify the data types of the identifiers we are going to
to create all the cells it needs to display the data. Although prototype cells work with. In this case, Sections for the sections and ItemsData.ID for the data.
can be added to the Table View in the Storyboard, Apple's technologies are After the object is created, we must add the sections with the
leaning towards the optimizations of elements created from code, so in appendSections() method and all the items IDs we want to show to the user
this example we have decided to follow that approach. Using the register() with the appendItems() method. Notice that because the items array contains a
method, we define a prototype cell with the myCell identifier and then to list of items, we map the array with the map() method to get an array of
create the diffable data source with this cell. identifiers instead. Finally, the snapshot is applied to the diffable data
The UITableViewDiffableDataSource initializer takes a reference to the Table source with the apply() method and the values appear on the screen.
View and a closure that is called every time the table needs a cell (defined
as a trailing closure in this example). The closure receives three values we Figure 10-5: Table View with standard configuration
must use to create and configure the cell: a reference to the Table View,
the index path with the location of the cell, and the UUID value that
identifies the item to be shown in that row. The cell is created by the
dequeueReusableCell() method. This method creates a new UITableViewCell
object from a prototype cell or returns an existent object from a cell that is
not currently in use. Whatever the origin, we always must update the
configuration with the values of the current item, and the first step is to get
the item. For this purpose, we call the first(where:) method on the items array.
This method compares the value of the id property of each item with the ID
received by the closure and returns the item which ID matches. With this
value, we can finally configure the cell.
The configuration is defined by a UIListContentConfiguration structure. In this
example, we use the instance returned by the defaultContentConfiguration()
method. This is a standard configuration that includes the image on the
left, the text on top, and a secondary text with a smaller font at the 
bottom. Although the configuration structure provides styles for every
element in the cell, we can define only those we need. In this case, we only Do It Yourself: Create a new project. Add a Table View to the scene
want the cell to show the name of the item, so we assign that value to the and pin it to the Safe Area. Connect the table to an Outlet in the
text property. After the configuration structure is ready, we assign it to the view controller called myTable. Create a Swift file called
contentConfiguration property to apply it to the cell.
ApplicationData.swift to store the code in Listing 10-1. Complete the
The Table View shows the cells on the screen, the diffable data source
ViewController class with the code in Listing 10-2. Run the application.
configures the cells with the data, but the data is managed by a snapshot
You should see a Table View like in Figure 10-5.
created from a NSDiffableDataSourceSnapshot object. This class is also generic
} Background
}
 
prepareDataSource()
prepareSnapshot()
}
func prepareDataSource() {
AppData.dataSource = UITableViewDiffableDataSource<Sections, ItemsData.ID>(tableView:
myTable) { tableView, indexPath, itemID in
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
if let item = AppData.items.first(where: { $0.id == itemID }) {
var config = cell.defaultContentConfiguration()
States

Cells can be in a variety of different states. For instance, if the user taps on
a cell, the cell becomes selected (the cell is in the selected state). If the
user swipes a cell to the side (a gesture used to delete a cell), it is said that
the cell is in the swipe state. The states are reported by Boolean properties
of the UICellConfigurationState structure. Some of the properties available are
isSelected, isHighlighted, isFocused, isDisabled, isEditing, isSwiped, and isExpanded. To
process these states, the UITableViewCell class includes the following
properties.
The code in Listing 10-7 defines the content configuration as before, but
the background configuration is defined by the closure. The first step is to
get a reference to the configuration for the current state. For this purpose,
we get a standard configuration with the listPlainCell() method and then call
the updated() method on it to get the configuration for the state determined
by the argument received by the closure. Next, we proceed to define the
Cell Subclass
The code in Listing 10-8 defines a subclass of UITableViewCell called FoodCell.

The class includes a property called item to store a value of type ItemsData,
and overrides the updateConfiguration() method to define the cell's
The dequeueReusableCell() method creates a UITableViewCell object from the
configuration. In this case, we define a simple configuration with just the
prototype cell. This object generates a standard cell that we must configure
item's name, but this method can also include a background configuration
to show the values we want. But this code can get long and difficult to
and all the values we need.
maintain. An alternative is to create a UITableViewCell subclass and move all
Now that the configuration of the cell is defined in the cell's subclass, all
the cell configuration from the view controller to this class. To specify the
the diffable data source needs to do is to create the cell from this class and
cell's configuration, the UITableViewCell class defines the following method.
assign the item to the cell's property.
updateConfiguration(using: State)—This method is called every Listing 10-9: Creating a cell from a custom subclass
time the cell needs to update the configuration for a specific state. 
import UIKit
The file for the subclass is created as we did before for view controllers,
class ViewController: UIViewController {
only this time we must select the UITableViewCell class as the superclass. As
@IBOutlet weak var myTable: UITableView!
always, there are no requirements for the name, but it is better to give it a
name related to the cell (for our example, we call it FoodCell). Inside the override func viewDidLoad() {
super.viewDidLoad()
subclass, we must implement the updateConfiguration() method, and also a myTable.register(FoodCell.self, forCellReuseIdentifier: "myCell")
property to store a copy of the data to be displayed, as shown in the prepareDataSource()
prepareSnapshot()
following example. }
func prepareDataSource() {
Listing 10-8: Configuring the cells from a UITableViewCell subclass AppData.dataSource = UITableViewDiffableDataSource<Sections, ItemsData.ID>(tableView:
myTable) { tableView, indexPath, itemID in
 let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as!
import UIKit FoodCell
if let item = AppData.items.first(where: { $0.id == itemID }) {
class FoodCell: UITableViewCell { cell.item = item
var item: ItemsData! }
return cell
override func updateConfiguration(using state: UICellConfigurationState) { }
var config = self.defaultContentConfiguration() }
config.text = item.name func prepareSnapshot() {
self.contentConfiguration = config var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
} snapshot.appendSections([.main])
} snapshot.appendItems(AppData.items.map({ $0.id }))
 AppData.dataSource.apply(snapshot)
}
} Custom Cell


Because we are now creating the cells from our subclass, we must register
the prototype cell with this data type, and cast the value returned by the Sometimes the configuration and elements provided by a standard cell
dequeueReusableCell() method to FoodCell. Once we have the FoodCell object, all may not be enough to satisfy the requirements of our application. There
we need to do in the diffable data source is to assign the current item to are different options available to customize the cells. One alternative is
the item property, so the cell knows which values to display. All the work provided by the UIListContentConfiguration structure. In addition to all the
has been moved from the diffable data source to the cell, but the result is properties defined by this structure to assign attributes to the texts and the
the same as before. image, the structure also includes the following to define the padding
between the elements and their container.
Do It Yourself: Create a new file with a UITableViewCell subclass
called FoodCell. Complete the class with the code in Listing 10-8. imageToTextPadding—This property sets or returns a CGFloat value
Update the ViewController class with the code in Listing 10-9. Run the that determines the padding between the image and the main text.
application. You should see an interface like the one in Figure 10-5. textToSecondaryTextHorizontalPadding—This property sets or
returns a CGFloat value that determines the padding between the text
IMPORTANT: The current template generated by Xcode for and the secondary text when they are displayed side by side.
subclasses of UITableViewCell includes the methods awakeFromNib() and
setSelected(). The awakeFromNib() method is like the viewDidLoad() method.
textToSecondaryTextVerticalPadding—This property sets or
It is called as soon as the cell is loaded, and it is used to initialize it. returns a CGFloat value that determines the padding between the text
The setSelected() method, on the other hand, is called when the cell is and the secondary text when they are displayed on top of each other.
selected. directionalLayoutMargins—This property sets or returns a value
that determines the padding between the elements and the container
view (also known as Content View). It is a value of type
NSDirectionalEdgeInsets.
Listing 10-10: Defining the cell's padding Table View cells include a view, called Content View, where all the cell's
 content is placed. When working with configuration structures, the object
import UIKit that represents this view is created from a subclass of UIView that conforms
to the UIContentView protocol. Although we can define our own subclasses,
class FoodCell: UITableViewCell {
var item: ItemsData! as we will see later, UIKit includes a subclass called UIListContentView that
comes with all the elements necessary to present standard values on the
override func updateConfiguration(using state: UICellConfigurationState) {
var config = self.defaultContentConfiguration() screen. This includes the two labels for the text and secondary text, and
config.text = item.name the Image View for the image. This view is automatically pinned to the
config.secondaryText = "\(item.calories) Calories"
config.image = UIImage(named: item.image)
sides of the cell, so the text can use all the space available. There are
config.imageProperties.maximumSize = CGSize(width: 60, height: 60) different ways to add custom elements along with those included by a List
config.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 0, leading: 50, bottom: 0, Content View. An alternative is to create our own UIListContentView object
trailing: 0)
self.contentConfiguration = config and define the constraints to make room for additional views. For instance,
} below is a cell with a List Content View on the left and a custom button on
}

the right.
Figure 10-11: Cell with custom padding Figure 10-12: Cell with custom views
The List Content View is pinned to the top, leading, and bottom of the cell's
Content View with Space constraints, and our custom button is occupying
the space on the right with Space constraints to the List Content View and
the cell's Content View. Now the area available for the text, secondary text,
and image is not the entire cell but the space left by the button.
To produce this layout, we must work with a few new elements. First, we
need to access the cell's Content View. The UITableViewCell class includes the
following property for this purpose.

contentView—This property returns a reference to the cell's
Content View. It is a value of type UIView.
Two properties were added to the FoodCell class to store our List Content
Instead of working with the UIListContentView object created by the View and the button, but the configuration is defined inside the
configuration structure, we must create our own. The UIListContentView class updateConfiguration() method, as before. The UIListContentView class includes
includes the following initializer. the configuration property to set and return the configuration structure
assigned to the view. The property returns a value of type
UIListContentView(configuration: UIContentConfiguration that we must cast to the data type of the configuration
UIListContentConfiguration)—This initializer creates a List Content structure we used to create the view (in this case, UIListContentConfiguration).
View with the configuration defined by the argument. If the casting is successful, we get a copy for the current state, then assign
the item's values to the configuration properties, and finally assign the
The configuration of the cell doesn't change, but now the configuration configuration back to the view.
structure is provided by our custom UIListContentView object. The following Every time the cell calls the updateConfiguration() method to get the
example shows the process we follow to create the cell of Figure 10-12. configuration for the cell, we call our createViews() method to create the
views. This method must create the List Content View and the UIButton to
Listing 10-11: Configuring a cell with a custom List Content View delete the cell, along with all the constraints necessary to achieve the
 layout in Figure 10-12.
import UIKit
Listing 10-12: Defining the views for a custom cell
class FoodCell: UITableViewCell {

private var customView: UIListContentView!
private var customButton: UIButton! func createViews() {
var item: ItemsData! guard contentView.viewWithTag(999) == nil else { return }
button is pressed, the item is deleted from the model and the cell is
customButton.leadingAnchor.constraint(equalTo: customView.trailingAnchor, constant: 8).isActive
= true removed.
customButton.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant:
-16).isActive = true
Figure 10-13: Cells with custom views
customButton.centerYAnchor.constraint(equalTo: customView.centerYAnchor).isActive = true
customButton.widthAnchor.constraint(equalToConstant: 30).isActive = true
customButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
}

The createViews() method is called every time the state of the cell changes, so
we need to make sure that the views are created only once. For this
purpose, we assign the number 999 to the button's tag property and check
whether a view with that tag already exists inside the cell's Content View. If
no view is found, we proceed to create them.
The views and the constraints are created as before. We create the object,
set the translatesAutoresizingMaskIntoConstraints property to false to stop the
system from defining the constraints for us, add the view to the container
(in this case the cell's Content View), and then assign the constraints.
The action for the button executes a method called eraseItem() which
purpose is to erase the item and update the snapshot to show the change 
on the screen, as shown below.
Do It Yourself: Update the FoodCell class with the code in Listing 10-
Listing 10-13: Deleting a custom cell 11. Add to the class the methods in Listings 13-12 and 13-13. Run
 the application and press the trashcan button to erase a cell.
func eraseItem() {
AppData.items.removeAll(where: { $0.id == item.id })
Custom views added to a standard cell are not part of the cell's
var currentSnapshot = AppData.dataSource.snapshot() configuration and therefore they are not optimized by the system. If we
currentSnapshot.deleteItems([item.id])
AppData.dataSource.apply(currentSnapshot)
want our custom views to be part of the cell's configuration, or to define a
} configuration with custom elements, we must provide our own

configuration structure and UIView subclass. To define the configuration,
UIKit includes the UIContentConfiguration protocol, which requires the
With these changes, the cell now contains a List Content View with a
implementation of the following methods.
standard configuration to present an image, a text, and a secondary text,
along with a button on the right-hand side to delete the item. When the
makeContentView()—This method is called by the cell when it to store a reference to our configuration structure. Along with
configuration
needs a Content View. The method must return a UIView object that this property, we must also create the elements the cell needs to display
conforms to the UIContentView protocol. the values on the screen. For instance, the class in the following example
creates a label and an Image View to show the item's name and image, and
updated(for: State)—This method is called by the cell to get the
adds them to the view with the necessary constraints to place the name on
configuration structure for a state. The method must return the
the left and the image on the right.
configuration structure we want to implement for the state specified
by the for argument. Listing 10-15: Defining a custom Content View

The definition of the configuration structure is simple. We must include class CustomContentView: UIView, UIContentView {
properties to store the values that the cell is going to display and let picture = UIImageView(frame: .zero)
let name = UILabel(frame: .zero)
implement the two required methods.
var configuration: UIContentConfiguration {
Listing 10-14: Defining a custom configuration structure didSet {
newConfiguration()
 }
struct CustomConfig: UIContentConfiguration { }
var name: String! init(configuration: UIContentConfiguration) {
var picture: UIImage! self.configuration = configuration
super.init(frame: .zero)
func makeContentView() -> UIView & UIContentView {
let content = CustomContentView(configuration: self) picture.translatesAutoresizingMaskIntoConstraints = false
return content picture.contentMode = .scaleAspectFit
} self.addSubview(picture)
func updated(for state: UIConfigurationState) -> CustomConfig {
return self let cp1 = picture.widthAnchor.constraint(equalToConstant: 100)
} let cp2 = picture.heightAnchor.constraint(equalToConstant: 100)
} let cp3 = picture.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -16)
 let cp4 = picture.topAnchor.constraint(equalTo: self.topAnchor, constant: 10)
let cp5 = picture.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -10)
cp5.priority = .defaultLow
In this example, the cell is going to show the item's name and image, so we self.addConstraints([cp1, cp2, cp3, cp4, cp5])
include two properties called name and picture to store those values. The
name.translatesAutoresizingMaskIntoConstraints = false
makeContentView() method creates and returns our custom Content View name.numberOfLines = 1
(called CustomContentView in this example), and because the configuration in name.font = UIFont.preferredFont(forTextStyle: .title1)
self.addSubview(name)
this case is going to be the same for every state, the updated() method just
returns the instance of the structure. let cn1 = name.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 16)
For the Content View, we need a subclass of UIView that conforms to the let cn2 = name.trailingAnchor.constraint(equalTo: picture.leadingAnchor, constant: 0)
let cn3 = name.centerYAnchor.constraint(equalTo: picture.centerYAnchor)
UIContentView protocol, which only requirement is a property called self.addConstraints([cn1, cn2, cn3])
With the configuration structure and the Content View ready, we can
newConfiguration()
} finally configure the cell. The process is the same as before, but instead of
func newConfiguration() { using a standard configuration, we implement our CustomConfig structure.
if let config = self.configuration as? CustomConfig {
name.text = config.name
picture.image = config.picture Listing 10-16: Implementing a custom configuration
} 
}
required init?(coder: NSCoder) { import UIKit
fatalError("Error")
} class FoodCell: UITableViewCell {
} var item: ItemsData!

override func updateConfiguration(using state: UICellConfigurationState) {
var config = CustomConfig().updated(for: state)
The CustomContentView class defined in Listing 10-15 is initialized from the config.name = item.name
makeContentView() method in the configuration structure. When this method config.picture = UIImage(named: item.image)
self.contentConfiguration = config
is executed, it sends the configuration structure to the CustomContentView }
initializer. In this initializer, we assign that value to the configuration property }

and proceed to create the views. We create the UIImageView object first, add
it to the Content View, and define the constraints to place it on the right
When the CustomConfig structure is assigned to the cell's contentConfiguration
side of the cell. Then we do the same for the UILabel object, which is pinned
property, the cell calls the makeContentView() method, this method returns
to the left and centered with the image.
the CustomContentView object, the view created by this object is assigned as
The values of these elements change after they are created and each time
the cell's Content View, and the values are shown on the screen.
a new configuration is assigned to the view. To apply these changes, we
included a method called newConfiguration() and call it whenever necessary.
Figure 10-14: Table with custom cells
The method gets the configuration structure by casting the value of the
configuration property to our custom data type (CustomConfig) and updates the
elements with the current values.
left side of each row when they are in edition mode. It must return a called EditingStyle included in the UITableViewCell class that indicates the
UISwipeActionsConfiguration object containing the UIContextualAction objects type of operation performed. The possible values are delete and insert.
that define the actions. sectionIndexTitles(for: UITableView)—This method is called by
tableView(UITableView, the table to get the strings that represent the sections in the table’s
trailingSwipeActionsConfigurationForRowAt: IndexPath)— index. The method must return an array of strings with the values for
This method is called by the table to get the actions to display on the the indexes.
right side of each row when they are in edition mode. It must return a tableView(UITableView, sectionForSectionIndexTitle: String,
UISwipeActionsConfiguration object containing the UIContextualAction objects at: Int)—This method is called by the table to get the section
that define each action. corresponding to an index title. The method must return an integer
value with the section’s index. The sectionForSectionIndexTitle
Although the data is provided by a diffable data source, the table still relies argument is a string with the index’s title, and the at argument is the
on methods defined by the UITableViewDataSource protocol for advanced
position where the title is located on the index.
configuration. The following are the most frequently used.
tableView(UITableView, moveRowAt: IndexPath, to:
tableView(UITableView, titleForHeaderInSection: Int)—This IndexPath)—This method is called when the user moves a cell to a
method is called by the table to get the title for the header of the different position on the table. It is implemented along with the
section indicated by the titleForHeaderInSection argument. The edition tools provided by Table Views. The moveRowAt argument is
method must return a String value with the header’s title. the index path in which the cell is currently located and the to
argument is the index path where the cell is going to be placed.
tableView(UITableView, titleForFooterInSection: Int)—This
method is called by the table to get the title for the footer of the tableView(UITableView, canMoveRowAt: IndexPath)—This
section indicated by the titleForFooterInSection argument. The method is called when the user tries to move a cell to a different
method must return a String value with the footer’s title. position. The method must return a Boolean value to indicate if the
action is allowed. The canMoveRowAt argument indicates the cell's
tableView(UITableView, canEditRowAt: IndexPath)—This
location.
method is called by the table to know if the user is allowed to edit a
row. The method must return a Boolean value to indicate if the row at
The methods defined by the UITableViewDelegate protocol are easy to
the path determined by the canEditRowAt argument can be edited.
implement. All we need to do is to define an object that conforms to the
tableView(UITableView, commit: EditingStyle, forRowAt: protocol (usually the view controller) and assign it as the table's delegate,
IndexPath)—This method is called by the table when the user inserts so all the protocol methods are called on it, as shown next.
or deletes a row. It is implemented along with the edition tools
provided by Table Views. The commit argument is an enumeration Listing 10-17: Selecting multiple items
 }

import UIKit
class ViewController: UIViewController, UITableViewDelegate { In this example, we make the view controller conform to the
@IBOutlet weak var myTable: UITableView! UITableViewDelegate protocol and assign it as the Table View's delegate using
override func viewDidLoad() { the delegate property provided by the UITableView class. Assigning the value
super.viewDidLoad() self to this property, the view controller becomes the table's delegate, and
myTable.register(UITableViewCell.self, forCellReuseIdentifier: "myCell")
myTable.delegate = self we can proceed to implement the protocol methods we need. In this case,
prepareDataSource() we implement the tableView(UITableView, didSelectRowAt:) method to modify the
prepareSnapshot()
}
value of the item's selected property every time the user selects a cell.
func prepareDataSource() { First, we get the item's identifier from the cell's index path with the
AppData.dataSource = UITableViewDiffableDataSource<Sections, ItemsData.ID>(tableView: itemIdentifier() method provided by the data source. The itemIdentifier()
myTable) { tableView, indexPath, itemID in
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) method takes the IndexPath value received by the protocol method, which
if let item = AppData.items.first(where: { $0.id == itemID }) { contains the indexes of the section and the row where the selected cell is
cell.configurationUpdateHandler = { cell, state in
var config = cell.defaultContentConfiguration().updated(for: state)
located, and returns the corresponding identifier for the item in the cell.
config.text = item.name With the identifier, we get the item from the model, as before, and modify
config.secondaryText = "\(item.calories) Calories"
its values. In this example, we toggle the value of the item's selected
config.image = UIImage(named: item.image)
config.imageProperties.maximumSize = CGSize(width: 40, height: 40) property with the toggle() method. If the value was true, it becomes false, and
cell.contentConfiguration = config vice versa, indicating whether the item is selected or not.
cell.accessoryType = item.selected ? .checkmark : .none Because we are modifying the value of the item's selected property every
} time a cell is selected, we don't need the table to show the selected cell
}
return cell
anymore, so we deselect it with the Table View's deselectRow() method. To
} show the user which items are currently selected and which not, we add
} an accessory view to the cell. The UITableViewCell class includes the
func prepareSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>() accessoryType property to define the type of accessory we want to show.
snapshot.appendSections([.main]) When we want to display the accessory, we must assign one of the values
snapshot.appendItems(AppData.items.map({ $0.id }))
AppData.dataSource.apply(snapshot) available or the value none to hide it. In our example, we use the checkmark
} type. If the value of the selected property is true, we assign this value to the
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
accessoryType property and a checkmark is shown on the screen for that
if let itemID = AppData.dataSource.itemIdentifier(for: indexPath) {
if let item = AppData.items.first(where: { $0.id == itemID }) { item, otherwise, the value assigned to the property is none and the
item.selected.toggle() accessory is hidden. The result is shown below.
}
}
tableView.deselectRow(at: indexPath, animated: true) Figure 10-15: Selected cells highlighted with Checkmark accessories
}
Tables in iOS are meant to work along with other scenes to provide more
functionality. The addition of rows, for example, requires a separate scene
to provide the fields and buttons the user needs to insert the information.
This is the reason why we frequently see applications with tables
embedded in Navigation Controllers. Navigation Controllers present scenes
with a transition that feels more natural to the user and helps establish the
relationship between the table and the rest of the scenes. Figure 10-16
shows what a scene with a table looks like after it is embedded in a
Navigation Controller.
The addition of new items is very simple, we provide the form for the user
to insert the data and then store it in the model (In our case, this is the
items array in the AppData structure). To keep it simple, we have included
only a Text Field to let the user insert the name of the item (Figure 10-19,
right). The rest of the information is filled with placeholders ("noimage"
and "Not Defined"). The following is the code for the view controller of the
new scene (we call it AddItemViewController).
import UIKit NSDiffableDataSourceSnapshotobject and provide all the values again as we did
class AddItemViewController: UIViewController { in the prepareSnapshot() method before.
@IBOutlet weak var newItem: UITextField!
The view controller includes an Outlet for the Text Field called newItem and
an Action for the button called saveItem(). When the button is pressed, the
saveItem() method trims the text in the Text Field with the trimmingCharacters()
method (see Listing 5-53), and then compares it with an empty string. If
the user’s input is not empty, the code capitalizes the string, creates the
ItemsData structure to store the information for the new item, and adds it to
the items array with the append() method.
This process modifies the model, but we still need to update the snapshot
to provide the new information to the data source and update the table. In
this case, we must consider that the model sorts the data in alphabetical
order and therefore we can't just append the new item to the current
snapshot. Items appended to an existent snapshot are added at the end of
the list, but the position for the new item may be different, depending on
the name inserted by the user. In consequence, we must create a new
Deleting Rows
Listing 10-22: Removing the selected item


@IBAction func deleteItem(_ sender: UIButton) {
The same way we can add items we can also delete them. The process is as if selected != nil {
simple as deleting the item from the model and updating the snapshot, but AppData.items.removeAll(where: { $0.id == selected.id })
there are several ways to allow the user to do it. An alternative is to include var currentSnapshot = AppData.dataSource.snapshot()
a button in the Detail View that the user can press to delete the item. In currentSnapshot.deleteItems([selected.id])
that scene, we know what item was selected by the user because we have AppData.dataSource.apply(currentSnapshot)
a reference in the selected property, and the user also knows what item is navigationController?.popViewController(animated: true)
going to be deleted because the item's information is on the screen. Figure }
}
10-20 shows the Detail View of our example modified to include a Delete 
button below the thumbnail.
The removeAll(where:) method implemented in the code in Listing 10-22 reads
Figure 10-20: Delete button the value of the id property of each item in the array and removes the item
which value matches the value of the id property of the selected item.
After the item is removed from the model, we also remove it from the
current snapshot with the deleteItems() method, and the scene is closed.
includes an editing mode that when activated shows a button on the left to
delete the item and a button on the right to confirm the action.
 
The process to activate and deactivate this mode involves the isEditing The purpose of the Edit button is to activate or deactivate the edition
property, the setEditing() method (both from the UITableView class), and two mode depending on its current state. To determine the state, we can read
methods defined in the UITableViewDataSource protocol: the the isEditing property, and to set the new state we must call the setEditing()
tableView(UITableView, canEditRowAt:) method to tell the table whether a row method, as in the following example.
can be edited or not, and the tableView(UITableView, commit:, forRowAt:) method
to process the deletion. Listing 10-23: Activating and deactivating the editing mode
The first step is to provide a way for the user to decide when to activate or 
deactivate the mode. For our example, we have added a Bar Button Item to @IBAction func editItems(_ sender: UIBarButtonItem) {
if myTable.isEditing {
the Navigation Bar called Edit. myTable.setEditing(false, animated: true)
} else {
myTable.setEditing(true, animated: true)
Figure 10-22: Edit button }
}

The editItems() method in Listing 10-23 checks the value of the isEditing
property and sets the mode to false if the property is true, or to true if the
property is false. When the value is set to true, the Table View calls the
tableView(UITableView, canEditRowAt:) method on the data source delegate and
allows the user to edit the rows or not, based on the value returned by the the action performed by the user. If the value is delete, which means the
method (true or false). user wants to delete the row, we delete the item from the model and the
Data source delegate methods are implemented by the diffable data snapshot following the same procedure used before in the
source, so if we want to provide our own implementation, we must define DetailViewController class (see Listing 10-22). Notice that this time we
a subclass of the UITableViewDiffableDataSource class. For this example, we reference the data source with self because we are inside a
have created a Swift file called MyDataSource.swift to store our subclass UITableViewDiffableDataSource subclass.
called MyDataSource. To implement this subclass in our code, we must define the dataSource
property in our model with the MyDataSource data type and create the
Listing 10-24: Implementing the data source delegate methods diffable data source from this subclass.

import UIKit Listing 10-25: Implementing our data source subclass

class MyDataSource: UITableViewDiffableDataSource<Sections, ItemsData.ID> {
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool func prepareDataSource() {
{ AppData.dataSource = MyDataSource(tableView: myTable) { tableView, indexPath, itemID in
return true let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
} if let item = AppData.items.first(where: { $0.id == itemID }) {
override func tableView(_ tableView: UITableView, commit editingStyle: var config = cell.defaultContentConfiguration()
UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) { config.text = item.name
if editingStyle == UITableViewCell.EditingStyle.delete { cell.contentConfiguration = config
if let itemID = self.itemIdentifier(for: indexPath) { }
AppData.items.removeAll(where: { $0.id == itemID }) return cell
}
var currentSnapshot = self.snapshot() }
currentSnapshot.deleteItems([itemID]) 
self.apply(currentSnapshot)
}
}
The prepareDataSource() method in Listing 10-25 replaces the same method in
} our ViewController class, but the only difference is that now instead of
} creating the diffable data source from the UITableViewDiffableDataSource class,

we do it from our subclass (This example assumes that we have defined
The MyDataSource subclass in Listing 10-24 implements the two methods we the dataSource property in our model with the data type MyDataSource, as in
var dataSource: MyDataSource!).
need to manage the editing mode. First, we override the
tableView(UITableView, canEditRowAt:) method and return the value true. This
tells the table that all the rows are editable. Next, we override the Do It Yourself: Add a Bar Button Item with the title "Edit" to the
tableView(UITableView, commit:, forRowAt:) method. This method is called when Navigation Bar at the top of the Table View (Figure 10-22). Connect
the user presses the Delete button on the table (see Figure 10-21, right). the button to an Action in the ViewController class called editItems() and
The method receives an EditingStyle value with a value that corresponds to complete the method with the code in Listing 10-23. Create a new
Swift file called MyDataSource.swift for the code in Listing 10-24. UISwipeActionsConfiguration(actions: [UIContextualAction])
Open the ApplicationData.swift file and change the data type of the —This initializer returns an object with the actions provided by the
dataSource property to MyDataSource (var dataSource: MyDataSource!). argument and the configuration defined by its properties.
Replace the prepareDataSource() method in the ViewController class with performsFirstActionWithFullSwipe—This is a Boolean property
the method in Listing 10-25. Run the application and press the Edit that determines if the first action included in the object is going to be
button. You should see the edition buttons appear on the table's left- executed when the user performs a full swipe.
hand side. Press one of these buttons. The row should displace to
the left and reveal the Delete button on the right. Press this button To define the buttons and actions, we must create an object of the
to delete the row. UIContextualAction class. The following are its initializer and properties.
IMPORTANT: Tables include a hidden feature that simplifies the UIContextualAction(style: Style, title: String?, handler:
deletion of rows and the execution of custom tasks. If the delegate Closure)—This initializer returns a UIContextualAction object with the
methods are implemented, as we did in the MyDataSource subclass, definition of a button. The style argument defines the style of the
the feature is activated, which allows the user to uncover the Delete button. It is an enumeration called Style included in the
button by swiping the row to the left. If this feature is enough for UIContextualAction class. The values available are normal (color gray) and
your application, there is no need to add an Edit button to activate destructive (color red). The title argument is a string with the text to be
or deactivate the mode, all you need to do is implement the
shown on the button, and the handler argument is the closure that is
delegate methods.
going to be executed when the action is performed. The closure
receives three values: a reference to the action, a reference to the
We can also show our own buttons on the left and right side of the rows
implementing the methods tableView(UITableView, button’s view, and another closure that takes a Boolean to declare
trailingSwipeActionsConfigurationForRowAt:) and tableView(UITableView, whether the action was successful or not.
leadingSwipeActionsConfigurationForRowAt:) defined by the UITableViewDelegate title—This property sets or returns a string with the button’s title.
protocol. These methods are called when the user swipes a row to one side backgroundColor—This property sets or returns a UIColor object
or the other and are in charge of defining the buttons displayed by the
with the button’s background color.
table and the actions they perform. For this purpose, UIKit includes two
classes: the UISwipeActionsConfiguration class, to provide the actions and image—This property sets or returns a UIImage object with the
configuration values, and the UIContextualAction class to define each button button’s image.
and action. The following are the initializer and property provided by the
UISwipeActionsConfiguration class. If we implement these methods in our view controller instead of the
tableView(UITableView, commit:, forRowAt:) method in the diffable data source,
the Delete button is replaced by our own buttons. For instance, in the
following example we create our own Delete button with a blue This method is defined by the UITableViewDelegate protocol, so we must
background and a different title. implement it in the ViewController class and make the class conform to the
protocol, as done before (see Listing 10-18). The method replaces the
Listing 10-26: Creating our own actions tableView(UITableView, commit:, forRowAt:) method implemented in the
 MyDataSource subclass of Listing 10-24, but we still have to implement the
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: tableView(UITableView, canEditRowAt:) method in the data source to tell the
IndexPath) -> UISwipeActionsConfiguration? { table that the user is allowed to edit the rows.
let button = UIContextualAction(style: .normal, title: "Remove", handler: { (action, view,
completion) in
if let itemID = AppData.dataSource.itemIdentifier(for: indexPath) { Do It Yourself: Add the method in Listing 10-26 to the ViewController
AppData.items.removeAll(where: { $0.id == itemID })
class. Remove the tableView(UITableView, commit:, forRowAt:) from the
var currentSnapshot = AppData.dataSource.snapshot() MyDataSource class. Run the application and swipe a row to the left.
currentSnapshot.deleteItems([itemID])
AppData.dataSource.apply(currentSnapshot) You should see a blue button with the title "Remove". Press the
}
button to remove the row.
completion(true)
})
button.backgroundColor = UIColor.systemBlue
When the user swipes a row to the left, the table calls the method in
Listing 10-26 to know which buttons to display. In this example, we create
an action called button with the style normal, the title "Remove", and a
closure that performs the same action as before (it gets the item's
identifier, it removes it from the model, and updates the snapshot). Notice
that at the end of the closure we call the completion handler received by
the third argument with the value true to indicate that the action was
performed successfully, otherwise the button is not removed. Finally, we
create a new UISwipeActionsConfiguration object with this action, set its
performsFirstActionWithFullSwipe property to false so the user cannot perform
the action with a full swipe, and return it.
To allow users to modify the items, we must provide a form where they can
insert the new values. For our example, we have added a button called Edit
 Item to the Detail View (Figure 10-24, number 1) and a scene connected to
this button with a Show segue. The scene presents the same interface used
To move a cell, the user must drag the cell from the icon to the new before to add items (Figure 10-24, number 2).
position. In the example of Figure 10-23, we moved the Cookies cell to the
second row. When the change is performed, the Table View calls the Figure 10-24: Button and interface to modify items
delegate methods of Listing 10-27 and the item corresponding to the cell is
moved to an index that reflects its new position on the table.
Table Views that take up the entire screen are very common in mobile
applications. To simplify the creation of these tables, the UIKit framework
provides a subclass of the UIViewController class called UITableViewController,
which includes a full-screen table. The class has several advantages over
adding the Table View ourselves. It conforms to the Table View protocols
by default, the controller is fully integrated with Navigation Controllers,
search bars and controls to refresh the data, and it even offers another
type of cells called Static Cells that allow us to present the cells as they are
defined in the Storyboard. The Library includes the Table View Controller
option to add this view controller to the interface.
Figure 10-26: Table View Controller in the Storyboard Listing 10-30: Managing the table from a subclass of UITableViewController

import UIKit
func prepareDataSource() { have to deselect the cell anymore, as we did in the example of
AppData.dataSource = UITableViewDiffableDataSource<Sections, ItemsData.ID>(tableView:
tableView) { tableView, indexPath, itemID in Listing 10-20. The UITableViewController class offers a Boolean property
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) called clearsSelectionOnViewWillAppear to activate or deactivate this
if let item = AppData.items.first(where: { $0.id == itemID }) {
var config = cell.defaultContentConfiguration()
feature.
config.text = item.name
cell.contentConfiguration = config
}
return cell
}
}
func prepareSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
snapshot.appendSections([.main])
snapshot.appendItems(AppData.items.map({ $0.id }))
AppData.dataSource.apply(snapshot)
}
}

The UITableView class inherits the following property from the UIScrollView
Table View Controllers include a feature usually provided by modern
class to assign the control to the table.
applications that allow the user to refresh the data by scrolling down the
table. When the user keeps scrolling down the table, a spinning wheel
appears at the top to indicate that the system is refreshing the data. refreshControl—This property sets or returns a reference to the
Refresh Control associated to the table.
Figure 10-27: Refresh Control
When the user scrolls down the table, the Table View Controller shows the
Refresh Control and fires a valueChanged event to indicate to the code that it
should perform the corresponding tasks. Therefore, to respond to the
control, we must add an action to the Refresh Control for this event with
the addAction(for:) method, as shown in the following example.

Listing 10-31: Presenting a Refresh Control

The graphics are created by the Table View Controller, but UIKit defines the
import UIKit
UIRefreshControl class to manage the control. The class includes the following
properties and methods to configure the control and report the state of class MyTableViewController: UITableViewController {
var refresh: UIRefreshControl!
the process.
override func viewDidLoad() {
super.viewDidLoad()
tintColor—This property sets or returns a UIColor value that defines tableView.register(UITableViewCell.self, forCellReuseIdentifier: "myCell")
the control's color. prepareDataSource()
prepareSnapshot()
attributedTitle—This property sets or returns the text shown in the
control. It is a value of type NSAttributedString. refresh = UIRefreshControl()
refresh.addAction(UIAction(handler: { [unowned self] action in
isRefreshing—This is a Boolean property that indicates if the control self.refreshTable()
}), for: .valueChanged)
is in the process of refreshing the data.
let text = AttributedString("Refreshing Table")
beginRefreshing()—This method tells the control that the process refresh.attributedTitle = NSMutableAttributedString(text)
of refreshing the data was initiated. tableView.refreshControl = refresh
}
func refreshTable() {
prepareSnapshot()
up to us to take this input and select the items in our model that match the method is case sensitive, so we lowercased both strings before comparing).
term. There are different ways to do it. When working with arrays, one Because the model manages the search, the view controller is simple. We
alternative is to use the filter() method introduced in Chapter 3. The need to create the search controller and conform to the
UISearchResultsUpdating protocol to process the input.
Listing 10-33: Processing the input from the search controller The first thing we do in this view controller is to create a search controller.
 In the initializer, we specify the nil value to declare this view controller as
import UIKit the one responsible for showing the results. The view controller is also
assigned as the delegate of the search controller through the
class MyTableViewController: UITableViewController, UISearchResultsUpdating {
override func viewDidLoad() { searchResultsUpdater property, and the search controller is finally assigned to
super.viewDidLoad() the searchController property of the Navigation Item to show the search bar
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "myCell")
prepareDataSource() on the screen.
prepareSnapshot() When the user inserts a character in the search bar, the search controller
calls the updateSearchResults(for:) method with a reference to the search
let searchController = UISearchController(searchResultsController: nil)
searchController.searchResultsUpdater = self controller. From this reference, we read the value inserted by the user,
searchController.obscuresBackgroundDuringPresentation = false assign it to the searchValue property in the model, and then update the
navigationItem.searchController = searchController
} snapshot with the prepareSnapshot() method. (Notice that the snapshot is
func prepareDataSource() { created with the values of the filteredItems property instead of the items
AppData.dataSource = UITableViewDiffableDataSource<Sections, ItemsData.ID>(tableView:
tableView) { tableView, indexPath, itemID in
property.) The result is shown below.
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
if let item = AppData.items.first(where: { $0.id == itemID }) { Figure 10-28: Search controller in the Navigation Bar
var config = cell.defaultContentConfiguration()
config.text = item.name
cell.contentConfiguration = config
}
return cell
}
}
func prepareSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
snapshot.appendSections([.main])
snapshot.appendItems(AppData.filteredItems.map({ $0.id }))

AppData.dataSource.apply(snapshot)
}
func updateSearchResults(for searchController: UISearchController) {
Do It Yourself: Embed the Table View Controller in a Navigation
if let text = searchController.searchBar.text { Controller. Insert the title Groceries for the scene. Add the code in
AppData.searchValue = text.trimmingCharacters(in: .whitespaces)
prepareSnapshot()
Listing 10-32 to the ApplicationData structure (below the definition of
} the items property). Update the MyTableViewController class with the
}
} code in Listing 10-33. Run the application. You should see the search
 bar at the top. Insert a value. The table should be updated with the
items that match the value, as shown in Figure 10-28.
IMPORTANT: In the example of Listing 10-33, we assigned the Search Bar
value false to the obscuresBackgroundDuringPresentation property to not let

the controller obscure the interface when the bar is selected, but
you can also assign the value false to the
The bar created by the controller is an object of the UISearchBar class, which
hidesNavigationBarDuringPresentation property to keep the Navigation Bar
includes several methods and properties for configuration. In the example
visible and take advantage of the hidesSearchBarWhenScrolling property
of Listing 10-33, we used the text property to get the value inserted by the
of the Navigation Item to keep the search bar visible when the table
user, but there are more available.
is scrolled up.
text—This property sets or returns the text in the field. It is an
optional of type String.
placeholder—This property sets or returns the placeholder. It is an
optional of type String.
tintColor—This property sets or returns the color of the bar’s
elements. It is an optional of type UIColor.
The UISearchBar class can also designate its own delegate object to report
the state of the bar. The delegate must conform to the UISearchBarDelegate
protocol and implement its methods.
searchBarTextDidBeginEditing(UISearchBar)—This method is
called by the bar on its delegate when the edition begins (the user
taps on the bar to perform a search).
searchBarTextDidEndEditing(UISearchBar)—This method is
called by the bar on its delegate when the edition ends.
searchBarCancelButtonClicked(UISearchBar)—This method is 
called by the bar on its delegate when the Cancel button is pressed. var searchValue: String = ""
var selectedButton: Int = 0
searchBar(UISearchBar, var filteredItems: [ItemsData] {
get {
selectedScopeButtonIndexDidChange: Int)—This method is if searchValue.isEmpty {
called by the bar on its delegate when a button in the scope bar is return items
} else {
pressed. The button's index is provided by the second argument. var list = items.filter( { (item) -> Bool in
if selectedButton == 0 {
let value1 = item.name.lowercased()
The last method works with a feature provided by search bars called Scope
let value2 = searchValue.lowercased()
Bar (see Figure 10-29, below). This is an additional bar displayed below the return value1.hasPrefix(value2)
search bar with selectable buttons (like Segmented Controls). When a } else if selectedButton == 1 {
if let maximum = Int(searchValue), item.calories < maximum {
button is selected, the others are automatically deselected. The UISearchBar return true
class includes the following properties to create and configure these }
}
buttons. return false
})
list.sort(by: { (value1, value2) in value1.name < value2.name })
showsScopeBar—This property is a Boolean value that determines return list
whether the scope bar is displayed or not. When the value is set to }
}
true, an additional bar is shown at the bottom of the Search Bar with
}
buttons to select the scope of the search. 
Listing 10-34: Expanding the model to work with a scope bar Listing 10-35: Adding a scope bar
 }
}
import UIKit func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange
selectedScope: Int) {
class MyTableViewController: UITableViewController, UISearchResultsUpdating, AppData.selectedButton = selectedScope
UISearchBarDelegate { prepareSnapshot()
override func viewDidLoad() { searchBar.placeholder = selectedScope == 0 ? "Search Product" : "Maximum Calories"
super.viewDidLoad() searchBar.text = ""
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "myCell") }
prepareDataSource() }
prepareSnapshot() 
dequeueReusableHeaderFooterView(withIdentifier: String)—
This method returns a UITableViewHeaderFooterView object to represent a
header or footer view. The object is created from the prototype with
the identifier defined by the withIdentifier argument.
Like cells, section headers are created from prototypes. After the Listing 10-39: Configuring the headers
tableView(UITableView, titleForHeaderInSection:) method returns the title for the 
header, the Table View creates a header view from a prototype view and import UIKit
shows it on the screen. Although this is enough for some applications, we class MyTableViewController: UITableViewController {
can define and configure our own prototype. The UITableView class includes override func viewDidLoad() {
super.viewDidLoad()
the following methods to register a prototype and create a view from it.
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "myCell")
tableView.register(UITableViewHeaderFooterView.self,
register(AnyClass?, forHeaderFooterViewReuseIdentifier: forHeaderFooterViewReuseIdentifier: "myHeader")
prepareDataSource()
String)—This method registers a prototype header or footer view. prepareSnapshot()
The fist argument is the class that is going to be used to create the }
func prepareDataSource() {
cells (the UITableViewHeaderFooterView class or a subclass of it), and the AppData.dataSource = MyDataSource(tableView: tableView) { tableView, indexPath, itemID in
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
forHeaderFooterViewReuseIdentifier argument specifies the string if let item = AppData.items.first(where: { $0.id == itemID }) {
the table is going to use to identify this prototype view. var config = cell.defaultContentConfiguration()
config.text = item.name
cell.contentConfiguration = config
}
return cell
}
}
func prepareSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Sections.ID, ItemsData.ID>()
snapshot.appendSections(AppData.sections.map({ $0.id }))
for section in AppData.sections {
let itemIDs = AppData.items.compactMap({ value in
return value.section == section.name ? value.id : nil
})
snapshot.appendItems(itemIDs, toSection: section.id)
}
AppData.dataSource.apply(snapshot)
}
override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) ->
UIView? {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "myHeader")!
Figure 10-31: Table with custom headers provide an array with the titles for the indexes, and the tableView(UITableView,
sectionForSectionIndexTitle:, at:) method to specify the section to which the
Table View Controllers offer the possibility to create static tables. Static
tables present a fixed number of cells and therefore are particularly useful
when we have a limited number of options or predefined data to display.
To turn the table of a Table View Controller into a static table, we must
select it and change the Content option in the Attributes Inspector panel to
Static Cells.


From this point on, the cells and their content are designed in the
Once the option is selected, the table replaces the prototype cells with Storyboard by dragging the elements from the Library to the cells, as we do
three custom cells. with any other view. We can add new cells by dragging the Table View Cell
option from the Library, and new sections may be added by modifying the
Figure 10-34: Empty static cells in an Inset Grouped table value of the Section option from the Attributes Inspector panel. When a
section is selected, the Attributes Inspector panel offers options to specify
the titles for the section's header and footer.
If the scene in the Storyboard is not tall enough to fit all the cells we need,
we can set a different size from the Simulated Size option in the Size
Inspector panel, as we did for Scroll Views in Chapter 7. Figure 10-35,
below, shows a possible design. For this example, we changed the table’s
style to Inset Grouped, use custom cells, and added labels and a section
with a single cell to present an Image View.
The custom cells for a static table do not need their own UITableViewCell
subclass; all we need to do is to connect the elements directly to Outlets in
the view controller. The following is the code for the UITableViewController
subclass necessary to control the interface of Figure 10-35.
Modify the sizes of the cells from the Size Inspector panel to fit the
elements and add labels to the cells of the first section and an Image
View to the cell of the second section. Assign the necessary
constraints to get the interface in Figure 10-35. Select the sections to
change their titles. Create a subclass of UITableViewController called
MyTableViewController and assign it to the scene. Complete the class
with the code in Listing 10-42 and connect the elements on the cells
to the Outlets in the code. Run the application. You should see
something like Figure 10-36.
Collection Views are like Table Views, with the difference that they can
present the information with a customizable layout. They come with a grid-
like layout by default, but we can modify it or replace it entirely to present
the views any way we want.
The following are some of the properties and methods included in the
UICollectionView class to configure the view.
The views displayed by a Collection View are also called cells. Cells in a
Collection View are created from the UICollectionViewCell class, but they do
not include any standard elements by default. Therefore, to create a cell,
we must define a subclass of the UICollectionViewCell class and add our own
views to the cell's Content View. The class includes the following property
for this purpose.
Just like the cells in a Table View, these cells are created from prototype
cells. To register a prototype cell, the UICollectionView class defines the
CellRegistration structure.
Collection Views can designate a delegate to respond to user interaction. As with tables, the snapshot is created from the NSDiffableDataSourceSnapshot
UIKit includes the UICollectionViewDelegate protocol to define this delegate class, but UIKit defines a specific class to create the diffable data source for
object. The following are the methods we need to implement to respond Collection Views called UICollectionViewDiffableDataSource. The class provides
to the selection of a cell. the following initializer.
 The FoodCell class includes a property called picture to store the Image View.
This is the property that we access later to configure the cell with the
The Collection View included with the scene comes with a prototype cell image of each item. When the cell is initialized, we set up the Image View
(the square at the top of the scene in Figure 11-4). Before iOS 14, this was with Space constraints to pin it to the sides of the Content View.
the recommended way to create our prototype cells, but modern cells are The steps to set up the Collection View are the same as for Table Views,
but the process is different. The configuration of the cell is done by the
closure assigned to the CellRegistration structure, so all that is left to do for
the diffable data source is to create the cell with the Next, we create the diffable data source. Because the cell is already
dequeueConfiguredReusableCell() method and return it. (For this example, we are configured by the CellRegistration structure, all we need to do here is to call
implementing the same model used for Table Views in Chapter 10, Listing the dequeueConfiguredReusableCell() method with a reference to the
10-1. The only difference is that now the diffable data source must be configuration structure to create the cell and return it.
defined with the UICollectionViewDiffableDataSource class.) The cells, the diffable data source, and the snapshot define the content of
the Collection View, but how the cells are going to be laid out is defined by
Listing 11-2: Configuring a Collection View a layout object. Collection Views come with a default layout called Flow
 layout. This layout is configured to position the cells in a grid, which means
import UIKit the cells will be displayed in as many rows and columns as the available
space allows.
class MyCollectionViewController: UICollectionViewController {
override func viewDidLoad() {
super.viewDidLoad() Figure 11-5: Flow layout
let cellRegistration = UICollectionView.CellRegistration<FoodCell, ItemsData.ID> { cell,
indexPath, itemID in
if let item = AppData.items.first(where: { $0.id == itemID }) {
cell.picture.image = UIImage(named: item.image)
}
}
AppData.dataSource = UICollectionViewDiffableDataSource<Sections, ItemsData.ID>
(collectionView: collectionView) { (collection, indexPath, itemID) in
return collection.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath,
item: itemID)
}
var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
snapshot.appendSections([.main]) 
snapshot.appendItems(AppData.items.map({ $0.id }))
AppData.dataSource.apply(snapshot)
} Do It Yourself: Create a new project. Remove the initial scene and
} add a Collection View Controller from the Library. Select the scene

and activate the option Is Initial View Controller from the Attributes
The CellRegistration structure is generic. To create an instance, we specify the Inspector panel. Create a file with a subclass of UICollectionViewCell
cell's data type (FoodCell) and the data type we use to identify the values in called FoodCell. Update this class with the code in Listing 11-1. Create
the model (ItemsData.ID). The initializer creates the prototype cell and a Swift file called ApplicationData.swift with the model in Chapter
provides the closure to configure it. This closure is called later every time a 10, Listing 10-1. Define the dataSource property in the model with the
new cell is required by the Collection View. In our example, we get the item UICollectionViewDiffableDataSource class (var dataSource:
from the item's ID, and assign the item's image to the cell's picture property. UICollectionViewDiffableDataSource<Sections, ItemsData.ID>!). Create a
subclass of UICollectionViewController called MyCollectionViewController.
Update this class with the code in Listing 11-2. Download the } else {
backgroundConfig.backgroundColor = .systemBackground
thumbnails from our website and add them to the Assets Catalog. }
Run the application. You should see something like Figure 11-5. cell.backgroundConfiguration = backgroundConfig
}
Rotate the device to see how the layout adapts the cells to the space }
AppData.dataSource = UICollectionViewDiffableDataSource<Sections, ItemsData.ID>
available.
(collectionView: collectionView) { (collection, indexPath, itemID) in
return collection.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item:
Collection View cells can be selected, but they do not show any indication itemID)
}
to the user (the background color doesn't change). If we want to change }
the background every time a cell is selected or deselected, we must apply a func prepareSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
background configuration. Like Table View cells, Collection View cells snapshot.appendSections([.main])
include the configurationUpdateHandler property to take a closure that we can snapshot.appendItems(AppData.items.map({ $0.id }))
AppData.dataSource.apply(snapshot)
use to configure the cell every time the state changes, and the
}
backgroundConfiguration property to assign the background configuration to }
the cell. The implementation is the same used for Table Views (see 
if state.isSelected { 
backgroundConfig.backgroundColor = .systemGray5
Flow Layout

How the cells are laid out in a collection view is determined by an object
created from a subclass of the UICollectionViewLayout class. Although we can
create our own subclass to define any layout we want, UIKit includes a
subclass called UICollectionViewFlowLayout that provides a highly customizable
grid-like layout called Flow layout. This is the layout assign to the Collection
View by default and the one we have been using so far. In our examples,
we implemented this layout with standard settings, but the class includes
the following properties for configuration.
collectionView(UICollectionView, layout:
UICollectionViewLayout, sizeForItemAt: IndexPath)—This
method is called by the layout object to get the size of a cell. It must
return a CGSize value with the size of the cell at the location indicated
by the sizeForItemAt argument.
collectionView(UICollectionView, layout: 
UICollectionViewLayout, insetForSectionAt: Int)—This method
Below the Cell Size option there is an option called Estimate Size (Figure
is called by the layout object to get the UIEdgeInsets structure with the
11-7, number 1). If the value of this option is Automatic or Custom, the
inset values for the section (the margins). The insetForSectionAt
cells are display with the size determined in this panel, but we can set the
argument is an integer with the section’s index.
size from the layout by selecting the option None. When the size of the
collectionView(UICollectionView, layout: cells is not determined by the Collection View, we can set it from the layout
UICollectionViewLayout, minimumLineSpacingForSectionAt: object, as shown next.
Int)—This method is called by the layout object to get the CGFloat
value that determines the minimum space between lines in a section. Listing 11-4: Configuring Flow layout
The minimumLineSpacingForSectionAt argument is an integer with 
The layout object calls the protocol methods on the delegate assigned to
the Collection View’s delegate property, so we must designate our view
controller as the delegate for these methods to be called. The Flow layout
calls the collectionView(UICollectionView, layout:, sizeForItemAt:) method every time
it needs to know the size of a cell to calculate the layout. In this example,
we decided to assign a size of 146x100 points to every cell, except when
the index of the cell is multiple of 3. When the remainder of the division of
the index by 3 is equal to 0, we assign a size of 292x200 points. As a result,
every three items, the cell is displayed in a larger size.
Custom Layout

With Flow layout, we can achieve many designs, but the number of cells
displayed in a row or column is always determined by the layout according
to the space available. Flow layout gets the dimensions of the Collection
View, subtracts the margins and the space between cells, and then
positions the cells that fit into the remaining space. If, for example, we set
the minimumInteritemSpacing property to a value too big to fit two cells in the
same row, Flow layout will show only one cell and move the second cell to 
a new row. The only way to make sure that the cells are organized the way
we want, is to define a custom layout object. The items are grouped horizontally or vertically, the groups repeat over
Although we can create our own subclass of the UICollectionViewLayout class and over again to show all the items available, and one or multiple sections
to define a custom layout, the framework includes a more flexible can be used to present these groups. Each part is highly configurable and
alternative called Compositional Layouts. Compositional layouts are flexible, starting with the layout itself. The following are the initializers
defined by the UICollectionViewCompositionalLayout class and their main included in the UICollectionViewCompositionalLayout class to create the layout.
characteristic is that they can organize (compose) a layout from smaller
parts. There are three main parts in a compositional layout: the item (a UICollectionViewCompositionalLayout(section:
cell), the group (a group of cells), and the section (a subdivision of the NSCollectionLayoutSection)—This initializer creates a
layout that can include one or multiple groups). Figure 11-10, below, compositional layout with the section specified by the argument.
illustrates these parts in a simple layout.
UICollectionViewCompositionalLayout(section:
Figure 11-10: Compositional layout
NSCollectionLayoutSection, configuration:
UICollectionViewCompositionalLayoutConfiguration)—This
initializer creates a compositional layout with the section specified by
the section argument and with the configuration specified by the
configuration argument.
UICollectionViewCompositionalLayout(sectionProvider:
Closure)—This initializer creates a compositional layout with the
section returned by the closure specified by the sectionProvider
argument.
UICollectionViewCompositionalLayout(sectionProvider: The NSCollectionLayoutItem class also includes the following properties to
Closure, configuration: configure the items.
UICollectionViewCompositionalLayoutConfiguration)—This
initializer creates a compositional layout with the section returned by contentInsets—This property sets or returns a structure of type
the closure specified by the sectionProvider argument and with the NSDirectionalEdgeInsets with the insets for the content's left, right, top,
and bottom sides. These values are defined from the structure's
configuration specified by the configuration argument.
initializer (NSDirectionalEdgeInsets(top: CGFloat, leading: CGFloat, bottom:
CGFloat, trailing: CGFloat)).
The layout is configured with an object of the
UICollectionViewCompositionalLayoutConfiguration class. The class includes the edgeSpacing—This property sets or returns an object of type
following properties to configure the layout. NSCollectionLayoutEdgeSpacing that defines the space on the sides of the
layout. The space is defined with an object of type
interSectionSpacing—This property sets or returns a CGFloat value NSCollectionLayoutSpacing, which includes the type methods fixed(CGFloat)
that determines the space between sections. for fixed values, and flexible(CGFloat) for flexible values. The values for
each side are declared by the NSCollectionLayoutEdgeSpacing class'
scrollDirection—This property sets or returns a value that
initializer (NSCollectionLayoutEdgeSpacing(leading: NSCollectionLayoutSpacing?,
determines the scrolling orientation. It is an enumeration of type
top: NSCollectionLayoutSpacing?, trailing: NSCollectionLayoutSpacing?, bottom:
ScrollDirection with the values vertical and horizontal.
NSCollectionLayoutSpacing?)).
Once we have the layout, we must define its parts. The items are defined
The items are organized in groups. The framework includes the
by the NSCollectionLayoutItem class. The class includes the following
NSCollectionLayoutGroup class to create them. The class includes the following
initializers.
methods to define and configure a group.
NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize)
—This initializer creates an item of the size specified by the layoutSize horizontal(layoutSize: NSCollectionLayoutSize, subitems:
argument. [NSCollectionLayoutItem])—This method returns a group with the
items in horizontal order. The layoutSize argument determines the
NSCollectionLayoutItem(layoutSize: NSCollectionLayoutSize,
size of the group, and the subitems argument determines the type of
supplementaryItems: items the group is going to contain. The number of items in the group
[NSCollectionLayoutSupplementaryItem])—This initializer is determined by the space available.
creates an item of the size specified by the layoutSize argument for
the supplementary views specified by the supplementaryItems horizontal(layoutSize: NSCollectionLayoutSize, subitem:
argument. NSCollectionLayoutItem, count: Int)—This method returns a
group with the items in horizontal order. The layoutSize argument
determines the size of the group, the subitem argument determines
the type of items the group is going to contain, and the count the group argument.
argument specifies how many.
vertical(layoutSize: NSCollectionLayoutSize, subitems: To configure the section, the NSCollectionLayoutSection class includes the
[NSCollectionLayoutItem])—This method returns a group with the following properties.
items in vertical order. The layoutSize argument determines the size
of the group, and the subitems argument determines the type of contentInsets—This property sets or returns a structure of type
items the group is going to contain. The number of items in the group NSDirectionalEdgeInsets with the inset for the left, right, top and bottom
is determined by the space available. sides of the section. These values are defined from the structure's
initializer (NSDirectionalEdgeInsets(top: CGFloat, leading: CGFloat, bottom:
vertical(layoutSize: NSCollectionLayoutSize, subitem:
CGFloat, trailing: CGFloat)).
NSCollectionLayoutItem, count: Int)—This method returns a
group with the items in vertical order. The layoutSize argument interGroupSpacing—This property sets or returns a CGFloat value
determines the size of the group, the subitem argument determines that determines the space between groups.
the type of items the group is going to contain, and the count orthogonalScrollingBehavior—This property sets or returns a
argument specifies how many. value that determines the section's scrolling behavior. When it is
custom(layoutSize: NSCollectionLayoutSize, itemProvider: declared, the section scrolls perpendicular to the layout. It is an
Closure)—This method returns a group with the size determined by enumeration of type UICollectionLayoutSectionOrthogonalScrollingBehavior
the layoutSize argument and the item returned by the closure with the values continuous, continuousGroupLeadingBoundary, groupPaging,
groupPagingCentered, paging, and none.
assigned to the itemProvider argument.
The NSCollectionLayoutGroup class also includes a property to specify the The size of items and groups is determined by objects of type
NSCollectionLayoutSize. The class includes the following initializer.
space between items.
NSCollectionLayoutSection class. The class includes the following initializer. absolute(CGFloat) to provide absolute values, estimated(CGFloat) to provide
a size estimate, fractionalWidth(CGFloat) to calculate the value from the
NSCollectionLayoutSection(group: NSCollectionLayoutGroup) width of the container, and fractionalHeight(CGFloat) to calculate the
—This initializer creates a section with the type of groups specified by
value from the height of the container. The fractional values are }
func createLayout() -> UICollectionViewCompositionalLayout {
specified with numbers from 0.0 to 1.0. let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.5),
heightDimension: .fractionalWidth(0.5))
let item = NSCollectionLayoutItem(layoutSize: itemSize)
It is time to see an example of how to compose a complex layout with all item.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 5, bottom: 5, trailing: 10)
these tools. The process is simple, we must create a
UICollectionViewCompositionalLayout object with a definition of the layout's let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
heightDimension: .fractionalWidth(0.5))
items, groups, and sections, and assign it to the Collection View. In the let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
following example, we create this object from a method and then call the
let section = NSCollectionLayoutSection(group: group)
method from viewDidLoad() to create the custom layout and assign it to the
Collection View as soon as the scene is loaded. let layout = UICollectionViewCompositionalLayout(section: section)
return layout
}
Listing 11-6: Assigning a compositional layout to the Collection View }
 
import UIKit
The process to define a compositional layout is declarative. We define the
class MyCollectionViewController: UICollectionViewController { object that configures the items, then the object that configures the
override func viewDidLoad() {
super.viewDidLoad() groups, then the object that configures the sections, and finally the layout
let customLayout = createLayout() object that contains the sections. In the createLayout() method of Listing 11-
collectionView.collectionViewLayout = customLayout
prepareDataSource() 6, we begin by defining the size of the items. To adapt the items to the
prepareSnapshot() space available, we use fractional values. The width dimension was
}
declared as half the width of the container, and we do the same for the
func prepareDataSource() {
let cellRegistration = UICollectionView.CellRegistration<FoodCell, ItemsData.ID> { cell, height. This means that the system is going to get the width of the group,
indexPath, itemID in divide it by half (width * 0.5), and assign the result to the item's width and
if let item = AppData.items.first(where: { $0.id == itemID }) {
cell.picture.image = UIImage(named: item.image) height. For instance, if the screen has a width of 320 points and the group
} is declared of the same size, the items are going to have a size of 160 by
}
AppData.dataSource = UICollectionViewDiffableDataSource<Sections, ItemsData.ID>
160 points (320 * 0.5). With this size, we instantiate the
(collectionView: collectionView) { (collection, indexPath, itemID) in NSCollectionLayoutItem object and then assign a new value to the contentInsets
return collection.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: property to separate the items from each other and the edges of the
itemID)
} container.
} With the items configured, we proceed to define the group that is going to
func prepareSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>() contain those items. First, the size of the group is declared with fractional
snapshot.appendSections([.main]) values. This time, we assign a width that is 100% the size of the group's
snapshot.appendItems(AppData.items.map({ $0.id }))
container (fractionalWidth(1)) and a height that is half the width. Again, if the
AppData.dataSource.apply(snapshot)
screen is 320 points wide, the group is going to have a width of 320 points
and a height of 160. Next, we use the horizontal() type method to create the Listing 11-7: Customizing a compositional layout
group using the size we just calculated and an array with the 
NSCollectionLayoutItem object defined before. This asks the layout to align the func createLayout() -> UICollectionViewLayout {
items in the group horizontally and fit as many as possible. Finally, the let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.2),
heightDimension: .fractionalWidth(0.2))
section is defined with this group and the layout is defined with that let item = NSCollectionLayoutItem(layoutSize: itemSize)
section. item.contentInsets = NSDirectionalEdgeInsets(top: 10, leading: 5, bottom: 5, trailing: 10)
This example creates a simple layout, like the one built with a Flow layout let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension:
before, but with the difference that the size of the items is determined by .fractionalWidth(0.2))
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
the space available. The layout organizes the items in one section and one
group, and because the size of each item was specified as half the width of let section = NSCollectionLayoutSection(group: group)
the container, only two are included per group, no matter the space
let layout = UICollectionViewCompositionalLayout(section: section)
available. return layout
}

Figure 11-11: Compositional layout
In this example, the width and height of the items were declared as 20%
the width of the container, which means that a total of five items will fit in
a group. Notice that the height of the group was defined as 20% of the
width of the container, so it matches the size of the items.
Although we can use whatever value we want to identify the view, the The SupplementaryRegistration structure is generic, which means it needs to
UICollectionView class includes the following type properties with predefined
know the data type of the view is going to use to create the prototype
identifiers for headers and footers. views. UIKit defines the UICollectionReusableView class to create these views.
The process is the same we have used before for custom cells in Table
elementKindSectionHeader—This property returns a string to Views. We must define the properties to reference the elements in the
identify a header. view and then create those elements when the view is initialized. The
following example defines a reusable view with a background image and a
elementKindSectionFooter—This property returns a string to label at the center.
identify a footer.
Listing 11-8: Defining a reusable view

textView.translatesAutoresizingMaskIntoConstraints = false
textView.font = UIFont.preferredFont(forTextStyle: .title1) After the cells are defined as always, we create a SupplementaryRegistration
self.addSubview(textView) structure using our view's data type (MyHeader). In the closure, we initialize
textView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
the properties of the view with a background image called gradientTop
textView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true (available on our website) and the title "My Food".
} Creating supplementary views from this prototype is simple. We just have
required init?(coder: NSCoder) {
fatalError("Error") to assign a closure to the supplementaryViewProvider property of the diffable
} data source, call the dequeueConfiguredReusableSupplementary() method with the
}

registration structure, and return the view produced by it, as we did for
cells before.
Once we have our view class, we can register the prototype and create the This creates the views, but they are not included in the Collection View.
supplementary views. The following are the changes we need to introduce This is the responsibility of the layout object. For instance, Compositional
to our prepareDataSource() method. layout works with NSCollectionLayoutBoundarySupplementaryItem objects to
define headers and footers. The class includes the following initializer.
Listing 11-9: Defining the supplementary views
 NSCollectionLayoutBoundarySupplementaryItem(layoutSize:
func prepareDataSource() { NSCollectionLayoutSize, elementKind: String, alignment:
let cellRegistration = UICollectionView.CellRegistration<FoodCell, ItemsData.ID> { cell,
indexPath, itemID in
NSRectAlignment)—This initializer creates a layout item called
if let item = AppData.items.first(where: { $0.id == itemID }) { boundary supplementary item that is used to add headers and footers
cell.picture.image = UIImage(named: item.image)
}
to a section. The layoutSize argument determines the size of the view,
} the elementKind argument is the identifier we used to create the
view, and the alignment argument determines the position of the 
The snapshot is the same we used for Table Views. The following is the
prepareSnapshot() method for this example.
The information included in the sections of our example belong to the As we will see in the next section of this chapter, section snapshots are
same topic. In this case, all items represent food. But Collection Views can frequently used to create hierarchical lists, but they are also useful when
work with information provided by multiple sources or with different the information of each section is unique. For example, the following
characteristics. For instance, a Collection View may include two sections, model defines two sections, one for the items selected by the user and
one to show the items available and another to display those selected by another for the items that are still available to select.
the user. In cases like this, it may be useful to create a snapshot for each
section. The framework provides the NSDiffableDataSourceSectionSnapshot class Listing 11-13: Defining a model to test section snapshots
for this purpose. As a normal snapshot, the class includes all the properties 
and methods required to add and remove items, but also some that are import UIKit
specific to section snapshots. The following are the most frequently used.
enum Sections {
case selected, available
items—This property returns an array with the identifiers of the }
class ItemsData: Identifiable {
items in the section. var id: UUID = UUID()
var name: String
rootItems—This property returns an array with the identifiers of the var image: String
items at the top of the snapshot's hierarchy. var calories: Int
var selected: Bool
append([ItemIdentifierType], to: ItemIdentifierType?)—This
init(_ name: String, _ image: String, _ calories: Int, _ selected: Bool) {
method adds the items specified by the first argument to the section self.name = name
specified by the to argument. self.image = image
self.calories = calories
contains(ItemIdentifierType)—This method returns a Boolean self.selected = selected
}
value that determines whether the item specified by the argument is }
included in the section. struct ApplicationData {
var dataSource: UICollectionViewDiffableDataSource<Sections, ItemsData.ID>!
expand([ItemIdentifierType])—This method expands the section var items: [ItemsData] = [] {
to show the items specified by the argument. didSet {
items.sort(by: { $0.name < $1.name })
collapse([ItemIdentifierType])—This method collapses the section }
}
to hide the items specified by the argument. init() {
items.append(contentsOf: [ItemsData("Bagels", "bagels", 250, false), ItemsData("Brownies",
isExpanded(ItemIdentifierType)—This method returns a Boolean "brownies", 466, false), ItemsData("Butter", "butter", 717, false), ItemsData("Cheese", "cheese", 402,
value to indicate whether the item specified by the argument is in the false), ItemsData("Coffee", "coffee", 0, false), ItemsData("Cookies", "cookies", 502, false),
ItemsData("Donuts", "donuts", 452, false), ItemsData("Granola", "granola", 471, false),
expanded state. ItemsData("Juice", "juice", 23, false), ItemsData("Lemonade", "lemonade", 40, false),
ItemsData("Lettuce", "lettuce", 15, false), ItemsData("Milk", "milk", 42, false),
ItemsData("Oatmeal", "oatmeal", 68, false), ItemsData("Potatoes", "potato", 77, false),
ItemsData("Tomatoes", "tomato", 18, false), ItemsData("Yogurt", "yogurt", 59, false)])
} }
} AppData.dataSource.supplementaryViewProvider = { view, kind, indexPath in
var AppData = ApplicationData() return self.collectionView.dequeueConfiguredReusableSupplementary(using:
 headerRegistration, for: indexPath)
}
}
The sections are identified by the Sections enumeration. The enumeration func prepareSnapshot() {
includes two values: selected, for the selected items, and available, for the var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
snapshot.appendSections([.selected, .available])
items available to select. The diffable data source is defined with this data AppData.dataSource.apply(snapshot, animatingDifferences: true)
type, but the rest of the code in the model is the same as before.
In the view controller, we need to create two section snapshots and then let selectedIDs = AppData.items.compactMap({ value in
return value.selected ? value.id : nil
update their content when an item is selected by the user, as shown next. })
var selectedSnapshot = NSDiffableDataSourceSectionSnapshot<ItemsData.ID>()
selectedSnapshot.append(selectedIDs)
Listing 11-14: Moving items between section snapshots AppData.dataSource.apply(selectedSnapshot, to: .selected, animatingDifferences: false)

let availableIDs = AppData.items.compactMap({ value in
import UIKit
return value.selected ? nil : value.id
})
class MyCollectionViewController: UICollectionViewController {
var availableSnapshot = NSDiffableDataSourceSectionSnapshot<ItemsData.ID>()
override func viewDidLoad() {
availableSnapshot.append(availableIDs)
super.viewDidLoad()
AppData.dataSource.apply(availableSnapshot, to: .available, animatingDifferences: false)
let customLayout = createLayout()
}
collectionView.collectionViewLayout = customLayout
func createLayout() -> UICollectionViewLayout {
prepareDataSource()
let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.33),
prepareSnapshot()
heightDimension: .fractionalWidth(0.33))
}
let item = NSCollectionLayoutItem(layoutSize: itemSize)
func prepareDataSource() {
let cellRegistration = UICollectionView.CellRegistration<FoodCell, ItemsData.ID> { cell,
let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1),
indexPath, itemID in
heightDimension: .fractionalWidth(0.33))
if let item = AppData.items.first(where: { $0.id == itemID }) {
let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
cell.picture.image = UIImage(named: item.image)
}
let section = NSCollectionLayoutSection(group: group)
}
let headerSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
AppData.dataSource = UICollectionViewDiffableDataSource<Sections, ItemsData.ID>
heightDimension: .absolute(60))
(collectionView: collectionView) { (collection, indexPath, itemID) in
let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(layoutSize: headerSize,
return collection.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item:
elementKind: UICollectionView.elementKindSectionHeader, alignment: .top)
itemID)
section.boundarySupplementaryItems = [sectionHeader]
}
let headerRegistration = UICollectionView.SupplementaryRegistration<MyHeader>
let layout = UICollectionViewCompositionalLayout(section: section)
(elementKind: UICollectionView.elementKindSectionHeader) { headerView, kind, indexPath in
return layout
if let sectionID = AppData.dataSource.sectionIdentifier(for: indexPath.section) {
}
headerView.pictureView.image = UIImage(named: "gradientTop")
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath:
headerView.textView.text = sectionID == .selected ? "Selected" : "Available"
IndexPath) {
}
if let itemID = AppData.dataSource.itemIdentifier(for: indexPath) {
if let item = AppData.items.first(where: { $0.id == itemID }) { UICollectionViewDelegateprotocol, which is called every time the user taps on a
if item.selected {
var selectedSnapshot = AppData.dataSource.snapshot(for: .selected) cell. In this method, we perform two operations, depending on whether
selectedSnapshot.delete([itemID]) the item was previously selected (the value of the item's selected property is
AppData.dataSource.apply(selectedSnapshot, to: .selected, animatingDifferences: true)
true). First, we delete the item from the current section and then create a
item.selected = false
new snapshot to update the other section (If the item is selected, we
let availableIDs = AppData.items.compactMap({ value in remove it from the selected section and update the available section, or vice
return value.selected ? nil : value.id
}) versa).
var availableSnapshot = NSDiffableDataSourceSectionSnapshot<ItemsData.ID>() Now we have two sections that manage different information.
availableSnapshot.append(availableIDs)
AppData.dataSource.apply(availableSnapshot, to: .available, animatingDifferences: false)
} else { Figure 11-15: Section snapshots
var availableSnapshot = AppData.dataSource.snapshot(for: .available)
availableSnapshot.delete([itemID])
AppData.dataSource.apply(availableSnapshot, to: .available, animatingDifferences: true)
item.selected = true
list(using: UICollectionLayoutListConfiguration)—This type To turn a Collection View into a list, we must get the List Layout with the
method creates a compositional layout that displays the views on a list() method, assign it to the Collection View, register a
UICollectionViewListCell, and configure the cell as we did before for Table
list. The using argument is a structure that defines the style for the
Views.
list.
Listing 11-15: Creating a Collection View List
Like Table Views, a Collection View List can have different styles or

appearances. This configuration is determined by a
import UIKit
UICollectionLayoutListConfiguration structure.
class MyCollectionViewController: UICollectionViewController {
override func viewDidLoad() {
UICollectionLayoutListConfiguration(appearance: super.viewDidLoad()
Appearance)—This initializer creates a configuration structure to let config = UICollectionLayoutListConfiguration(appearance: .grouped)
let layout = UICollectionViewCompositionalLayout.list(using: config)
define the appearance of the list. The appearance argument is an collectionView.collectionViewLayout = layout
enumeration with the values plain, grouped, insetGrouped, sidebar, and
prepareDataSource()
sidebarPlain. prepareSnapshot()
}
func prepareDataSource() {
To create the cells, the framework includes the UICollectionViewListCell class. let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell,
The class implements the defaultContentConfiguration() method to configure the ItemsData.ID> { cell, indexPath, itemID in
content and the following properties for the accessories and layout. if let item = AppData.items.first(where: { $0.id == itemID }) {
var config = cell.defaultContentConfiguration()
config.text = item.name
config.secondaryText = "Calories: \(item.calories)"
config.image = UIImage(named: item.image)
headerMode—This property sets or returns an enumeration value Listing 11-17: Adding supplementary views to the list
that determines the type of header to use for the list. The values 
available are none, supplementary, and firstItemInSection. import UIKit
footerMode—This property sets or returns an enumeration value class MyCollectionViewController: UICollectionViewController {
that determines the type of footer to use for the list. The values override func viewDidLoad() {
super.viewDidLoad()
available are none and supplementary. var config = UICollectionLayoutListConfiguration(appearance: .grouped)
config.headerMode = .supplementary
let layout = UICollectionViewCompositionalLayout.list(using: config)
As always, we need a UICollectionReusableView subclass to design the header. collectionView.collectionViewLayout = layout
The following example includes a label with a headline style at the center of
the view. prepareDataSource()
prepareSnapshot()
}
Listing 11-16: Defining the header for a list func prepareDataSource() {
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell,
 ItemsData.ID> { cell, indexPath, itemID in
import UIKit if let item = AppData.items.first(where: { $0.id == itemID }) {
var config = cell.defaultContentConfiguration()
class MyHeader: UICollectionReusableView { config.text = item.name
var textView = UILabel() config.secondaryText = "Calories: \(item.calories)"
config.image = UIImage(named: item.image)
override init(frame: CGRect) { config.imageProperties.maximumSize = CGSize(width: 60, height: 60)
super.init(frame: frame) cell.contentConfiguration = config
textView.translatesAutoresizingMaskIntoConstraints = false }
textView.font = UIFont.preferredFont(forTextStyle: .headline) }
self.addSubview(textView) AppData.dataSource = UICollectionViewDiffableDataSource<Sections.ID, ItemsData.ID>
(collectionView: collectionView) { (collection, indexPath, itemID) in
textView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true return collection.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item:
textView.topAnchor.constraint(equalTo: self.topAnchor, constant: 16).isActive = true itemID)
textView.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -16).isActive = true }
}
Update the MyCollectionViewController class with the code in Listing 11- topSeparatorInsets—This property sets or returns an
17. Run the application. You should see the headers with the title of value that defines the padding around the
NSDirectionalEdgeInsets
each section. separator at the top of the cell.
bottomSeparatorInsets—This property sets or returns an
Another aspect of the list we can configure are the separators (the lines value that defines the padding around the
NSDirectionalEdgeInsets
between cells). The UICollectionLayoutListConfiguration structure includes the
separator at the bottom of the cell.
following properties for this purpose.
A list displays lines between the cells, but also at the top and bottom of
showsSeparators—This property sets or returns a Boolean value
each section. We can modify this behavior from the closure assigned to the
that determines whether the separators are displayed or not. itemSeparatorHandler property. In the following example, we change the color
separatorConfiguration—This property sets or returns a value that of the separators to red and then remove the separators at the top and
defines the configuration for the separators. It is a structure of type bottom of each section.
UIListSeparatorConfiguration.
Listing 11-18: Configuring cell's separators

override func viewDidLoad() {
super.viewDidLoad()
var config = UICollectionLayoutListConfiguration(appearance: .grouped)
config.headerMode = .supplementary
config.separatorConfiguration.color = .systemRed
var lastRow = 0
if let sectionID = AppData.dataSource.sectionIdentifier(for: section) {
lastRow = AppData.dataSource.snapshot().numberOfItems(inSection: sectionID)
lastRow = lastRow > 0 ? lastRow - 1 : 0
}
var configuration = config
configuration.topSeparatorVisibility = row == 0 ? .hidden : .automatic
configuration.bottomSeparatorVisibility = row == lastRow ? .hidden : .automatic
return configuration
}
let layout = UICollectionViewCompositionalLayout.list(using: config)
collectionView.collectionViewLayout = layout
prepareDataSource() 
prepareSnapshot()
}
As Table Views, Collection View Lists can also include accessories. These

are small icons on the left or right side of the cell that incorporate
The code in Listing 11-18 assigns a closure to the itemSeparatorHandler additional functionality. The accessories are defined by the UICellAccessory
property to configure the separators cell by cell. To know if the current cell structure. The following are some of its methods.
is the first one in the section, we just read the index and hide the top
separator when it is equal to 0 (The first row is always at the index 0), but disclosureIndicator(displayed: DisplayedState, options:
to know whether the cell is the last one in the section, we get the number DisclosureIndicatorOptions)—This method returns an accessory
of items in the section minus 1 and compare that value with the index of that indicates that there is information to disclose. The displayed
the current cell. If they match, it means that the cell is the last one in the argument indicates when the accessory is displayed, and the options
section, so we hide the bottom separator. argument defines the accessory's configuration.
checkmark(displayed: DisplayedState, options:
Figure 11-17: Custom separators
CheckmarkOptions)—This method returns an accessory that
presents a checkmark. The displayed argument indicates when the config.image = UIImage(named: item.image)
config.imageProperties.maximumSize = CGSize(width: 60, height: 60)
accessory is displayed, and the options argument defines the cell.contentConfiguration = config
accessory's configuration.
let selected = item.selected
delete(displayed: DisplayedState, options: DeleteOptions, cell.accessories = selected ? [.checkmark()] : []
}
actionHandler: Closure)—This method returns an accessory that }
allows the user to delete a cell. The displayed argument indicates 
All these accessories use similar values for configuration. For instance, a override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath:
IndexPath) {
value of the DisplayedState enumeration determines when the accessory is if let itemID = AppData.dataSource.itemIdentifier(for: indexPath) {
going to be displayed. The values available are always, whenEditing, and if let item = AppData.items.first(where: { $0.id == itemID }) {
item.selected.toggle()
whenNotEditing. The options for each accessory are defined by specific
structures, but they usually share the same properties, like isHidden to hide var current = AppData.dataSource.snapshot()
the accessory, or tintColor to change its color. Although we can modify any current.reconfigureItems([itemID])
AppData.dataSource.apply(current)
of these values, usually those assigned by default are more than enough.
For instance, in the following example we add an accessory of type collectionView.deselectItem(at: indexPath, animated: true)
}
checkmark to the cells when they are selected or remove it when not. }
}

Listing 11-19: Adding accessories to the cells

Figure 11-18: Checkmark accessories
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, ItemsData.ID>
{ cell, indexPath, itemID in
if let item = AppData.items.first(where: { $0.id == itemID }) {
var config = cell.defaultContentConfiguration()
config.text = item.name
config.secondaryText = "Calories: \(item.calories)"
trailingSwipeActionsConfigurationProvider—This property
takes a closure to configure swipe actions for the cell's trailing edge.
The closure receives an IndexPath value with the location of the cell and
must return a UISwipeActionsConfiguration object with the buttons we
want to show.
The process to add these buttons to the cell is the same we used before for
Table Views. The buttons are defined with the UIContextualAction class, and
then the UISwipeActionsConfiguration object is initialized with these values and
returned. The following example adds a trailing button to delete the cell.
config.trailingSwipeActionsConfigurationProvider = { indexPath in
let button = UIContextualAction(style: .normal, title: "Remove", handler: { (action, view,

completion) in
if let itemID = AppData.dataSource.itemIdentifier(for: indexPath), let sectionID =
Collection View Lists can also implement functionality to allow users to AppData.dataSource.sectionIdentifier(for: indexPath.section) {
AppData.items.removeAll(where: { $0.id == itemID })
delete cells. The system works in the same way as with Table Views. The
UICollectionView class includes the isEditing property to activate or deactivate var currentSnapshot = AppData.dataSource.snapshot()
currentSnapshot.deleteItems([itemID])
the editing mode (true or false), and the UICollectionLayoutListConfiguration if currentSnapshot.numberOfItems(inSection: sectionID) <= 0 {
structure includes the following properties to provide the actions for the AppData.sections.removeAll(where: { $0.id == sectionID })
swipe gesture. currentSnapshot.deleteSections([sectionID])
}
AppData.dataSource.apply(currentSnapshot)
leadingSwipeActionsConfigurationProvider—This property }
completion(true)
takes a closure to configure swipe actions for the cell's leading edge. })
The closure receives an IndexPath value with the location of the cell and let config = UISwipeActionsConfiguration(actions: [button])
return config
must return a UISwipeActionsConfiguration object with the buttons we }
want to show. let layout = UICollectionViewCompositionalLayout.list(using: config)
collectionView.collectionViewLayout = layout
containers for other items, called children, that the user can see by tapping
prepareDataSource()
prepareSnapshot() on the top item's accessory.
} We don't need anything new to create these types of lists, all the

functionality is provided by section snapshots, but the information in the
model must be organized accordingly. For instance, the following model
The closure assigned to the trailingSwipeActionsConfigurationProvider property in
includes a structure to store all the items, but the structure includes a
Listing 11-21 creates a button with an action that removes the cell and the
property with an array of instances of the same structure to store the
section if there are no cells left. The button is used to create the
items that are going to be shown when the parent item is expanded.
UISwipeActionsConfiguration object, and the object is returned to configure the
Listing 11-22: Defining a model for a hierarchical list
list.

import UIKit
Figure 11-19: Swipe button to delete the cells
enum Sections {
case main
}
struct MainItems: Identifiable {
var id = UUID()
var name: String!
var options: [MainItems]!
}
struct ApplicationData {
var dataSource: UICollectionViewDiffableDataSource<Sections, MainItems.ID>!
let items = [
 MainItems(name: "Food", options: [
MainItems(name: "Oatmeal", options: nil),
MainItems(name: "Bagels", options: nil),
Do It Yourself: Replace the viewDidLoad() method in your MainItems(name: "Brownies", options: nil),
MyCollectionViewControllerclass with the method of Listing 11-21. Run MainItems(name: "Cheese", options: nil),
MainItems(name: "Cookies", options: nil),
the application and swipe a cell to the left. Press the Delete button. MainItems(name: "Donuts", options: nil)
The cell should be deleted. If you want to activate the editing mode, ]),
MainItems(name: "Beverages", options: [
as we did for Table Views in Chapter 10, assign the value true to the MainItems(name: "Coffee", options: nil),
isEditing property of the UICollectionView object (collectionView.isEditing = MainItems(name: "Juice", options: nil),
MainItems(name: "Lemonade", options: nil)
true) and add the delete accessory to the cell (cell.accessories = [.delete()]). ])
]
}
Collection View Lists can also work along with section snapshots to create a var AppData = ApplicationData()
hierarchical list. This is a list in which some items can expand or collapse to 
display or hide other items. The main items, also called parents, work as
The MainItems structure includes the name property to store the item's text, }
func prepareSnapshot() {
and the options property to store the children. In this example, we store two var snapshot = NSDiffableDataSourceSectionSnapshot<MainItems.ID>()
parent items called "Food" and "Beverages", and the children for each for mainItem in AppData.items {
snapshot.append([mainItem.id], to: nil)
parent are stored in the item's options property. When the user taps on the
snapshot.append(mainItem.options.map({ $0.id }), to: mainItem.id)
"Food" or "Beverages" items, all the items stored in their options property }
are shown on the screen. AppData.dataSource.apply(snapshot, to: .main, animatingDifferences: false)
}
For this feature to work, we must mirror this organization in a section func getItem(id: MainItems.ID) -> MainItems? {
snapshot. The section snapshot is created first and then the items are var item = AppData.items.first(where: { $0.id == id })
if item == nil {
added with the append([ItemIdentifierType], to: ItemIdentifierType?) method, where for main in AppData.items {
the to argument is the identifier of the parent to which the children belong if let found = main.options.first(where: { $0.id == id }) {
or nil if the item to be added is a parent item, as shown next. item = found
break
}
Listing 11-23: Creating a hierarchical list }
}
 return item
import UIKit }
}
class MyCollectionViewController: UICollectionViewController { 
override func viewDidLoad() {
super.viewDidLoad()
let config = UICollectionLayoutListConfiguration(appearance: .plain)
The prepareSnapshot() method in Listing 11-23 creates the
let layout = UICollectionViewCompositionalLayout.list(using: config) NSDiffableDataSourceSectionSnapshot structure and then adds all the items from
collectionView.collectionViewLayout = layout the items array. First, we add the parent and then the children stored in the
prepareDataSource() parent's options property. The rest of the code is similar to previous
prepareSnapshot() examples, but because we are working with nested items, we use a
}
func prepareDataSource() { method called getItem() to get the item from the item's identifier. First, the
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, method compares the identifier with the parent's identifier. If the item is
MainItems.ID> { cell, indexPath, itemID in
if let item = self.getItem(id: itemID) {
not found (it is not a parent item), we iterate through the items array to look
var config = cell.defaultContentConfiguration() for the item inside the options array. The item is returned, and the cell is
config.text = item.name configured as before.
cell.contentConfiguration = config
cell.accessories = item.options != nil ? [.outlineDisclosure()] : [] Notice that the cell includes an Outline Disclosure accessory. This is an
} accessory specifically designed to work with hierarchical lists (also known
}
AppData.dataSource = UICollectionViewDiffableDataSource<Sections, MainItems.ID>
as expandable/collapsible outlines). The accessory shows an arrow that
(collectionView: collectionView) { (collection, indexPath, itemID) in points down when the parent item is expanded, as shown below.
return collection.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item:
itemID)
} Figure 11-20: Hierarchical list
The container view controllers studied so far are good for devices with
small screens, such as iPhones and iPods, but the space available in devices
like the iPads and large iPhones in landscape mode demand a more
elaborated design. UIKit includes a subclass of UIViewController called
UISplitViewController that can present up to three scenes at a time: the 
primary scene, the supplementary scene, and the secondary scene.
iPhones in portrait mode present a different configuration; the columns are
Figure 12-1: Split View Controller with three columns displayed as in a Navigation Controller. They replace one another and
buttons are included to navigate back.
The Primary and Supplementary columns are removable, and they are 
presented on the screen depending on the number of columns and the
space available. By default, on iPads in landscape mode, only one of the Split View Controllers can be configured with two or three columns
removable columns is shown. In a two-column design, the Split View (Primary and Secondary, or Primary, Supplementary, and Secondary) and
Controller displays the Primary column, while in a three-column design the the removable columns may be shown on the left of the Secondary column
Supplementary column is shown instead. But on iPads on portrait mode or on top of it. This style is defined by the controller's display mode. There
and large iPhones in landscape mode, only the Secondary column is are a total of six display modes available.
displayed, and a button is provided in the navigation bar to open the
removable columns. Figure 12-4: Display modes for Split View Controllers
Split View Controllers are created from the UISplitViewController class. As with
any other view, they can be added to the interface from code or to the
Storyboard from the Library.
The option in the Library adds to the Storyboard a two-column Split View
Controller, which includes the scenes for the Primary and Secondary
columns. The Primary scene is embedded in a visible Navigation Controller,
like those we have implemented before. On the other hand, the Secondary
scene is a single view embedded in a Navigation Controller that is
automatically assigned to the scene by the Split View Controller, as shown
below.
 The panel includes the Style option to configure the Split View Controller
with two or three columns (Figure 12-7, number 1), and the Display Mode
By default, this Split View Controller is configured with two columns and Behavior options to change the controller's display mode and
(Primary and Secondary), but we can modify this configuration and other behavior, respectively (Figure 12-7, number 2). The Display Mode
values from the Attributes Inspector panel. determines how the columns are shown on the screen, and the split
Behavior determines how the Secondary column is going to behave when
Figure 12-7: Split View Controller configuration the removable columns are visible. There are three values available: Tile,
Overlay, and Displace.
we need to do is to control-drag a line from the Split View Controller to the Split View Controller Configuration
scene and select the type of segue according to their role on the interface.

For this purpose, the Storyboard defines four special segues called
Relationship Segues, as shown next.
Because Split View Controllers automatically expand and collapse to adapt
to the space available, they are perfect to create universal applications that
Figure 12-9: Relationship Segues for Split View Controllers
work on iPads and iPhones, but they still require some configuration. In
addition to the options included in the Attributes Inspector panel, the
UISplitViewController class includes the following properties.
Another useful segue when working with Split View Controllers is the Show displayMode—This property returns the current display mode. It is
Detail segue. This segue is used to connect the Primary or Supplementary an enumeration called DisplayMode with the values secondaryOnly,
scenes to the Secondary scene (the segue is triggered from the Primary or oneBesideSecondary, oneOverSecondary, twoBesideSecondary, twoOverSecondary,
Supplementary scenes to open a scene in the Secondary column). twoDisplaceSecondary, and automatic.
Figure 12-11: Split View controller with custom Primary and Secondary
scenes
Collection Views in Chapter 11), but it also must perform the Show Detail
In this example, we have replaced the Table View Controller included in the segue to open a new scene every time an item is selected.
standard Split View Controller with a Collection View Controller. The scene
for the Secondary column was embedded in a Navigation Controller, and Listing 12-1: Controlling the Primary scene
an Image View and two labels have been added to show the item's values 
on the screen. Notice that the Primary scene was connected to the import UIKit
Navigation Controller of the Secondary scene with a Show Detail segue, so
class PrimaryViewController: UICollectionViewController {
every time an item is selected in the Primary column, its values are shown var selected: ItemsData.ID!
in the Secondary column.
override func viewDidLoad() {
super.viewDidLoad()
Do It Yourself: Create a new project. Erase the initial scene and let config = UICollectionLayoutListConfiguration(appearance: .sidebar)
let layout = UICollectionViewCompositionalLayout.list(using: config)
add a Split View Controller from the Library (Figures 12-5 and 12-6). collectionView.collectionViewLayout = layout
Select the Split View Controller and activate the option Is Initial View
prepareDataSource()
Controller from the Attributes Inspector panel. Replace the Table prepareSnapshot()
View Controller with a Collection View Controller and connect it to }
func prepareDataSource() {
the Navigation Controller with a Root View Controller segue (Figure let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell,
12-11, number 1). Double click on the Navigation Bar to change its ItemsData.ID> { cell, indexPath, itemID in
if let item = AppData.items.first(where: { $0.id == itemID }) {
title to "Food". Embed the Secondary scene in a Navigation var config = cell.defaultContentConfiguration()
Controller and add an Image View and two labels to the scene config.text = item.name
cell.contentConfiguration = config
(Figure 12-11, number 2). Control-drag a line from the Collection }
}
View Controller to the Navigation Controller of the Secondary scene
AppData.dataSource = UICollectionViewDiffableDataSource<Sections, ItemsData.ID>
to create a Show Detail segue (Figure 12-11, number 3). Give the (collectionView: collectionView) { collection, indexPath, itemID in
return collection.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item:
segue the identifier "showSecondary". itemID)
}
}
As always, the first step is to define the data model. For this example, we
func prepareSnapshot() {
are going to use the same model implemented for Table Views in Chapter var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
10 (see Listing 10-1). All we need to remember is to define the diffable data snapshot.appendSections([.main])
snapshot.appendItems(AppData.items.map({ $0.id }))
source to work with a Collection View (var dataSource: AppData.dataSource.apply(snapshot)
UICollectionViewDiffableDataSource<Sections, ItemsData.ID>!). }
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath:
The view controller for the Primary scene must define the diffable data IndexPath) {
source and the snapshot to provide the data to the Collection View (see if let itemID = AppData.dataSource.itemIdentifier(for: indexPath) {
selected = itemID
performSegue(withIdentifier: "showSecondary", sender: nil) class SecondaryViewController: UIViewController {
} @IBOutlet weak var itemThumbnail: UIImageView!
} @IBOutlet weak var itemName: UILabel!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { @IBOutlet weak var itemCalories: UILabel!
if segue.identifier == "showSecondary" { var selected: ItemsData.ID!
if let navigator = segue.destination as? UINavigationController {
let controller = navigator.topViewController as! SecondaryViewController override func viewDidLoad() {
controller.selected = selected super.viewDidLoad()
} if selected != nil {
} if let item = AppData.items.first(where: { $0.id == selected! }) {
} itemThumbnail?.image = UIImage(named: item.image)
} itemName?.text = item.name
 itemCalories?.text = "Calories: \(item.calories)"
}
}
When possible, Apple recommends using a Collection View List with a side }
bar configuration to display the content of the Primary and Supplementary }

columns. In this example, we configure the layout with a sidebar appearance
and the cells to display the item's name, so we get a list of items on the
By default, iPads display the Primary and Secondary columns side by side in
Primary column. When an item is selected, the Collection View Controller
landscape, and only the Secondary column in portrait mode. In landscape
calls the collectionView(UICollectionView, didSelectItemAt:) method. In this method,
mode, the Split View Controller includes a button in the navigation bar to
we get the selected item with the itemIdentifier() method, store the identifier
hide or show the Primary column (Figure 12-12, number 1), but in portrait
in a property, and perform the Show Detail segue (identified with the name
mode the Primary column can be opened with a swipe gesture from the
"showSecondary").
left or by pressing the back button included in the navigation bar (Figure
In the prepare() method, the value of the selected property is sent to the view
12-12, number 2).
controller of the Secondary scene to show the values on the screen. Notice
that the Show Detail segue is connected to the Navigation Controller, so we
Figure 12-12: Split View Controller in landscape and portrait modes
first get a reference to the Navigation Controller of the Secondary scene,
then read the topViewController property of the Navigation Controller to get a
reference to the view controller of the Secondary scene, and finally pass
the value of the selected property to this view controller.
All the view controller of the Secondary scene needs to do is to get the
item from the identifier and show its values on the screen. 
UICollectionViewDiffableDataSource<Sections, ItemsData.ID>!).
Create a file Swipe Gesture
with a subclass of UICollectionViewController called PrimaryViewController

and assign it to the Primary scene. Update the class with the code in
Listing 12-1. Create a file with a subclass of UIViewController called As we already mentioned, Split View Controllers include a gesture and a
SecondaryViewController and assign it to the Secondary scene. Update button that allow the user to change the display mode (show or hide the
the class with the code in Listing 12-2. Connect the elements in the Primary column), but if we want to provide our own tools to control the
scene to their respective Outlets. Download the food thumbnails columns, we can remove these features by assigning the value false to the
from our website and add them to the Assets Catalog. Run the presentsWithGesture property.
application in the iPad and iPhone simulators. Rotate the simulator The property can be modified from any view controller, but a good practice
to see how the Split View Controller responds in every device. is to create a subclass of the UISplitViewController class to take care of the
controller's configuration.
The UISplitViewController subclass in Listing 12-3 assigns the value true to the
presentsWithGesture property as soon as the Split View Controller is loaded.
Now the Split View Controller doesn't offer the built-in features to allow
the user to manipulate the interface.
Display Mode Do It Yourself: Update the MySplitViewController class with the code
in Listing 12-5 (if you haven't created this subclass yet, you can do it

as explained for the example in Listing 12-3). Run the application.
Due to the reduced size of the screens in iPads in portrait mode and large The Primary column should always be visible, regardless of the
iPhones in landscape mode, the Split View Controller presents the columns orientation, as illustrated in Figure 12-14.
with a display mode of type secondaryOnly, which means that only the
secondary column is visible. But we can recommend an alternative mode
and split behavior by assigning a different value to the preferredDisplayMode
and the preferredSplitBehavior properties.
The code in Listing 12-5 recommends the Split View Controller to use the
mode oneBesideSecondary and the split behavior tile (the columns share the
space available). In consequence, the Split View Controller displays the
Primary column beside the Secondary column when possible.

Default Item
Do It Yourself: Update the viewDidLoad() method in your

class with the code in Listing 12-6 and run the
SecondaryViewController
When our application is launched, no item has been selected by the user application. You should see the first item in the model on the screen.
yet, so the Secondary scene has nothing to show. Depending on the
characteristics of our application, we may provide a placeholder or just
select an item at random. For instance, we can modify our
SecondaryViewController class to get the first item in the model when no item
was selected.
if selected == nil {
if let item = AppData.items.first {
selected = item.id
}
}
if let item = AppData.items.first(where: { $0.id == selected! }) {
itemThumbnail?.image = UIImage(named: item.image)
itemName?.text = item.name
itemCalories?.text = "Calories: \(item.calories)"
}
}

Default Column For this example, we have decided to designate our UISplitViewController
subclass as the Split View Controller delegate, but any other view

controller would work as well. In this case, we implement the protocol
method and return the value primary, so the Split View Controller shows the
When the app is launched, the Split View Controller decides how to display
Primary column first when the app is launched in a collapsed interface.
the columns. If there is only space available for one column, by default it
will show the Secondary. This means that, for example, when we launch
Do It Yourself: Update the MySplitViewController class with the code
the app in an iPhone in portrait mode, we will always see the Secondary
in Listing 12-7. Run the application on the iPhone simulator in
column until an action is performed. The UISplitViewControllerDelegate protocol
defines the splitViewController(UISplitViewController, portrait mode. You should see the Primary scene on the screen.
topColumnForCollapsingToProposedTopColumn:) method to change this predefined
behavior. The method is called by the Split View Controller to know what
column we want to show when the interface is collapsed. For instance, the
view controller in the following example conforms to the
UISplitViewControllerDelegate protocol and implements this method to ask the
Split View Controller to show the Primary column first.
Three-Column Design Do It Yourself: Remove the Compact scene added in the previous
example and add a Collection View Controller. Connect this scene to

the Split View Controller with a Supplementary View Controller
Split View Controllers can be configured with two or three columns. So far, segue (Figure 12-17, number 1). Remove all the configuration
we have been using a two-column design, but we can easily add an changes added to the UISplitViewController subclass before to allow the
additional column by changing the Style option in the Attributes Inspector Split View Controller to provide the standard buttons to manage the
panel to Triple Column (see Figure 12-7, number 1). Once we select this columns. Remove the Show Detail segue and add a new one from
option, a Supplementary scene is added to the Storyboard and connected the Supplementary scene to the Navigation Controller of the
to the Split View Controller with a Supplementary View Controller segue. Secondary scene (Figure 12-17, number 2). The rest of the interface
Xcode adds a single view to the Storyboard, but we can replace it with any is the same as before, but because we are going to use the Primary
type of scene we want. For our example, we are going to use the Primary scene to show the list of categories, you should change its title to
scene to show a list of categories, and the Supplementary scene to show Categories, as we did in the interface in Figure 12-17.
the items belonging to each category, so we need a Collection View
Controller. Figure 12-17, below, shows what the interface looks like after
For this example, we need to organize the items in categories. The
we replace the scene with a Collection View Controller (number 1).
following model defines a class to store the categories called Categories,
which includes an array of ItemsData objects to store the items for each
Figure 12-17: Scenes for a three-column Split View Controller
category.
enum Sections {
case main
}
class Categories: Identifiable {
var id: UUID = UUID()
var name: String
var items: [ItemsData]
AppData.selectedCategory = category
controller.prepareSnapshot() override func viewDidLoad() {
super.viewDidLoad()
if splitViewController?.isCollapsed == true { let config = UICollectionLayoutListConfiguration(appearance: .sidebarPlain)
splitViewController?.show(.supplementary) let layout = UICollectionViewCompositionalLayout.list(using: config)
} collectionView.collectionViewLayout = layout
}
} navigationItem.title = "Food"
}
} prepareDataSource()
 prepareSnapshot()
}
func prepareDataSource() {
The view controller in Listing 12-9 shows the list of categories, so the let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell,
diffable data source and the snapshot are defined with the Categories.ID type ItemsData.ID> { cell, indexPath, itemID in
if let item = AppData.selectedCategory?.items.first(where: { $0.id == itemID }) {
and the categories array. Again, we implement the collectionView(UICollectionView, var config = cell.defaultContentConfiguration()
didSelectItemAt:) method to respond to the selection of a cell. In this method, config.text = item.name
we get the item's identifier and send it to the Supplementary scene. cell.contentConfiguration = config
}
The Supplementary scene displays the items that belong to the category }
selected by the user, so we get the Categories object that represents the AppData.dataSource = UICollectionViewDiffableDataSource<Sections, ItemsData.ID>
(collectionView: collectionView) { collection, indexPath, itemID in
selected category from the categories array and store it in a property in the return collection.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item:
model (We store it in a property in the model to make it available for the itemID)
}
Supplementary and the Secondary scenes). After the selected category is
}
assigned to the selectedCategory property, we call the prepareSnapshot() method func prepareSnapshot() {
in the Supplementary scene to update the Collection View, and then var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
snapshot.appendSections([.main])
arrange the columns on the screen. By default, the Split View Controller let items = AppData.selectedCategory?.items.map({ $0.id })
can manage the columns in expanded interfaces without any help, but we snapshot.appendItems(items ?? [])
AppData.dataSource.apply(snapshot, animatingDifferences: false)
still have to call the show() method to show the Supplementary scene in a }
collapsed interface. override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath:
When opened, the Supplementary scene reads the selected category from IndexPath) {
if let itemID = AppData.dataSource.itemIdentifier(for: indexPath) {
the model and shows the items on the screen. selected = itemID
performSegue(withIdentifier: "showSecondary", sender: nil)
}
Listing 12-10: Showing the items in a category }
 override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showSecondary" {
import UIKit
if let navigator = segue.destination as? UINavigationController {
let controller = navigator.topViewController as! SecondaryViewController
class SupplementaryViewController: UICollectionViewController {
controller.selected = selected
var selected: ItemsData.ID!
}
} }
} }
} 

In iPads in landscape mode, the columns are shown by default, but in iPads
This view controller performs the same tasks as the view controller for the in portrait mode and large iPhones in landscape mode, the removable
Primary scene, but now the snapshot is created from the items property of columns are hidden and only the Secondary column is visible, and in
the selected category, so only the items that belong to that category are iPhones in portrait mode (collapsed interfaces) the columns are shown one
shown on the screen. at a time, but buttons are always provided to access the rest of the
When an item is selected in the Supplementary scene, it must be sent to columns.
the Secondary scene to show the values on the screen. This is the same
process used before for a two-column Split View Controller; we assign the Figure 12-18: Three-column Split View Controller on an iPhone
item to the selected property and perform the Show Detail segue.
No changes are required for the view controller that controls the
Secondary scene, except that this time we get the item by default from the
selected category (this is why we assigned the selected category to a
property in the model).

Listing 12-11: Showing the item's values
 Do It Yourself: Update the ApplicationData.swift file in your project
import UIKit with the model in Listing 12-8. Update the PrimaryViewController class
class SecondaryViewController: UIViewController { and the SecondaryViewController class with the codes in Listings 12-9
@IBOutlet weak var itemThumbnail: UIImageView!
and 12-11. Create a subclass of UICollectionViewController called
@IBOutlet weak var itemName: UILabel!
@IBOutlet weak var itemCalories: UILabel! SupplementaryViewController and assign it to the Supplementary scene.
var selected: ItemsData.ID!
Complete this class with the code in Listing 12-10. Run the
override func viewDidLoad() { application and test it in different devices and orientations to see
super.viewDidLoad()
if selected == nil {
how the columns work.
if let item = AppData.selectedCategory.items.first {
selected = item.id
}
}
if let item = AppData.selectedCategory?.items.first(where: { $0.id == selected! }) {
itemThumbnail?.image = UIImage(named: item.image)
itemName?.text = item.name
itemCalories?.text = "Calories: \(item.calories)"
}

12.3 Modal Scenes
modalPresentationStyle—This property sets or returns the scene's

presentation style. It is an enumeration of type UIModalPresentationStyle
Besides showing and hiding columns, we can expand the interface with with the values automatic, fullScreen, pageSheet, formSheet, currentContext,
modal scenes and popovers. We have already introduced modal scenes in custom, overFullScreen, overCurrentContext, popover, blurOverFullScreen, and
Chapter 9. They are normal scenes but connected with a Present Modally none.
segue. In iPhones, these scenes open as a sheet on top of the current modalTransitionStyle—This property sets or returns the transition
scene, but in iPads and large iPhones in landscape mode they are style. It is an enumeration of type UIModalTransitionStyle with the values
presented with designs that take advantage of the larger screens. These coverVertical, flipHorizontal, crossDissolve, and partialCurl. Notice that some of
presentation styles are set by the segue. The options are available from the these transitions are only available for the Full Screen presentation
Attributes Inspector panel when the segue is selected.
style.
Full Screen presents a scene of the size of the screen over the present(UIViewController, animated: Bool, completion: Block)
current interface. —This method presents a modal scene. The first argument is a
Current Context presents a scene in the place and of the size of reference to the view controller of the scene we want to present, the
the designated area. animated argument determines whether the process will be animated,
Page Sheet presents a scene as a sheet that emerges from the and the completion argument is an optional closure that is executed
bottom of the screen. after the scene is presented on the screen.
Form Sheet presents a scene as a sheet over the current dismiss(animated: Bool, completion: Block?)—This method
interface and in the center of the screen. dismisses the scene. The animated argument is a Boolean value that
Over Full Screen presents a scene over the interface and with determines whether the process will be animated, and the
the size of the screen (but without hiding the current scenes).
completion argument is a closure that is executed after the scene is
Over Current Context presents a scene in the place and with
closed.
the size of the designated area (but without hiding the current
scene).
Modal scenes are used to present additional information. For instance, we
can connect a scene to the Secondary scene with an Image View to let the
Because modal scenes are not part of the main interface and are not
user see a larger image of the selected item.
added to the navigation stack of a Navigation Controller, we must provide a
way for the user to control them. In Chapter 9, we studied how to do this
Figure 12-20: Interface with a Modal Segue
with an Unwind Segue, but the UIViewController class also offers some
properties and methods to present and dismiss these scenes
programmatically.
The view controller for the new scene must include a property called
selected to receive the identifier of the selected item and then update the
Image View using the item's values. We called this view controller
PictureViewController.
if selected != nil {

if let item = AppData.selectedCategory?.items.first(where: { $0.id == selected! }) {
bigThumbnail.image = UIImage(named: item.image)
In Figure 12-20, we have modified the Secondary scene of our example to }
}
incorporate a button below the item’s thumbnail with the title "Expand” }
(number 1) and have connected this button with a Present Modally segue @IBAction func closeScene(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
to a scene that includes an Image View. Now we can pass the selected item }
from the Secondary scene to the new scene to show a larger picture on the }
screen. For this purpose, the SecondaryViewController class has to implement 
The modal scene in this example was presented as a Page Sheet. There are
two modes available for Page Sheets: large and medium. A large Page
Sheet is displayed at full height, and a medium Page Sheet is shown at
approximately half the height of the screen. By default, the presentation is
large, but we can set a different mode by modifying the controller before
the scene is shown on the screen.
UIKit defines a class called UIPresentationController to manage the
presentation of modal scenes. When a modal scene is created and
presented, an object of the UIPresentationController class is automatically
created to manage the presentation. Page Sheets are created from a
subclass of the UIPresentationController class called UISheetPresentationController.
This class includes a property called detents that we can use to define the
presentation mode. The property takes an array of objects of a class called
Detent, which includes the following type methods.

medium()—This type method returns a Detent object to create a
Do It Yourself: Add a button below the thumbnail in the Secondary medium Page Sheet.
scene with the title "Expand" (Figure 12-20, number 1). Add a scene large()—This type method returns a Detent object to create a large
to the Storyboard and connect the button to this scene with a Page Sheet.
Present Modally segue. Assign the segue the identifier
"showPicture" and the Presentation style Page Sheet. Add a button To provide access to the UISheetPresentationController object created for our
with the title "Close" and an Image View to the new scene modal scene, the UIViewController class includes the sheetPresentationController
(Remember to assign a higher Vertical Content Hugging Priority to property. From this property, we can modify the detents property to set the
the button if necessary). Create a subclass of UIViewController called Page Sheet's mode. These changes can be performed in the prepare()
PictureViewController and assign it to the new scene. Connect the Image method, before the segue is triggered, as shown next.

This is not the same option as the Simulated Size offered by the Size Preferred Explicit Size, and assign a size of 300 x 400 points (Figure
Inspector panel (Figure 7-14). The Simulated Size option determines the 12-23). Run the application, select an item, and press the Expand
size of the scene in the Storyboard, while the Use Preferred Explicit Size button. You should see something like Figure 12-24.
option sets the size the scene is going to have when the application is
running. We can set both options to the same values if we want to work on Adding scenes to the interface with Modal segues is easy, but there are
a scene that matches what the user is going to see on the screen. Figure times when our application requires the addition of the scenes dynamically
12-24, below, shows the modal scene from the previous example from code. There are several alternatives, but the easiest way to do it is to
presented on an iPad as a Form Sheet and with an explicit size of 300 x add the scene to the Storyboard, as always, but disconnected from the rest
400. of the interface. These scenes are not connected to other scenes with
segues, but they can be managed from the object created to represent the
Figure 12-24: Form Sheet presentation with an explicit size Storyboard.
When the app is executed, the Storyboard is loaded and processed. The
objects representing the scenes and their content are created and
connected with each other, but the system also creates an object that
represents the Storyboard itself. This object is instantiated from the
UIStoryboard class and offers the following methods to access its content.
Independent scenes are added to the Storyboard the same way we do with
other scenes, the only difference is that they are not connected to the rest
 of the scenes. Once the scene is ready, the process to generate its content
and view controller are the same. For our example, we will add to our
Do It Yourself: Select the Modal segue and change the project the scene down below with a view controller called
SingleViewController.
Presentation style to Form Sheet. Select the scene, open the
Attributes Inspector panel, go to the bottom, activate the option Use
Figure 12-25: Single scene in the Storyboard
adaptivePresentationStyle(for: UIPresentationController)—
This method is called on the delegate to know the presentation style
to use when the horizontal Size Class becomes compact.
presentationController(UIPresentationController,
viewControllerForAdaptivePresentationStyle:
UIModalPresentationStyle)—This method is called on the delegate
to get the view controller to present for the style determined by the
second argument.
The UIPresentationController object has two presentation styles, one for the
normal state and another for the adaptive state. The adaptive state is the
state in which the horizontal Size Class is compact. This is the state we will
find in small iPhones or when the app is opened in an iPad in multitasking
mode. When a modal scene is presented in these conditions, the 
presentation style is automatically changed to pageSheet, but we can specify
the style we want implementing the protocol methods. The advantage of The scene on the left is the same introduced in Figure 12-25, and the one
these methods is that they are called not only to know the presentation on the right is the new scene we will open in the adaptive state (when the
style to use in the adaptive state but also to get the view controller we horizontal Size Class is compact). In this example, we just change the
want to present, and therefore we can designate a completely different position of the Close button, so on iPads the button will be shown at the
view controller for each state. The following example adds another scene bottom and on iPhones at the top, but other changes and elements may be
to our Storyboard to present when the interface is in an adaptive state. introduced to adapt the scene to each device and situation. To control this
new scene, we have created a class called iPhoneViewController with the
Figure 12-27: Two interfaces for the same modal scene Action for the button.
arrowDirection—This property returns the arrow's direction. It is a The steps to add a popover to the Storyboard are the same as for modal
structure of type UIPopoverArrowDirection with the properties up, down, scenes, with the exception that we must use a specific segue called Present
left, right, any, and unknown. As Popover. Figure 12-29 shows a small scene connected to a button in our
Secondary scene with this type of segue. The purpose of the button is to
sourceView—This property sets or returns the view to which the
open a popover that shows how many calories are provided by 10 units of
popover is anchored. It is an optional value of type UIView.
the item. The button has been connected to a scene with an explicit size of
sourceRect—This property sets or returns a CGRect value with the 300 x 100.
rectangle of the view to which the popover is anchored.
barButtonItem—This property sets or returns the Bar Button Item Figure 12-29: Popover in the Storyboard
to which the popover is anchored. It is an optional value of type
UIBarButtonItem.
be located. For example, if we position the arrow at the top (Up), the scene
is going to be positioned below the element. By default, the segue
activates all the values to let the system decide the most appropriate, but
we can suggest a specific position by activating only the ones we want.
The popover of our example shows a message with the calories provided
by 10 items, so we must pass the selected item from the Secondary scene
to the popover, calculate the calories, and show the result on the screen.
The first step is to modify the prepare() method of the SecondaryViewController
class to send the item's identifier to the popover view controller when the
popover’s segue is triggered. The segue was identified with the
"showPopover" string.

Listing 12-20: Sending the item to the popover
A Present As Popover segue assigns values by default to the 
UIPopoverPresentationController object, but we can modify some of them from override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showPicture" {
the Attributes Inspector panel. let controller = segue.destination as! PictureViewController
controller.selected = selected
Figure 12-30: Popover configuration } else if segue.identifier == "showPopover" {
let controller = segue.destination as! PopoverViewController
controller.selected = selected
}
}

The view controller for the popover must calculate the total calories
provided by 10 items and assign the result to the label. We called it
PopoverViewController.
The code in Listing 12-21 defines two Outlets to control the labels, one for
the message and another for the calories. When the view is loaded, we

prepare the strings with the item's name and calories and assign them to
the labels to show them on the screen.
Do It Yourself: Add a button with the title "Show" below the labels
in the Secondary scene. Add a new scene to the Storyboard and
Figure 12-31: Popover
connect the button to this scene with a Present As Popover segue.
Give the segue the identifier "showPopover". Add two labels to the
popover, as shown in Figure 12-29. Select this scene and modify its
explicit size from the Attributes Inspector panel (Figure 12-23). You
can also modify the size in the Storyboard from the Size Inspector
panel (Figure 7-14). Create a subclass of UIViewController called
PopoverViewController and assign it to the new scene. Complete the
subclass with the code in Listing 12-21. Connect the labels to the
messageLabel and caloriesLabel Outlets. Update the prepare() method in
 the SecondaryViewController class with the code in Listing 12-20. Run the
application in the iPad simulator, select an item, and press the Show
If we leave the segue with the configuration by default, the system decides button. You should see something like Figure 12-31. Select the
where to position the arrow and the scene for us. In this case, it decided popover segue, go to the Attributes Inspector panel, and deactivate
that it was better to present the scene at the top of the Show button, but the directions Up, Down, and Right. Run the application again. You
we can select a different position by deactivating the directions we don't
should see something like Figure 12-32.
want. For instance, the following is what we see if only the Left arrow is
activated.
This works fine on iPads. The scene is presented as a popover, and it can be override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showPicture" {
dismissed tapping anywhere else on the screen. But in a compact let controller = segue.destination as! PictureViewController
horizontal Size Class, the popover is shown as a Page Sheet and the only controller.selected = selected
} else if segue.identifier == "showPopover" {
way for the user to close it is to drag it down. To solve this problem, we can let controller = segue.destination as! PopoverViewController
implement the protocol methods we used before for modal scenes. One controller.selected = selected
alternative is to return the none value from the adaptivePresentationStyle() controller.popoverPresentationController?.delegate = self
}
method. This tells the UIPresentationController object that we want the scene }
to be presented with its original presentation style, so the scene always func adaptivePresentationStyle(for controller: UIPresentationController) ->
UIModalPresentationStyle {
looks like a popover, no matter the Size Class. return .none
UIKit defines a special protocol for popovers called }
UIPopoverPresentationControllerDelegate. This protocol inherits from the }

UIAdaptivePresentationControllerDelegate protocol, so all the methods defined in
that protocol are also available for popovers. The following are the The UIViewController class includes the popoverPresentationController property to
modifications we need to introduce to the SecondaryViewController class to access the UIPopoverPresentationController object in charge of presenting the
conform to this protocol and modify the popover's adaptive style.
popover. From this object, we assign the SecondaryViewController class as the
popover's delegate and then implement the adaptivePresentationStyle() method
Listing 12-22: Modifying the adaptive style

to return the value none. From now on, every time the popover is about to
import UIKit
be presented in a compact horizontal Size Class, the
UIPopoverPresentationController object receives the none value and therefore
class SecondaryViewController: UIViewController, UIPopoverPresentationControllerDelegate {
@IBOutlet weak var itemThumbnail: UIImageView! does not adapt the presentation style (the popover is not shown as a Page
@IBOutlet weak var itemName: UILabel! Sheet). Figure 12-33 shows what the popover looks like on an iPhone in
@IBOutlet weak var itemCalories: UILabel!
var selected: ItemsData.ID! portrait mode.
Alert Views of type Alert are usually presented to inform the user that a
specific task has been completed or that something went wrong. For
example, we may show an Alert View of type Alert to report an error when
the user tries to save the value of an empty Text Field. The following
interface includes a Text Field and a button to allow the user to enter a
name. If the user presses the button before inserting any text, the code
displays an Alert View reminding the user that a name is required.
The view controller for this scene must check whether the Text Field
contains text or not and show the alert if it is empty.
} else {
print("Value stored: \(text)")
nameText.text = ""
}
}
func showAlert() {
let alert = UIAlertController(title: "Error", message: "Insert your name in the field",
preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(action)
present(alert, animated: true, completion: nil)
}
}

The view controller in Listing 13-1 includes an Outlet for the Text Field
called nameText and an Action for the button called saveName(). When the
user presses the button, the Action checks the value of the text property. If
the value is empty, it calls the showAlert() method to present an Alert View,
otherwise it prints a message on the console and clears the field.
The showAlert() method creates the view controller with the UIAlertController()
initializer, which includes the title, the message, and the view's type (Alert 
in this case). Next, it creates and adds a UIAlertAction object for every button
to be included. The initializer for this class also allows us to define the Do It Yourself: Create a new project. Add a label, a Text Field, and a
information required, like the title for the button, its purpose, and a button to the scene. Connect the Text Field to the view controller
closure that is going to be executed when the button is pressed. For this with an Outlet called nameText and the button to an Action called
example, we need only one button of type Default with no action, so the saveName(). Complete the ViewController class with the code in Listing
handler is declared as nil. The action is added to the Alert View with the 13-1. Run the application and press the button. You should see the
addAction() method and then the scene is presented on the screen with the
alert shown in Figure 13-2.
present() method, as we did for modal scenes in Chapter 12. If we run the
application and press the Save button before typing any text, an Alert View
The OK button in our example doesn't do anything other than dismissing
reminds us that we must enter our name.
the scene. If we want to perform a task, we must provide a closure for the
handler argument of the UIAlertAction initializer. For instance, the following
Figure 13-2: Alert View of type Alert with a single button
code changes the background color of the Text Field after the OK button is
pressed.
Listing 13-2: Changing the background color of the Text Field when the buttons can perform additional tasks, we just need to provide the closure
button is pressed in the UIAlertAction initializer.

func showAlert() { Do It Yourself: Replace the showAlert() method in your project with
let alert = UIAlertController(title: "Error", message: "Insert the name in the field", preferredStyle:
.alert)
the methods in Listings 13-2 and 13-3 to test each example.
let action = UIAlertAction(title: "OK", style: .default, handler: { (action) in
self.nameText.backgroundColor = UIColor(red: 255.0/255.0, green: 230.0/255.0, blue:
Alert Views of type Alert may also include Text Fields. Text Fields are
230.0/255.0, alpha: 1.0)
}) usually added to an Alert View to get input from the user or to create sign-
alert.addAction(action) up or log-in forms. For instance, we can create an interface with an initial
present(alert, animated: true, completion: nil)
} scene that allows the user to log in, and a second scene that opens if the
 values inserted by the user match the data in the model. The interface
below includes a welcoming window with a button called Log In to open an
We can include more buttons in the Alert View by adding more actions. Alert View to insert an email and a password.
The following example asks the user if the application should store the
value anyway and offers a Cancel button to cancel the operation. Figure 13-3: Interface to log in
class ViewController: UIViewController { order of the Text Fields in the code (the first one is at index 0, the next one
@IBAction func loginUser(_ sender: UIButton) {
let alert = UIAlertController(title: "Insert Email and Password", message: nil, preferredStyle: at index 1, and so on). For this example, we keep it simple and compare the
.alert) values of each Text Field with hard-coded strings. If the values match, we
perform the segue to open the second scene where the user can begin
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(cancel) working with our app (the segue is a Show segue identified with the string
"showMainScreen").
let action = UIAlertAction(title: "Login", style: .default, handler: { (action) in
if let fields = alert.textFields {
let email = fields[0].text Figure 13-4: Alert View with Text Fields
let password = fields[1].text
alert.addTextField(configurationHandler: { (textField) in
textField.placeholder = "Email"

})
alert.addTextField(configurationHandler: { (textField) in
textField.placeholder = "Password" Do It Yourself: Create a new project. Embed the scene in a
textField.isSecureTextEntry = true
}) Navigation Controller. Add a button with the title Log In to the scene.
present(alert, animated: true, completion: nil)
}
Add a second scene to the Storyboard. Add a label with the text "You
} are logged in" to the second scene. Connect the first scene to the

second scene with a Show segue. Give the segue the identifier
“showMainScreen”. Connect the Log In button with an Action called
The addTextField() method includes an argument that takes a closure to
loginUser(). Complete the Action with the code in Listing 13-4. Run the
configure the Text Field. For the email, we only need a placeholder, but the
Text Field for the password should be declared as secure with the application and press the button. Insert the email "[email protected]"
isSecureTextEntry property, so the password is not displayed on the screen. and the password "12345". Press Login. You should see something
The Alert View includes two buttons: Cancel and Login. When the user like Figure 13-4.
presses the Login button, we compare the values inserted in the fields with
the data we have in our model (usually a database in the device or in a
server). To access the Text Fields from inside the closure, we read the
textFields property of the Alert View. This property contains an array with
references to the Text Fields available. The indexes of the array follow the
Action Sheets the cancel button is always displayed at the bottom, in a separate box, and
the destructive button is highlighted in red.

Figure 13-5: Action Sheet
Action Sheets are generally presented when the user must decide between
multiple available options. As shown in the following example, the creation
of Action Sheets is like Alert Views, but the initializer must be defined with
the style actionSheet. The example assumes that the interface contains a
button connected to an Action called openSheet().
present(alert, animated: true, completion: nil) Do It Yourself: Create a new project. Add a button called Open
} Sheet to the scene. Connect the button to the ViewController class with
}
 an Action called openSheet(). Complete the Action with the code in
Listing 13-5. Run the application on an iPhone simulator and press
The code in Listing 13-5 creates an Alert View of type Action Sheet when the button. You should see something like Figure 13-5.
the user presses the button on the interface. This Action Sheet contains a
total of three buttons: a default button called Move to Inbox, a destructive As we already mentioned, Action Sheets are presented as modal scenes at
button called Delete, and a cancel button called Cancel. We didn't include the bottom of the screen in small devices, but as popovers in iPads. The
any handler for the buttons, so their only purpose is to dismiss the scene, problem is that the system cannot display a popover without the necessary
but the example illustrates how an Action Sheet is created and how the information. If we execute the previous application on an iPad, we get an
buttons are organized. No matter the position of the buttons in the code, error and the application crashes. To make sure that the code works on
every device, we must configure the presentation controller. For this the internal frame of the button’s view to the sourceRect property we make
purpose, the UIAlertController object includes the popoverPresentationController sure that the arrow always points to one side of the button and the button
property with a reference to the UIPopoverPresentationController object in is not covered by the view. Finally, we set the permittedArrowDirections
charge of the presentation when the presentation style is popover. The property with the value up, so the scene is positioned below the button.
values we need to provide are the view to which the Action Sheet is The Action Sheet adapts the presentation style to the space available and
anchored, the rectangle inside the view the popover’s arrow points to, and also the design. If we run the example in Listing 13-6 on an iPad, the Cancel
the direction of the arrow. button is automatically removed because the user can dismiss the popover
by tapping anywhere else on the screen.
Listing 13-6: Presenting the Action Sheet as a popover in iPads
 Figure 13-6: Action Sheet on an iPad
import UIKit
The view controller in Listing 13-6 adds the configuration for the popover
to the previous example. We decided to anchor the popover to the button
that opens the Alert View, so we assign the reference received by the
Action to the sourceView property of the UIPopoverPresentationController object.
This anchors the popover to the button, but we still must determine to
which part of the button’s view the arrow is going to point to. By assigning
CHAPTER 14 - CONCURRENCY 14.1 Asynchronous and Concurrent Tasks

Because multiple applications can run at the same time, the system doesn't
allocate certain number of cores per application. What it does is to create
execution threads, assign the tasks to the threads, and then decide which
threads are going to be executed by which core depending on the available Tasks
resources. In the example of Figure 14-1, left, there is an asynchronous task

that loads and image from the Web and then displays it on the screen.
While waiting for the image to download, the thread is free to perform
Asynchronous and concurrent code is defined by tasks. The Swift Standard
other tasks, so the system may use it to execute a different task that
Library includes the Task structure to create and manage these tasks. The
updates the progress bar. On the right, the tasks were created as
following is the structure's initializer.
concurrent tasks and therefore they are executed simultaneously in
different threads.
Task(priority: TaskPriority?, operation: Closure)—This initializer
creates and runs a new task. The priority argument is a structure that
helps the system decide when to execute the task. The structure
includes type properties to defined standard priorities. The currently
available are background, high, low, medium, userInitiated, and utility. The
operation argument is a closure with the statements to be executed
by the task.
There are also a few type properties and methods available to get
information from the current task or create tasks that perform specific
processes. The following are the most frequently used.
Asynchronous and concurrent tasks are defined in Swift with the async and
await keywords. For instance, to create an asynchronous task, we mark a
method with async and then wait for that method to complete with await.
This means that an asynchronous method can only be called with the await
keyword from inside another asynchronous method, which creates an
indefinite cycle. To start the cycle, we can create an asynchronous task with
the Task structure, as shown next.
Listing 14-1: Creating an asynchronous task

import UIKit
method returns a string with the name received from the task. To define }

the method as asynchronous, we add the async keyword after the
parameters, and then call it with the await keyword to indicate that the task
The processes are executed one by one, in sequential order. The task waits
must wait for this process to be over.
for a process to be over before executing the next. In this case, the whole
The application creates the task and adds it to the thread. When the
task is going to take 9 seconds to finish (3 seconds per process). The more
system is ready to run it, it executes the closure. In the closure, we call the
processes we add to an asynchronous task, the more is going to take to
loadImage() method and wait for its completion. The method pauses for 3
finish. For this reason, sometimes we might need to cancel a task before it
seconds and then returns a string. After this, the task continues executing
is completed.
the statements, and a message is printed on the console.
Cancelling a task is easy. We must assign the task to a variable and then call
the cancel() method on it. But the processes are not automatically cancelled;
Do It Yourself: Create a new project. Update the ViewController class we must detect whether the task has been cancelled with the isCancelled
with the code in Listing 14-1. Run the application. You should see a property and stop the process ourselves.
message appear on the console after 3 seconds. Use this project to
try the rest of the examples. Listing 14-3: Cancelling a task

A task can perform multiple asynchronous processes. For instance, in the import UIKit
following example we call the loadImage() method three times to download
class ViewController: UIViewController {
three images. override func viewDidLoad() {
super.viewDidLoad()
let myTask = Task(priority: .background) {
Listing 14-2: Running multiple asynchronous tasks let imageName = await loadImage(name: "image1")
 print(imageName)
import UIKit }
Timer.scheduledTimer(withTimeInterval: 2.0, repeats: false) { (timer) in
class ViewController: UIViewController { print("The time is up")
override func viewDidLoad() { myTask.cancel()
}
super.viewDidLoad()
Task(priority: .background) { }
let imageName1 = await loadImage(name: "image1") func loadImage(name: String) async -> String {
await Task.sleep(3 * 1000000000)
let imageName2 = await loadImage(name: "image2")
let imageName3 = await loadImage(name: "image3") if !Task.isCancelled {
print("\(imageName1), \(imageName2), and \(imageName3)") return "Name: \(name)"
} else {
}
} return "Task Cancelled"
func loadImage(name: String) async -> String { }
}
await Task.sleep(3 * 1000000000)
return "Name: \(name)" }
} 
}
}
This example assigns the previous task to a constant and then creates a 
timer to call the cancel() method on the task 2 seconds later. In the loadImage()
method, we read the isCancelled property and respond accordingly. If the Because we need to wait for the task to finish before using the value, we
task was cancelled, we return the "Task Cancelled" message, otherwise, have defined a second task. The process starts as always, with a task that
the name is returned as before. Notice that in this case we are working calls the loadImage() method, but now we create a second task in this
inside a process executed by the task, so we use the type property instead method that returns a string. This task executes another asynchronous
of the instance property (we read the isCancelled property from the data method that waits for 3 seconds and returns the number 50000. After this
type, not the instance). This property returns true or false depending on the process is over, the task creates a string with the name and the number
state of the current task. As a result, the task is cancelled before it is and returns it. We then get the string from the value property, and return it
completed. to the original task, which prints it on the console.
Tasks can receive and return values. The Task structure includes the value So far, we have worked with asynchronous methods, but we can also
property to provide access to the value returned by the task. Of course, we define asynchronous properties. All we need to do is to define the getter
also need to wait for the task to complete before reading this value, as with the async keyword, as shown next.
shown next.
Listing 14-5: Defining and asynchronous properties
Listing 14-4: Reading a value returned by a task 
 import UIKit
import UIKit
class ViewController: UIViewController {
class ViewController: UIViewController { var thumbnail: String {
override func viewDidLoad() { get async {
super.viewDidLoad() await Task.sleep(3 * 1000000000)
Task(priority: .background) { return "mythumbnail"
let imageName = await loadImage(name: "image1") }
print(imageName) }
} override func viewDidLoad() {
} super.viewDidLoad()
func loadImage(name: String) async -> String { Task(priority: .background) {
let result = Task(priority: .background) { () -> String in let imageName = await thumbnail
let imageData = await getMetadata() print(imageName)
return "Name: \(name) Size: \(imageData)" }
} }
let message = await result.value }
return message 
}
func getMetadata() async -> Int { This time, instead of calling a method, the task reads a property. The
await Task.sleep(3 * 1000000000)
return 50000 property suspends the tasks for 3 seconds and returns a string. Again, we
are suspending the task for didactic purposes, but we can perform any Errors
demanding task we want in this property, such as downloading an image

from the Web or processing data.
Asynchronous tasks are not always successful, so we must be prepared to
process the errors returned. If we are creating our own tasks, we can
define the errors with an enumeration that conforms to the Error protocol,
as explain in Chapter 3 (see Listing 3-190). The following example defines a
structure with two errors, one to return when no metadata is found on the
server (noData), and another for when the image is not available (noImage).
Task(priority: .background) {
do {
let imageName = try await loadImage(name: "image1")
print(imageName)
} catch MyErrors.noData {
print("Error: No Data Available")
} catch MyErrors.noImage {
print("Error: No Image Available")
}
}
}
func loadImage(name: String) async throws -> String {
await Task.sleep(3 * 1000000000)
Task(priority: .background) {
async let imageName1 = loadImage(name: "image1")
async let imageName2 = loadImage(name: "image2")
async let imageName3 = loadImage(name: "image3")
Every time a process is declared with the async let statement the system
creates a new concurrent task that runs in parallel with the rest of the
tasks. In the example of Listing 14-7, we create three concurrent tasks
actor ItemData {
var name: String
var counter: Int
init(name: String) {
self.name = name
self.counter = 0
}
func changeName(newName: String) {
name = newName
counter += 1
}
} Main Actor
class ViewController: UIViewController {
var item: ItemData! 
override func viewDidLoad() {
super.viewDidLoad() As we already mentioned, tasks are assigned to execution threads and then
item = ItemData(name: "Undefined")
the system distributes these threads among the multiple cores of a
Task(priority: .background) { processor to perform the tasks as fast and smoothly as possible. A thread
async let imageName1 = loadImage(name: "potatos")
async let imageName2 = loadImage(name: "milk")
can manage multiple tasks, and multiple threads may be created for our
async let imageName3 = loadImage(name: "orange") application. Besides the threads initialized to process asynchronous and
concurrent tasks, the system always creates a thread, called main thread,
let listNames = await "\(imageName1), \(imageName2), and \(imageName3)"
print(listNames) to start the application and run non-asynchronous codes, including the
} code that creates and updates the interface. This means that if we try to
}
func loadImage(name: String) async -> String {
modify the interface from an asynchronous or concurrent task, we may
await item.changeName(newName: name) cause a data race or a serious bug. To avoid these conflicts, the Swift
Standard Library defines the Main Actor. The Main Actor is an actor created
let count = await item.counter
return "\(name) \(count)" by the system that makes sure that every task that wants to interact with
} the main code or modify the elements of the interface waits for other tasks
}
 to finish.
Swift provides two easy ways to make sure that our code runs on the Main
The method in the ItemData actor updates the value of the name property Actor (the main thread): the @MainActor modifier and the run() method.
and then increments the value of the counter property by 1 to keep a count With the @MainActor modifier we can mark an entire method to run on the
on the number of times the name was modified. When the view is loaded, main thread, while the run() method executes a closure in the main thread.
we create an instance of this actor with the name "Undefined" and then For instance, in the following example we mark the loadImage() method with
define a task with three concurrent tasks inside. The concurrent tasks call @MainActor to make sure that the code inside the method is executed in the
the loadImage() method. In this method we call the changeName() method to main thread and we are able to modify the value of a label.
modify the name in the actor and then return this value along with the
current value of the counter property. Notice that we wait for both, the Listing 14-9: Executing a method in the Main Actor
method and the property, to make sure the tasks don't overlap. 
import UIKit
Task(priority: .background) { }
await loadImage(name: "image1") }
} func loadImage(name: String) async {
} await MainActor.run {
@MainActor func loadImage(name: String) async { myLabel.text = name
myLabel.text = name }
print(name) print(name)
} }
} }
 
This code creates an asynchronous task as before, but now the method is This code is the same as before, but now only the statement that modifies
marked with @MainActor, so the code is executed in the main thread, and the interface runs in the main thread, while the rest of the statements in
we can safely assign a text to the label. the method keep running in the thread assigned to the task. Notice that
the run() method is marked with await. The await keyword is necessary
Do It Yourself: Update the ViewController class with the code in because the method may have to wait for the main thread to be free to
Listing 14-9. Add a label to the scene and connect it with the myLabel execute the statements.
Outlet. Run the application. You should see the text "image1" on the The run() method can also return a value. This is useful when we need to
screen. get information from the interface. In the following example, we get the
label's current text and print it on the console.
Most of the time, only part of our code deals with the interface, but the
rest can be executed in the current thread. For cases like this, we can Listing 14-11: Returning a value from the Main Actor

implement the run() method. This is a type method defined by the MainActor
import UIKit
structure (the structure used to create the Main Actor). The method takes
a closure with the statements we need to execute in the main thread. class ViewController: UIViewController {
@IBOutlet weak var myLabel: UILabel!
Listing 14-10: Executing code in the Main Actor override func viewDidLoad() {
 super.viewDidLoad()
import UIKit
Task(priority: .background) {
await loadImage(name: "image1")
class ViewController: UIViewController {
}
@IBOutlet weak var myLabel: UILabel!
}
func loadImage(name: String) async {
override func viewDidLoad() {
let labelText: String = await MainActor.run {
super.viewDidLoad()
let text = myLabel.text
return text ?? ""
Task(priority: .background) {
}
await loadImage(name: "image1")
print(labelText)
} Asynchronous Sequences
}
 
On the other hand, the AsyncIteratorProtocol protocol only requires the data
type to implement the following method.
Task(priority: .background) {
let loader = ImageLoader(imageList: imageList)
for await image in loader {
print(image)
}
}
}
}

Task Group addTask(priority: TaskPriority?, operation: Closure)—This
method adds a task to the group. The priority argument is a structure

that helps the system decide when to execute the task. The structure
A task group is a container for dynamically generated tasks. Once the group includes type properties to defined standard priorities. The currently
is created, we can add and manage tasks from code as required by the available are background, high, low, medium, userInitiated, and utility. And the
application. The Swift Standard Library defines the following global operation argument is a closure with the statements to be executed
methods to create a group. by the task.
cancelAll()—This method cancels all the tasks in the group.
withTaskGroup(of: Type, returning: Type, body: Closure)—This
method creates a task group. The of argument defines the data type A task group is an asynchronous sequence of tasks. This sequence is
returned by the tasks, the returning argument defines the data type generic, which means that the tasks, and the group, can return any types
returned by the group, and the body argument is the closure where of values. This is the reason why the methods to create a task group have
the tasks are defined. If no values are returned, the arguments may two arguments to specify the data types returned by the tasks and the
be ignored. group. These two methods are the same. The one we implement depends
on whether we want to throw errors or not. These methods create a
withThrowingTaskGroup(of: Type, returning: Type, body:
TaskGroup structure that works with the data types specified by the
Closure)—This method creates a task group that can throw errors. arguments and send the instance to the closure. Using this value inside the
The of argument defines the data type returned by the tasks, the closure, we can add to the group all the tasks we want, as shown next.
returning argument defines the data type returned by the group, and
the body argument is the closure where the tasks are defined. If no Listing 14-13: Defining a task group
values are returned, the arguments may be ignored. 
import UIKit
The group is defined by an instance of the TaskGroup structure, which
class ViewController: UIViewController {
includes properties and methods to manage the tasks in the group. The override func viewDidLoad() {
following are the most frequently used. super.viewDidLoad()
Task(priority: .background) {
await withTaskGroup(of: String.self) { group in
isCancelled—This property returns a Boolean value that indicates group.addTask(priority: .background) {
let imageName = await self.loadImage(name: "image1")
whether the group was cancelled. return imageName
}
isEmpty—This property returns a Boolean value that indicates group.addTask(priority: .background) {
whether the group has any remaining tasks. let imageName = await self.loadImage(name: "image2")
return imageName
}
group.addTask(priority: .background) {
let imageName = await self.loadImage(name: "image3") CHAPTER 15 - STORAGE
return imageName
}
for await result in group {
print(result)
}
}
}
}
func loadImage(name: String) async -> String {
await Task.sleep(3 * 1000000000)
return "Name: \(name)"
}
}

In this example, we create a task group that doesn't throw errors. The
group doesn't return a value either, but the tasks return a string, so we
declare the of argument of the withTaskGroup() method with a reference to
the String data type (String.self). The tasks are added to the group one after
another. Each task performs the same process as before. They call the
loadImage() method asynchronously and get a string in return.
Because a task group is an asynchronous sequence of tasks, we can iterate
though the values with a for in loop, as we did for the asynchronous
sequence created in the previous section of this chapter. Every time a task
is completed, the group returns the value produced by the task until no
tasks remain, in which case the value nil is returned to finish the loop.
Up to this point, all the data was stored in arrays created in the model, and The simplest system available on Apple devices is called Users Defaults.
the values were hard-coded, meaning they are always the same every time This system was designed to store user preferences, which may include
the app is launched. If we added a new value to the model, it was only values set by the user to determine how the app should work and values
preserved for as long as the app was running, but as soon as the app was set by the app to restore previous states. These values are stored in a
closed, the values were lost, and everything was back to the initial state. To system-managed database and therefore continue to exist after the
preserve the values and all the changes introduced by the user, we need to application is closed and for as long as the user or the application decides
store the data permanently on the device. Apple includes several systems to keep them.
for storing data. They all work with files but can take various forms, from The Foundation framework defines a class called UserDefaults to manage this
simple text files to databases (indexed data). system. Only one UserDefaults object is assigned per application, so we can
reference this object anywhere in the code and always process the same
values. The class offers the following type property to get a reference to
the UserDefaults object created for the app.
The values are assigned to the UserDefaults object with an associated key
that we can use to retrieve them later. The types of values we can work
with are restricted to Property List values (NSNumber, NSString, NSDate,
NSArray, NSDictionary, NSData, and the equivalents in Swift), but we can also
work with other types of values, including custom objects, by converting
them into NSData objects, as we will see in further chapters. The UserDefaults
class includes the following methods to store, retrieve, and delete values of
any of these types.
object(forKey: String)—This method retrieves the value associated we can use the User Defaults system to allow the user to store a limit on
with the key specified by the forKey argument. If the value does not the number of items managed by the application, and then set that limit
exist, it returns nil. back every time the app is executed. The interface below includes a
Stepper and a label to illustrate how the process works.
removeObject(forKey: String)—This method removes the value
associated with the key specified by the forKey argument.
Figure 15-1: Interface to store settings
Because the methods above process values of any type, they return them
as objects of type Any. This means that we always need to cast the values
to the right data type before using them. To simplify our work, the class
includes methods to retrieve the values for specific types.

bool(forKey: String)—This method retrieves a value of type Bool. If
there is no value set for the key, the method returns the value false. The view controller for this scene must update the value of the label when
the interface is shown on the screen and store it in the User Defaults
float(forKey: String)—This method retrieves a value of type Float. If
system every time the user sets a new one.
there is no value set for the key, the method returns the value 0.0.
integer(forKey: String)—This method retrieves a value of type Int. If Listing 15-1: Storing values in the User Defaults system
there is no value set for the key, the method returns the value 0. 
Double.If there is no value set for the key, the method returns the class ViewController: UIViewController {
value 0.0. @IBOutlet weak var counter: UIStepper!
@IBOutlet weak var counterLabel: UILabel!
string(forKey: String)—This method retrieves a value of type String. var defaultValues: UserDefaults!
As we already mentioned, we can also store values generated by the app. Do It Yourself: Update the ViewController class with the code in
For example, we may add a value to User Settings to keep track of the time Listing 15-2 and run the application. Wait a few seconds and stop the
the user has spent without using the app. The following example updates
the last view controller to store this value. The code reads the value when
application from Xcode. Wait a few more seconds and run it again. 
You should see the time between runs printed on the console. import UIKit
For this example, we use three values called "color", "editable", and
"correction" to store the current settings. If the values were already set,
which means that the user changed them from the settings screen at some
point in the past, we configure the Text View with those values, otherwise
the application uses the values assigned in the Storyboard. For example,
the color for the text in the Text View is set as black by default in the

Storyboard, but if there is a value stored with the key "color" we use it to
get the current color from an array (black, gray, or light gray).
The view controller for the initial scene reads the values and updates the
In the view controller for the settings scene, we must read the values as
Text View.
well to update the elements, but also get the user’s input and store the
new values for future reference. We call this view controller
Listing 15-3: Configuring the Text View according to the values set by the
SettingsViewController.
user
we use in the viewDidLoad() method to update their states according to the
Listing 15-4: Saving the settings values selected by the user, and also to Actions that update the settings
 every time these values are changed (the Value Changed event is fired). In
import UIKit consequence, every time the user opens the settings scene, the states of
the elements are modified according to the values stored in User Settings,
class SettingsViewController: UITableViewController {
@IBOutlet weak var controlColors: UISegmentedControl! and as soon as the user moves a Switch or taps a button in the Segmented
@IBOutlet weak var controlEditable: UISwitch! Control, the new value is stored in the system.
@IBOutlet weak var controlCorrection: UISwitch!
var defaultValues: UserDefaults!
Do It Yourself: Create a new project. Embed the scene in a
override func viewDidLoad() {
super.viewDidLoad() Navigation Controller. Add a bar button called Settings and a Text
View. Add a Table View Controller to the Storyboard. Connect the
defaultValues = UserDefaults.standard
if let color = defaultValues.object(forKey: "color") as? Int { Settings button to the new scene with a Show Segue. Select the
controlColors.selectedSegmentIndex = color table, go to the Attributes Inspector panel, and change the Content
}
if let editable = defaultValues.object(forKey: "editable") as? Bool { option to Static Cells, and the value of Sections to 2. Select the
controlEditable.isOn = editable header of each section and erase their titles. Leave only one cell for
}
if let correction = defaultValues.object(forKey: "correction") as? Bool { the first section and two for the second section. Add labels, a
controlCorrection.isOn = correction Segmented Control with the buttons Dark, Medium, and Light, and
}
} switches to the interface (Figure 15-2, right). Create a subclass of the
@IBAction func saveColor(_ sender: UISegmentedControl) {
UITableViewController class called SettingsViewController and assign it to the
let current = controlColors.selectedSegmentIndex
defaultValues.set(current, forKey: "color") second scene. Complete the ViewController class and the
}
SettingsViewController class with the codes in Listings 15-3 and 15-4.
@IBAction func saveEditable(_ sender: UISwitch) {
let current = controlEditable.isOn Connect the elements to their Outlets and Actions. Run the
defaultValues.set(current, forKey: "editable")
} application and set new values. Wait a few seconds for the system to
@IBAction func saveCorrection(_ sender: UISwitch) { store the values in User Defaults. Stop the application and run it
let current = controlCorrection.isOn
defaultValues.set(current, forKey: "correction") again. The Text View should be always configured according to the
} values stored in the system.
}

So far, we have used a generic method to retrieve the values, but the
The settings scene contains a Segmented Control with the buttons Dark, methods for specific types provided by the UserDefault class can simplify our
Medium, and Light, and two Switches to set the edition and autocorrection code. The following example updates the ViewController class of Listing 15-3
features of the Text View. These elements were connected to Outlets that to read the values with the methods according to their type.
our app, it is good practice to set them when the app is launched, as in the
Listing 15-5: Reading values with specific methods following example.

import UIKit Listing 15-6: Setting values by default

class ViewController: UIViewController {
@IBOutlet weak var textEditor: UITextView! import UIKit
indicated by the atPath argument. The FileManager class provides URLs and Paths
constants to define the attributes, including creationDate, modificationDate,

size, and type, among others.
The urls() method returns an array of optional URL structures with all the The path must represent the route to the file, including its name and
possible locations of the directory. In the case of the Documents directory, extension. The URL class includes the appendingPathComponent() method to
the right value is the first item of the array, so in Listing 15-7 we get this extend a URL. The method adds a string to the end of the URL and takes
value from the first property, unwrap it, assign it to the docURL constant, care of including the necessary forward slashes to ensure its validity. In
and print it on the console. Listing 15-8, we use it to get the URL of the new file. After this, we obtain
its path from the path property, and finally call the createFile() method to
Do It Yourself: Create a new project. Update the ViewController class create the file (if the file already exists, the method does nothing). At the
with the code in Listing 15-7. Run the application. You should see the end, the Documents directory of this application contains an empty file
URL of the Documents directory printed on the console. called mytext.txt.
In the same way that we create a file, we can create a directory. The
Now that we have the URL, we can start adding other directories and files method to create a directory is called createDirectory(). Again, the method
inside the Documents directory. The FileManager class includes the createFile() takes three values: the path of the new directory (including its name), a
method to create new files. The method requires three values: the path Boolean value that indicates if we want to create all the directories
where the file is going to be created (including the file’s name and included in the path (in case the path includes directories we have not
extension), a value with the file's content, and the file’s attributes. The created yet), and the attributes. This method throws an error if it cannot
content and the attributes are optional. If we declare these values as nil, complete the task, so we must handle it with try.
the file is created empty and with attributes by default, as in the following
example. Listing 15-9: Creating a directory inside Documents

Listing 15-10: Listing the content of a directory Listing 15-11: Creating files in a custom directory


import UIKit
import UIKit
class ViewController: UIViewController {
class ViewController: UIViewController {
override func viewDidLoad() {
override func viewDidLoad() {
super.viewDidLoad()
super.viewDidLoad()
let manager = FileManager.default
let manager = FileManager.default
let documents = manager.urls(for: .documentDirectory, in: .userDomainMask)
let documents = manager.urls(for: .documentDirectory, in: .userDomainMask)
let docURL = documents.first!
let docURL = documents.first!
listItems(directory: docURL)
}
let newFileURL = docURL.appendingPathComponent("myfiles/anotherfile.txt")
func listItems(directory: URL) {
let path = newFileURL.path
let manager = FileManager.default
let created = manager.createFile(atPath: path, contents: nil, attributes: nil)
if let list = try? manager.contentsOfDirectory(atPath: directory.path) {
if !created {
if list.isEmpty {
print("We couldn't create the file")
print("The directory is empty")
}
} else {
}
for item in list {
}
print(item)

}
}
} The code in Listing 15-11 adds the necessary components to the
}
}
Documents directory’s URL and then creates the file as done before. In this
 example, we add the components all at once, separating each component
with a forward slash, but we could have called the appendingPathComponent() class ViewController: UIViewController {
override func viewDidLoad() {
method for every component and add them one by one. super.viewDidLoad()
We can also move a file or directory from one location to another. All we let manager = FileManager.default
let documents = manager.urls(for: .documentDirectory, in: .userDomainMask)
have to do is to provide the paths for the origin and destination and then let docURL = documents.first!
call the moveItem() method. For example, we can move the mytext.txt file to
the myfiles directory created before. let originURL = docURL.appendingPathComponent("myfiles/anotherfile.txt")
let destinationURL = docURL.appendingPathComponent("anotherfile.txt")
let originPath = originURL.path
Listing 15-12: Moving files let destinationPath = destinationURL.path
do {

try manager.copyItem(atPath: originPath, toPath: destinationPath)
import UIKit } catch {
print("File was not copied")
class ViewController: UIViewController { }
override func viewDidLoad() { }
super.viewDidLoad() }
let manager = FileManager.default 
let documents = manager.urls(for: .documentDirectory, in: .userDomainMask)
let docURL = documents.first!
Removing files or directories is also an easy task. All we need to do is to get
let originURL = docURL.appendingPathComponent("mytext.txt") the path to the item and then call the removeItem() method.
let destinationURL = docURL.appendingPathComponent("myfiles/mytext.txt")
let originPath = originURL.path
let destinationPath = destinationURL.path Listing 15-14: Removing files
do { 
try manager.moveItem(atPath: originPath, toPath: destinationPath)
} catch { import UIKit
print("File was not moved")
} class ViewController: UIViewController {
} override func viewDidLoad() {
} super.viewDidLoad()
 let manager = FileManager.default
let documents = manager.urls(for: .documentDirectory, in: .userDomainMask)
let docURL = documents.first!
We can also copy files from one directory to another. The following
example copies the anotherfile.txt file created inside the myfiles directory let fileURL = docURL.appendingPathComponent("anotherfile.txt")
let path = fileURL.path
into the Documents directory. do {
try manager.removeItem(atPath: path)
} catch {
Listing 15-13: Copying files print("File was not removed")
 }
}
import UIKit
}

Files Attributes
Do It Yourself: Update the ViewController class with the codes in

Listings 15-11, 15-12, 15-13, and 15-14, and run the application
every time. Incorporate the listItems() method of Listing 15-10 to the Some applications need to know more than the name of the file. The
class so you can see the files being created and moved. FileManager class offers the attributesOfItem() method to get the file attributes,
such as the date of creation or the size. The method returns a dictionary
with predefined keys to identify each value. There are several constants
available we can use as keys. The most useful are creationDate (the date the
file was created), modificationDate (last time it was modified), size, and type.
The following code gets the attributes of the mytext.txt file created in
previous examples.
The attributes are returned as Any values, so we cast them to their Files Content
corresponding data types. Most of the values are of data types we already

know, such as Int or Date, but the type key returns a property of a structure
called FileAttributeType that represent the resource's type. There are
Storage systems, like hard drives and solid-state drives, store information
properties available to represent different types of resources. The most
the only way a computer knows, as a series of ones and zeros. Therefore,
frequently used are typeRegular to represent files and typeDirectory to
the information that we want to store in files must be converted into a
represent directories. If we want to know if the resource is a file, we can
stream of bytes which can then be converted back to the original data.
compare the value returned by the type key with the typeDirectory property,
Foundation offers the Data structure for this purpose.
as we did in the example of Listing 15-15. If they are different, it means
Although we can work directly with a Data structure, most frameworks
that the resource is a file, and we can process its attributes.
include tools to convert our data into Data structures that we can process or
store on files. For example, the UIImage class includes the pngData() and
jpegData() methods to convert images into Data structures (see Chapter 5).
Another common data type that provides tools to convert data is the String
structure. This structure includes a method that turns a string into data an
also an initializer that can get back the string from a Data structure.
With these tools, we can store information generated by the user in a file

and keep it updated. For example, we can get input from the user through
a Text View and store the text in a file.
The view controller for the scene in Figure 15-3 must perform two tasks:
store the text from the Text View in a file every time the user introduces a
Figure 15-3: Interface to generate file content
change, and read the content back from the file every time the user loads
the application and show it on the screen.
diaryText.delegate = self
let manager = FileManager.default
let documents = manager.urls(for: .documentDirectory, in: .userDomainMask) View to the ViewController class with an Outlet called diaryText.
let docURL = documents.first!
fileURL = docURL.appendingPathComponent("userdata.txt") Complete the ViewController class with the code in Listing 15-16. Run
let filePath = fileURL.path the application and insert a text in the Text View. Stop the
if manager.fileExists(atPath: filePath) {
if let content = manager.contents(atPath: filePath) { application from Xcode and run it again. You should see the text on
diaryText.text = String(data: content, encoding: .utf8)
the screen.
}
} else {
manager.createFile(atPath: filePath, contents: nil, attributes: nil)
}
}
func textViewDidChange(_ textView: UITextView) {
let text = diaryText.text!
do {
try text.write(to: fileURL, atomically: true, encoding: .utf8)
} catch {
print("Error")
}
}
}

The first thing we do in this ViewController class is to call the fileExists() method
to check if the file where we are going to store the data already exists. If
the file exists, we read its content with the contents() method, convert the
data into a string with the String initializer, and assign the string to the Text
View. This way, the last text stored in the file is always displayed on the
screen as soon as the app is launched. If the file does not exist, however,
we create it with the createFile() method and no content (nil). To keep the file
updated with the text inserted by the user in the Text View, we declare the
ViewController class as the delegate of the Text View and implement the
textViewDidChange() method. This method is called by the Text View every
time its content changes (see Chapter 5). Inside the method we read the
current text and call its write() method to store it in the file. Now, everything
the user writes in the Text View is preserved and always available.
main—This type property returns a reference to the app’s bundle. Listing 15-17: Loading a file added to the project
bundleURL—This property returns a URL structure with the bundle’s 
URL. import UIKit
string on the console, but this information is usually stored in the model or 15.3 Archiving
assigned to the objects that requires it.

Do It Yourself: Create a new project and update its AppDelegate class
The methods we have just studied to store data in files are enough for
with the code in Listing 15-17. Create a text file with a text editor,
simple models but present some limitations. We can only work with single
write some text in it, save it with the name quote.txt, and add it to values and with classes that already provide a way to turn their content
your project. Run the application. You should see the text inside the into data. Professional applications rely on more elaborated models that
file printed on the console. include collection of values and custom objects. To give us more flexibility,
Foundation offers the NSCoder class. This class can encode and decode
values to Data structures for storage purposes in a process called Archiving.
An NSCoder object not only encodes an object but also the objects it is
connected to, preserving the connections and the hierarchy. For example,
we may have two objects with properties that reference the other object.
Object 1 references Object 2 and Object 2 references Object 1. With
archiving, both objects are encoded, stored, and then decoded and
connected again when we need them.
Encoding and Decoding Implementing these methods, we can generate Data structures for
processing, or we can just store and retrieve the data from a file. In the

following example, we encode and decode a string to a Data structure and
use FileManager methods to create and read the file.
The NSCoder class provides all the methods necessary to encode and
decode the values of an object, but all the work is done by instances of two
Listing 15-18: Encoding and decoding data
NSCoder subclasses called NSKeyedArchiver and NSKeyedUnarchiver. The

NSKeyedArchiver class calls the encode() method on the objects to encode their
import UIKit
values and stores the data in a Data structure or a file. The NSKeyedUnarchiver
class, on the other hand, initializes the objects with the protocol’s initializer class ViewController: UIViewController {
override func viewDidLoad() {
and returns the original values. super.viewDidLoad()
The NSKeyedArchiver class offers the following type method to encode an
Object Graph and store it in a Data structure. let manager = FileManager.default
let documents = manager.urls(for: .documentDirectory, in: .userDomainMask)
let docURL = documents.first!
archivedData(withRootObject: Any, requiringSecureCoding:
let fileURL = docURL.appendingPathComponent("quotes.dat")
Bool)—This type method encodes the Object Graph of the object let filePath = fileURL.path
specified by the withRootObject argument and stores it in a Data if manager.fileExists(atPath: filePath) {
if let content = manager.contents(atPath: filePath) {
structure. The requiringSecureCoding argument is a Boolean value if let result = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSString.self, from:
content) as String? {
that determines whether the data will be secured or not.
print(result)
}
The NSKeyedUnarchiver class offers the following type method to decode an }
} else {
Object Graph from a Data structure. let quote = "Fiction is the truth inside the lie"
if let fileData = try? NSKeyedArchiver.archivedData(withRootObject: quote,
requiringSecureCoding: false) {
unarchivedObject(ofClass: Class, from: Data)—This type manager.createFile(atPath: filePath, contents: fileData, attributes: nil)
method decodes the data specified by the from argument and returns }
}
the original Object Graph. The ofClass argument determines the data }
type of the decoded object. }

unarchivedObject(ofClasses: [AnyClass], from: Data)—This
type method decodes the data specified by the from argument and Listing 15-18 presents a view controller with a simple example. A single
returns the original Object Graph. The ofClasses argument string is encoded and stored in a file called quotes.dat. As we did in
determines the data types of the decoded object. previous examples, we first check if the file exists with the fileExists()
method and then proceed accordingly. If the file does not exist, we convert
the string in the quote constant to a Data value with the archivedData() method Another requirement for custom structures is that they implement the
and create a file with it, but if the file already exists, we read its content initializers and methods defined in the NSSecureCoding protocol for the
with the contents() method and decode the data with the unarchivedObject() system to be able to encode and decode the data. Fortunately, the Swift
method to get back the string. Standard Library defines a protocol called Codable that turns a structure into
The unarchivedObject() method can only work with data types that conform to an encodable and decodable data type. All we need to do, is to make our
a protocol called NSSecureCoding. That is the reason why we had to specify structure conform to the protocol and the compiler takes care of adding all
the NSString class as the value of the ofClass argument and convert it at the the methods required to encode and decode its values. The following
end to a String structure with the as operator (the NSString class conforms to example creates a structure to store information about books.
the NSSecureCoding protocol but the String structure does not).
Listing 15-19: Encoding and decoding a custom data type
Do It Yourself: Create a new project. Update the ViewController class 
with the code in Listing 15-18. Run the application. The first time, import Foundation
the file is created, and no message is printed on the console. Stop struct Book: Codable {
and run the application again. Now you should see the quote on the var title: String
var author: String
console. var edition: Int
}

The methods of the NSKeyedArchiver and NSKeyedUnarchiver classes work with
Property List values (NSNumber, NSString, NSDate, NSArray, NSDictionary, NSData,
Once our custom structure is defined, we can create instances of it and
and the equivalents in Swift). In the previous example, we had to use an
archive them. The next example follows the same procedure as before.
NSString value, but if we want to archive our own data types, we must
When the main view is loaded, we check if the file already exists and read
convert them to Property List values. Foundation offers two classes for this
it or create a new one. To archive and unarchive the data, we use the same
purpose, PropertyListEncoder and PropertyListDecoder, which include the
methods, but this time we encode or decode the values into a Property
following methods to encode and decode values.
List.
encode(Value)—This method of the PropertyListEncoder class encodes Listing 15-20: Encoding and decoding arrays of custom structures
a value into a Property List value. 
decode(Type, from: Data)—This method of the PropertyListDecoder import UIKit
class decodes a Property List value into a value of the data type class ViewController: UIViewController {
specified by the first argument. The from argument is a Data structure override func viewDidLoad() {
super.viewDidLoad()
with the data to be decoded.
let manager = FileManager.default
let documents = manager.urls(for: .documentDirectory, in: .userDomainMask)
let docURL = documents.first! Do It Yourself: Create a new project. Create a new Swift file called
let fileURL = docURL.appendingPathComponent("userdata.dat") Book.swift for the structure in Listing 15-19. Update the ViewController
let filePath = fileURL.path class with the code in Listing 15-20. As happened with the previous
if manager.fileExists(atPath: filePath) {
if let content = manager.contents(atPath: filePath) { example, the first time you run the application, the file is created
if let result = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSData.self, from:
content) as Data? {
and nothing is printed on the console, but if you run the application
let decoder = PropertyListDecoder() again, the view controller decodes the content of the file created
if let books = try? decoder.decode([Book].self, from: result) {
for book in books {
before and prints the values of every Book object on the console.
print("\(book.title) - \(book.author) - \(book.edition)")
}
}
}
}
} else {
let book1 = Book(title: "IT", author: "Stephen King", edition: 2)
let book2 = Book(title: "Pet Sematary", author: "Stephen King", edition: 1)
let book3 = Book(title: "The Shining", author: "Stephen King", edition: 1)
let list = [book1, book2, book3]
With archiving, we can store not only objects but also their connections. The structure of the Core Data’s Object Graph is defined with a data model.
This organization is called Object Graph. Archiving is a good tool to store an This has nothing to do with the data model of the MVC pattern
Object Graph on file but presents some limitations. The Object Graph implemented in previous chapters; a Core Data model is a definition of the
stored this way is difficult to expand or modified. The entire graph must be type of objects the graph is going to contain (called Entities) and their
stored in the file again after the smallest change, and it is not easy to connections (called Relationships).
control the connections between objects to determine exactly which A model can be created from code, but Xcode offers a practical editor to
objects will be stored. The solution is called Core Data. Core Data is an define the structure of the graph. The model is stored in a file and then the
Object Graph manager that defines and manages its own objects and file is compiled and included in the Core Data system created for our app.
connections and stores them in a database. We can determine the Xcode offers a template to create this file.
composition of the objects and their relationships. The system takes care
of encoding and decoding the objects, preserving consistency and Figure 15-4: Option to create a Core Data model in the iOS panel
maximizing efficiency.
The file may be created with any name we want but it must have the
extension xcdatamodel. Once created, it is included in our project along
with the rest of the files. Clicking on it reveals the Xcode editor in the
Editor Area.
Every attribute must be associated with a data type for the objects to know
what kind of values they can manage (Figure 15-7, number 2). Clicking on
the attribute’s type, we can open a menu to select the right data type. The
most frequently used are Integer 16, Integer 32, Integer 64, Double, Float,

String, Boolean, Date, and Binary Data. The Integer 16, 32, or 64 options
are for Int16, Int32, and Int64 values, Double and Float are for Double and Float
We can change the name of the newly created entity by double-clicking
values, String is for String values, Boolean is for Bool values, Date is for Date
the name (Figure 15-6, number 1) or by editing the Name field in the Data
values, and Binary Data is for Data values.
Model Inspector panel (Figure 15-6, number 2).
An entity may contain as many attributes as our objects need. For example,
An entity defines the composition of the objects that are going to be part
we may add a few more attributes to complement the information
of the Object Graph, so the next step is to declare the type of values those
required for books.
objects are going to store. For this purpose, entities include Attributes


We could have also included another attribute for the author’s name in the
Books entity, but here is when we need to think about the structure of the
In the example of Figure 15-8, we added an attribute called year to store
Object Graph and how the information will be stored. If we include a String
the year in which the book was published, and two attributes of type
type attribute for the author's name inside the Books entity, every time the
Binary Data to store images (the book's cover and thumbnail). The data
user inserts a new book it will have to type the name of the author. This is
types used by these attributes are analog to the Swift data types we have
error prone, time consuming, and when several books of the same author
being working so far. The title attribute takes a String value, the year
are available, it is impossible to make sure that all share the same exact
attribute stores a value of type Int16, and the images are going to be stored
name (one book could have the author’s middle name and others just the
as Data structures.
first one, for example). Without the certainty of having the exact same
Most values don't require much consideration, but images are made of big
name, we can never incorporate features in our app such as ordering the
chunks of data. Storing large amounts of data in a Persistent Store can
books by author or getting the list of books written by a particular author.
affect the system's performance and slow down essential processes like
Things get worse when, along with the name, we also decide to store other
searching for values or migrating the model. One alternative is to store the
information about the author, like his or her date of birth. A proper
images in separate files, but it can get cumbersome to coordinate
organization of this information demands separate objects and therefore
hundreds of files with the data in the database. Fortunately, Core Data can
we must create new entities to represent them. Additional entities are
perform the process for us. All we need to do is to store the image as
added to the model in the same way as we did with the first one. Figure
Binary Data and select the option Allows External Storage, available in the
15-10, next, shows our model with a new entity called Authors containing
Data Model inspector panel inside the Utilities Area, as shown in Figure 15-
an attribute called name.
9, below. After the option is selected, the images assigned to that attribute
are going to be stored in separate files managed by the system.
Figure 15-10: Multiple Entities
Figure 15-9: Option to store images outside the Persistent Store

Entities are blueprints that we use to define the characteristics of the
objects we want to store. For instance, when we want to store a new book
in our example, we create a new object based on the Books entity. That
object will have four properties corresponding to the values of its title,
year, cover, and thumbnail. The same happens when we want to store
information of an author. We create a new object based on the Authors
entity and assign the name of the author to its name property. At the end, 
we will have two objects in the storage space, one for the book and
another for the author. But if we want to retrieve these objects later, we A relationship only needs two values: its name (the name of the property)
need a way to know what Books object is related to what Authors object. and the destination (the type of objects it is referencing), but it requires
To create this connection, the Core Data model includes Relationships. some parameters to be set. We must tell the model if the relationship is
Relationships are like properties in one object containing references to going to be optional, define its type (To-One or To-Many), and determine
other objects. They could have a reference to only one object or a set of what should happen to the destination object if the source object is
objects. For example, in the Books entity, we can create a relationship that deleted (the Delete Rule). All these options are available in the Data Model
contains a reference to only one object of the Authors entity, because Inspector panel when the relationship is selected.
there could only be one author per book (for this example we are assuming
that our app is storing books written only by one author). On the contrary, Figure 15-12: Relationship settings
in the Authors entity, we need to establish a relationship that contains
references to multiple Books objects, because an author may have written
several books, not just one. Core Data calls these relationships according to
the number of objects they may reference. The names are To-One and To-
Many and they are created pressing the + button in the Relationships area
below the Attributes area. Figure 15-11, below, shows a relationship called
author we have created for the Books entity of our example.
By default, the relationship is set to Optional, which means that the source
may be connected to a destination object or not (a book can have an
author or not), the Type of the relationship is set to To-One (a book can
only have one author), and the Delete Rule is set to Nullify. The following
are all the values available for this rule.
Do It Yourself: Create a new project. Open the File menu at the top
of the screen, go to New and select the File option to create a new
file. Move to the Core Data section in the iOS panel and select the
Data Model option (Figure 15-4). Save the file with the name
books.xcdatamodeld. Click on this file to open the editor (Figure 15-
 5). Press the Add Entity button to create two new entities with the
names Authors and Books. Create the attributes for these entities as
Two relationships are simple to follow, but multiple relationships illustrated in Figures 15-8 and 15-10. Create the relationships for
connecting several entities together can turn the model into an every entity as shown in Figure 15-14. Set the books relationship to
indecipherable mess. To help us identify every component of the model, To-Many and keep the rest of the values by default. Click on the
Xcode offers an additional visualization style that displays the entities as Editor Style button to see a graphical representation of the model.
boxes and the relationships as arrows connecting the boxes. The option,
called Editor Style, is at the bottom of the Editor Area (Figure 15-5, number
3). Figure 15-15, below, shows what our model looks like when we switch
to this style (Notice that the To-Many relationship is represented by double
arrows).
Core Data Stack NSManagedObjectContext creates and manages the context that intermediates
between our app and the Persistent Store. Although we can instantiate

these objects and create the stack ourselves, the framework offers the
NSPersistentContainer class, which takes care of everything for us. The class
The creation of the model is just the first step in the definition of the Core
includes the following initializer and also properties to access each object
Data system. Once we have all the entities along with their attributes and
of the stack.
relationships set up, we must initialize Core Data. Core Data is created from
a group of objects that manage all the processes, from the organization of
NSPersistentContainer(name: String)—This initializer creates an
the Object Graph to the storage of the graph in a database. There is an
object that defines a Core Data stack. The name
NSPersistentContainer
object that manages the model, an object that stores the data on file, and
an object that intermediates between this Persistent Store and our own argument is a string representing the name of the container. This
code. The scheme is called stack. Figure 15-16 illustrates a common Core value must match the name of the Core Data model (the file's name,
Data stack. without the extension).
managedObjectModel—This property sets or returns an
Figure 15-16: Core Data stack NSManagedObjectModel object that represents the Core Data model.
persistentStoreCoordinator—This property sets or returns the
NSPersistentStoreCoordinator object that manages the Persistent Stores
available.
viewContext—This property sets or returns the
object in charge of the stack's context that we
NSManagedObjectContext
use to access and modify the Object Graph.

To create the Core Data stack from our app, we must initialize a new
NSPersistentContainer object and then load the Persistent Stores (one by
The code in our application interacts with the Context to manage the
default). Because the stores may take time to load, the class offers a
objects and access their values, the Context asks the Persistent Store to
specific method for this purpose.
read or add new objects to the graph, and the Persistent Store processes
the Object Graph and saves it in the database.
The Core Data framework offers classes to create objects that represent
loadPersistentStores(completionHandler: Closure)—This
every part of the stack. The NSManagedObjectModel class manages the model, method loads the Persistent Stores and executes a closure when the
the NSPersistentStore class manages a Persistent Store, the process is over. The closure receives two arguments, an
NSPersistentStoreCoordinator class is used to manage all the Persistent Stores NSPersistentStoreDescription object with the configuration of the stack, and
available (a Core Data stack can have multiple Persistent Stores), and the an optional NSError value to report errors.
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
All the communication between our app and the data in the Persistent func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
Store is done through the context. The context is created by the container [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
from the NSManagedObjectContext class. This class includes properties and
}
methods to manage the context and the objects in the Persistent Store. func application(_ application: UIApplication, configurationForConnecting
The following are the most frequently used. connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) ->
UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole:
hasChanges—This property returns a Boolean value that indicates if connectingSceneSession.role)
}
the context has changes that have to be saved in the Persistent Store. lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "books")
save()—This method saves the changes in the Persistent Store. container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
reset()—This method resets the context to a basic state. All the fatalError("Unresolved error \(error), \(error.userInfo)")
objects and modifications our app introduced to the context are }
})
ignored. return container
delete(NSManagedObject)—This method deletes an object in the }()
}
Persistent Store. 
Chapter 5 (see Listing 5-10). The following code illustrates a possible Managed Objects
implementation. We read the property in the app's delegate and assign its

value to a local property that we can use later from other methods in the
view controller to access the context.
Core Data does not store our custom objects; it defines a class called
NSManagedObject for this purpose. Every time we want to store information
Listing 15-22: Accessing the context from a view controller
in the database, we must create an NSManagedObject object, associate that

object to an Entity, and store the data the entity allows. For example, if we
import UIKit
import CoreData create an object associated to the Books entity, we are only allowed to
store five values that corresponds to the Entity's attributes and relationship
class ViewController: UIViewController {
var context: NSManagedObjectContext!
(title, year, cover, thumbnail, and author). The class includes the following
initializer and methods to create and manage the objects.
override func viewDidLoad() {
super.viewDidLoad()
let app = UIApplication.shared NSManagedObject(context: NSManagedObjectContext)—This
let appDelegate = app.delegate as! AppDelegate initializer creates an instance of the NSManagedObject class and adds it
context = appDelegate.persistentContainer.viewContext
} to the context specified by the argument.
}
 fetchRequest()—This type method generates a fetch request for an
entity. A fetch request is a request we use to fetch objects of a
Do It Yourself: Update the AppDelegate class with the code in Listing particular entity from the Persistent Store.
15-21. Replace the value of the name argument in the
NSPersistentContainer initializer by the name of your model's file To simplify our work, the system allows us to define subclasses of the
(books). At this moment, the app doesn't do anything other than NSManagedObject class that correspond to the entities of our model (Instead
creating the stack. of creating instances of the NSManagedObject class, we create instances of
the Books and Authors classes). Because this is common practice for any
developer, Xcode does it automatically for us. All we need to do is to
associate each Entity with a subclass from the Data Model Inspector panel.
The option is available in a section called Class.
To ask Xcode to create the subclasses for us, we must select the entities
one by one, click on the Class Definition value for the Codegen option
(number 2), and make sure that the name of the subclass is specified in the
Name field (number 1). Once the options are set, the classes are
automatically created. For example, when we set these options for the
entities in our model, Xcode creates a subclass of NSManagedObject called
Books with the properties title, year, cover, thumbnail, and author, and a subclass
called Authors with the properties name and books. From now on, all we need
to do to store a book in the Persistent Store is to create an instance of the
Books class using the NSManagedObject initializer.
Reading and writing information on a database is a delicate process. The All the interaction between our code and the Persistent Store is done
database may be accessed by different parts of the application and from through the context. When we want to access the objects already stored,
different threads, which can cause errors or even data corruption. To make add new ones, remove them, or modify any of their values, we must do it
sure the data is accurate and safe, we must access the database from the in the context and then move those changes from the context to the
same thread assigned to the Core Data context. For this purpose, the Persistent Store. To illustrate how this process works, we are going to
NSManagedObjectContext class includes the following asynchronous method. create a simple example that allows us to add, delete, and list books.
perform(schedule: ScheduledTaskType, Closure)—This Figure 15-18: Interface to list and add books
asynchronous method performs a closure in the thread assigned to
the Core Data context. The schedule argument determines how the
closure is going to be executed. It is an enumeration with the values
immediate (the closure runs asynchronously), and enqueued (the closure
runs concurrently). If the argument is ignored, the closure is executed
asynchronously.
bookTitle.becomeFirstResponder()
Listing 15-23: Defining the model to work with Core Data }
 @IBAction func saveBook(_ sender: UIBarButtonItem) {
let title = bookTitle.text!.trimmingCharacters(in: .whitespaces)
import UIKit let year = Int16(bookYear.text!)
import CoreData if title != "" && year != nil {
Task(priority: .high) {
enum Sections { await storeBook(title: title, year: year!)
case main }
} }
struct ApplicationData { }
var dataSourceBooks: UITableViewDiffableDataSource<Sections, NSManagedObjectID>! func storeBook(title: String, year: Int16) async {
var listOfBooks: [Books] = [] await context.perform {
} let newBook = Books(context: self.context)
var AppData = ApplicationData() newBook.title = title
 newBook.year = year
newBook.thumbnail = UIImage(named: "nothumbnail")?.pngData()
do { system automatically assigns the value nil to the rest of the properties (cover
try self.context.save()
self.closeScene() and author).
} catch { The Books() initializer inserts the new object into the context, but this
print("Error: \(error)")
change is not permanent. If we close the app after the values are assigned
}
} to the properties, the object is lost. To persist the changes, we must save
} the context with the save() method. This method takes the information in
func closeScene() {
Task(priority: .high) { the context and modifies the Persistent Store with it, so everything is
await MainActor.run { stored on file.
navigationController?.popViewController(animated: true)
}
} Do It Yourself: Create a Swift file called ApplicationData.swift for
}
}
the model in Listing 15-23. Create a subclass of the UIViewController
 class called EditBookViewController and assign it to the second scene.
Connect the first Text Field with an Outlet called bookTitle and the
As illustrated in this example, the first thing we need to do in any view second Text Field with an Outlet called bookYear. Connect the Save
controller that wants to access Core Data is to get a reference to the
Book button with an Action called saveBook(). Complete the class with
context from the app's delegate. In the view controller of Listing 15-24, we
the code in Listing 15-24.
assigned the context to a property called context for easy access.
When the Save Book button is pressed, the saveBook() method checks if the
The process to get the objects back from the Persistent Store is the
values in the Text Fields are valid and executes an asynchronous method
opposite. Instead of moving the changes in the context to the Persistent
called storeBook() to create the Books object and store it in the Persistent
Store, we get the objects from the Persistent Store and move them into the
Store. This method performs two asynchronous tasks: the perform() method
context. Once the objects are in the context, we can read their properties,
of the Core Data context to safely access the Persistent Store, and the run()
modify the values, or delete them. Core Data defines the NSFetchRequest
method of the Main Actor to remove the scene from the main thread
class to request objects from the Persistent Store. The following are some
when the process is over (see Concurrency in Chapter 14). To store the
of the properties included by the class for configuration.
values in the Persistent Store, we create a new object with the Books()
initializer. This not only creates a new object of type Books but it also adds it
to the context. Next, we assign the values inserted by the user to the
predicate—This property sets or returns the predicate used to filter
object's title and year properties and also a placeholder image called the objects. It is a value of type NSPredicate; a Foundation class used to
nothumbnail to the thumbnail property (the image is available on our establish logical conditions that describe objects in the Persistent
website). The title and year values are assigned directly to the properties, Store.
but the image must be converted to a Data structure with the pngData() sortDescriptors—This property sets or returns an array of sort
method of the UIImage object (see UIImage in Chapter 5). Notice that the descriptors that determine what the order of the objects obtained by
the request should be. It is an array of values of type NSSortDescriptor, a 
Foundation class used to sort the objects according to the value of a import UIKit
import CoreData
property in ascending or descending order.
class BooksViewController: UITableViewController {
fetchLimit—This property sets or returns the maximum number of var context: NSManagedObjectContext!
objects that the request should return. It takes a value of type Int.
override func viewDidLoad() {
resultType—This property sets or returns a value that determines super.viewDidLoad()
let app = UIApplication.shared
the type of data returned by the request. The framework offers the
let appDelegate = app.delegate as! AppDelegate
NSFetchRequestResultType structure with properties to define the value. context = appDelegate.persistentContainer.viewContext
The properties available are managedObjectResultType (it returns values of tableView.register(UITableViewCell.self, forCellReuseIdentifier: "booksCell")
type NSManagedObject), managedObjectIDResultType (it returns the prepareDataSource()
}
identification values of the NSManagedObject objects instead of the override func viewWillAppear(_ animated: Bool) {
objects themselves), dictionaryResultType (it returns a dictionary with the super.viewWillAppear(animated)
Task(priority: .high) {
values of the properties), and countResultType (it returns an integer await loadRequest()
value with the total of objects found).
await MainActor.run {
propertiesToFetch—This property sets or returns an array of values prepareSnapshot()
}
that determine the properties we want to get (by default, all the }
properties of the NSManagedObject objects are returned). The properties }
func prepareDataSource() {
of an entity (attributes) are represented by objects of the AppData.dataSourceBooks = UITableViewDiffableDataSource<Sections, NSManagedObjectID>
NSPropertyDescription class, or subclasses of it. (tableView: tableView) { tableView, indexPath, itemID in
let cell = tableView.dequeueReusableCell(withIdentifier: "booksCell", for: indexPath)
if let item = AppData.listOfBooks.first(where: { $0.objectID == itemID }) {
Every time we want to read objects from the Persistent Store, we must var config = cell.defaultContentConfiguration()
config.text = item.title
create an NSFetchRequest object to determine what type of objects we want. config.secondaryText = item.author?.name ?? "Undefined"
Because the request has to be associated to an entity, the subclasses of the config.secondaryTextProperties.color = .systemGray
NSManagedObject class representing our entities include the fetchRequest()
if let data = item.thumbnail, let image = UIImage(data: data) {
method. This method returns an NSFetchRequest object already associated to config.image = image
the entity. Once the request object is ready, we must fetch the objects with } else {
config.image = UIImage(named: "nothumbnail")
the fetch() method provided by the context. The following is the view }
controller for the Table View of our example. The code performs a request, config.imageProperties.maximumSize = CGSize(width: 60, height: 60)
stores the objects in the model, and shows their values on the screen. cell.contentConfiguration = config
}
return cell
Listing 15-25: Fetching values from the Persistent Store }
When the user presses the Add Book button in the initial scene (Figure 15- Listing 15-26: Adding an author to the book
18, center), the next scene now shows three input options: a Text Field to 
insert the title of the book, a Text Field to insert the year, and the Select import UIKit
button to select the author (Figure 15-20, left). This button is connected to import CoreData
the Table View Controller for the authors with a Show segue (Figure 15-20, class EditBookViewController: UIViewController {
center). In addition, we have included a bar button called Add Author to @IBOutlet weak var bookTitle: UITextField!
open a scene that includes a Text Field to insert the name of a new author @IBOutlet weak var bookYear: UITextField!
@IBOutlet weak var authorName: UILabel!
(Figure 15-20, right). var context: NSManagedObjectContext!
var selectedAuthor: Authors!
Do It Yourself: Add two labels and a button called Select to the override func viewDidLoad() {
EditBookViewController’s
scene (Figure 15-20, left). Add a Table View super.viewDidLoad()
let app = UIApplication.shared
Controller to the Storyboard and connect the Select button to this let appDelegate = app.delegate as! AppDelegate
scene with a Show segue (Figure 15-20, center). Add a bar button context = appDelegate.persistentContainer.viewContext
called Add Author to this scene. Add a new scene to the Storyboard bookTitle.becomeFirstResponder()
and connect the Add Author button to this scene with a Show segue }
@IBAction func saveBook(_ sender: UIBarButtonItem) {
(Figure 15-20, right). Add a label, an input field, and a bar button let title = bookTitle.text!.trimmingCharacters(in: .whitespaces)
let year = Int16(bookYear.text!)
if title != "" && year != nil { scenes added to the interface in Figure 15-20, the backAuthor() method is
Task(priority: .high) {
await storeBook(title: title, year: year!) executed (due to the Unwind Segues). In this method, the code gets the
} Authors object that represents the author, assigns it to the selectedAuthor
}
}
property, and updates the label on the screen with the value of its name
func storeBook(title: String, year: Int16) async { property. (This process is done for each Unwind Segue, so no matter if the
await context.perform { author is selected from the table or inserted in the form, its name is always
let newBook = Books(context: self.context)
newBook.title = title shown on the screen and the Authors object is assigned to the book when
newBook.year = year the user presses the Save Book button.)
newBook.author = self.selectedAuthor
The process to list and create new Authors objects is the same we used for
do { books. We perform a request to get the Authors objects from the Persistent
try self.context.save() Store and store them in a property in the model. The following are the
self.closeScene()
} catch { properties we must add to the model to manage this information.
print("Error: \(error)")
}
} Listing 15-27: Defining a model to manage books and authors
} 
func closeScene() {
import UIKit
Task(priority: .high) {
import CoreData
await MainActor.run {
navigationController?.popViewController(animated: true)
enum Sections {
}
case main
}
}
}
struct ApplicationData {
@IBAction func backAuthor(_ segue: UIStoryboardSegue) {
var dataSourceBooks: UITableViewDiffableDataSource<Sections, NSManagedObjectID>!
if segue.identifier == "backFromList" {
var dataSourceAuthors: UITableViewDiffableDataSource<Sections, NSManagedObjectID>!
let controller = segue.source as! AuthorsViewController
selectedAuthor = controller.selectedAuthor
var listOfBooks: [Books] = []
authorName.text = selectedAuthor.name
var listOfAuthors: [Authors] = []
} else if segue.identifier == "backFromNew" {
}
let controller = segue.source as! EditAuthorViewController
var AppData = ApplicationData()
selectedAuthor = controller.selectedAuthor

authorName.text = selectedAuthor.name
}
} With the model ready, we can now load and show the authors to the user.
}
 The following is the view controller to manage the Table View Controller
added to the interface for this purpose (see Figure 15-20). We call it
This view controller manages the scene that allows the user to insert new AuthorsViewController.
books, but it also must process the author selected by the user and assign
it to the book. When the user selects or inserts a new author from the Listing 15-28: Listing authors
 }
}
import UIKit }
import CoreData func prepareSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Sections, NSManagedObjectID>()
class AuthorsViewController: UITableViewController { snapshot.appendSections([.main])
var context: NSManagedObjectContext! snapshot.appendItems(AppData.listOfAuthors.map({ $0.objectID }))
var selectedAuthor: Authors! AppData.dataSourceAuthors.apply(snapshot, animatingDifferences: false)
}
override func viewDidLoad() { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
super.viewDidLoad() if let itemID = AppData.dataSourceAuthors.itemIdentifier(for: indexPath) {
let app = UIApplication.shared if let item = AppData.listOfAuthors.first(where: { $0.objectID == itemID }) {
let appDelegate = app.delegate as! AppDelegate selectedAuthor = item
context = appDelegate.persistentContainer.viewContext }
performSegue(withIdentifier: "backFromList", sender: self)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "authorsCell") }
prepareDataSource() }
} }
override func viewWillAppear(_ animated: Bool) { 
super.viewWillAppear(animated)
Task(priority: .high) {
await loadRequest() Other than changing the name of the array (listOfAuthors), the rest of the
code, including the fetch request, is the same we used to list books. The
await MainActor.run {
prepareSnapshot() code also implements the protocol method tableView(UITableView,
} didSelectRowAt:) to assign the author selected by the user to the selectedAuthor
}
} property and trigger the Unwind Segue (the example assumes that we
func prepareDataSource() { have created an Unwind Segue from the Table View Controller to the
AppData.dataSourceAuthors = UITableViewDiffableDataSource<Sections,
backAuthor() Action and have identified the segue with the string
NSManagedObjectID>(tableView: tableView) { tableView, indexPath, itemID in
let cell = tableView.dequeueReusableCell(withIdentifier: "authorsCell", for: indexPath) "backFromList".
if let item = AppData.listOfAuthors.first(where: { $0.objectID == itemID }) { The view controller to add new authors is also very similar to the one we
var config = cell.defaultContentConfiguration()
config.text = item.name used to add new books. We have to take the text inserted by the user in
cell.contentConfiguration = config the Text Field, create the new Authors object to represent the author, save
}
return cell
the context, and finally trigger the Unwind Segue to move back to the
} EditBookViewController’s scene (again, the example assumes that we have
} created an Unwind Segue from this scene to the backAuthor() Action
func loadRequest() async {
await context.perform { identified with the string "backFromNew"). We called this view controller
let request: NSFetchRequest<Authors> = Authors.fetchRequest() EditAuthorViewController.
do {
AppData.listOfAuthors = try self.context.fetch(request)
} catch { Listing 15-29: Inserting new authors
print("Error: \(error)")
 
import UIKit
import CoreData With these additions, our basic app is complete. When we pressed the
Select button, the app opens a Table View with all the authors available
class EditAuthorViewController: UIViewController {
@IBOutlet weak var authorName: UITextField! (Figure 15-21, center). If there are no authors or the author we want is not
var context: NSManagedObjectContext! on the list, we can press the Add Author button and insert a new one
var selectedAuthor: Authors!
(Figure 15-21, right). Every time we select an author from the list or insert
override func viewDidLoad() { a new one, the app goes back to the scene with the book’s information and
super.viewDidLoad()
shows the name of the author on the screen (Figure 15-21, left). When the
let app = UIApplication.shared
let appDelegate = app.delegate as! AppDelegate book is saved, the Authors object that represents the author is assigned to
context = appDelegate.persistentContainer.viewContext the book’s author property and therefore the name of the author is now
authorName.becomeFirstResponder() shown on the list of books.
}
@IBAction func saveAuthor(_ sender: UIBarButtonItem) {
let name = authorName.text!.trimmingCharacters(in: .whitespaces)
Figure 15-21: Assigning an author to a book
if name != "" {
Task(priority: .high) {
await storeAuthor(name: name)
}
}
}
func storeAuthor(name: String) async {
await context.perform {
self.selectedAuthor = Authors(context: self.context)
self.selectedAuthor.name = name
do { 
try self.context.save()
self.closeScene()
} catch { Do It Yourself: Update the model with the code in Listing 15-27.
print("Error: \(error)")
}
Update the EditBookViewController class with the code in Listing 15-26.
} Connect the label that is going to show the name of the author to
}
func closeScene() { the authorName Outlet. Create a UITableViewController subclass called
Task(priority: .high) { AuthorsViewController and assign it to the new Table View Controller
await MainActor.run {
performSegue(withIdentifier: "backFromNew", sender: self) (Figure 15-20, center). Complete the class with the code in Listing
} 15-28. Create an Unwind Segue to the backAuthor() method for this
}
} scene with the identifier "backFromList". Create a UIViewController
}
subclass called EditAuthorViewController and assign it to the last scene Counting Objects
(Figure 15-20, right). Connect the Text Field in this scene with an

Outlet called authorName and complete the EditAuthorViewController class
with the code in Listing 15-29. Create an Unwind Segue to the The NSManagedObjectContext class includes the count() method to count the
backAuthor() method for this scene with the identifier number of objects in a request. The method returns an integer with the
"backFromNew". Run the application and press the Add New Book number of objects we would get if we call the fetch() method with the same
button. Press the Select button and select or insert a new author. request. For example, we can use it in the EditBookViewController class to get
Insert the rest of the information and save the book. You should see the number of authors already available. The request is always the same,
the list of books with their respective authors. but instead of fetching the objects we count them with the count() method.
bookTitle.becomeFirstResponder()
Task(priority: .background) {
await context.perform {
let request: NSFetchRequest<Authors> = Authors.fetchRequest()
if let total = try? self.context.count(for: request) {
print("Total Authors: \(total)")
}
}
}
}

the AuthorsViewController class of Listing 15-28. Instead of assigning just the func loadRequest() async {
await context.perform {
name of the author to the cell’s label, we count the books associated to let request: NSFetchRequest<Books> = Books.fetchRequest()
the author and create a string with this value and the author's name. Now request.predicate = NSPredicate(format: "year = 1983")
do {
the user can see how many books are available per author. AppData.listOfBooks = try self.context.fetch(request)
} catch {
print("Error: \(error)")
}
}
}

This example defines an NSPredicate object for the BooksViewController class to print("Error: \(error)")
}
search for books published in 1983 (the year property is equal to 1983). }
If we are trying to search for a value in a relationship, we can concatenate }

the properties with dot notation. For example, the following request looks
for books written by the author Stephen King.
We may include all the placeholders and arguments we need. The
placeholders are replaced by the arguments one by one, in consecutive
Listing 15-33: Filtering books by author
order, as with formatted strings (see String structures in Chapter 3). For

example, we can search for books of a particular author and year. (Notice
func loadRequest() async {
await context.perform { that since the value of the year property is an integer, we use the %d
let request: NSFetchRequest<Books> = Books.fetchRequest() placeholder.)
request.predicate = NSPredicate(format: "author.name = 'Stephen King'")
do {
AppData.listOfBooks = try self.context.fetch(request) Listing 15-35: Creating filters with multiple values
} catch { 
print("Error: \(error)")
} func loadRequest() async {
} let search = "Stephen King"
} let year = 1983

await context.perform {
let request: NSFetchRequest<Books> = Books.fetchRequest()
The name of the author was inserted inside the string using single quotes, request.predicate = NSPredicate(format: "author.name = %@ && year = %d", search,
but we do not always know beforehand what value we need to search for. year)
do {
To incorporate the value of a variable inside the formatted string, we can AppData.listOfBooks = try self.context.fetch(request)
use placeholders. Placing the characters %@ inside the string, for example, } catch {
print("Error: \(error)")
indicates to the initializer that the value after the comma goes in that }
place. }
}

Listing 15-34: Creating filters with placeholders

Do It Yourself: Replace the viewWillAppear() method in the
func loadRequest() async {
let search = "Stephen King" class with any of the methods introduced above to
BooksViewController
see how predicates work. Add books with different authors and
await context.perform {
let request: NSFetchRequest<Books> = Books.fetchRequest() years to test them.
request.predicate = NSPredicate(format: "author.name = %@", search)
do {
AppData.listOfBooks = try self.context.fetch(request)
} catch {
Predicates use comparison and logical operators like those offered by Swift. await context.perform {
let request: NSFetchRequest<Books> = Books.fetchRequest()
For example, we can compare values with the operators =, !=, >, <, >= and request.predicate = NSPredicate(format: "author.name BEGINSWITH[c] %@", search)
<=, and also concatenate conditions with the characters && (or the word do {
AppData.listOfBooks = try self.context.fetch(request)
AND), || (or the word OR) and ! (or the word NOT). Predicates also include
} catch {
keywords for a more precise search. The following are the most frequently print("Error: \(error)")
used. }
}
}
BEGINSWITH—The condition determined by this keyword is true 
when the expression on the left begins with the expression on the
IMPORTANT: Diacritics are the small marks used in some languages
right.
to change the pronunciation of a letter, like the visible stress over
CONTAINS—The condition determined by this keyword is true when Spanish vowels. When we specify the character d between square
the expression on the left contains the expression on the right. brackets, the search ignores these marks and looks for the letter in
ENDSWITH—The condition determined by this keyword is true when its basic form. The c and d characters are usually implemented
the expression on the left ends with the expression on the right. together, as in [cd].
LIKE—The condition determined by this keyword is true when the
A practical application of predicates is to check for duplicates. Storing
expression on the left is equal to the expression on the right.
duplicated values is something that all applications should be prepared to
IN—The condition determined by this keyword is true when the avoid. For example, if we open our application and insert an author that
expression on the left is equal to any of the values included in the already exists, two Authors objects with the same name will be stored in the
expression on the right. The values are provided as an array between Persistent Store. To avoid this situation, we can use a request with a
parentheses. predicate that searches for authors of the same name before creating a
new object. The following example modifies the storeAuthor() method of the
These keywords may be accompanied by the characters c or d between EditAuthorViewController class to avoid duplicates.
The protocol method performs the segue when a cell is selected by the
user, and the prepare() method gets the index path of the selected row, uses
this value to get the Books object from the model, and assigns it to a
property in the EditBookViewController class called selectedBook.
The EditBookViewController class must receive this object and show its values
to the user. But we must also contemplate that the user might be trying to
insert a new book instead. The following are all the changes we need to
 introduce to this class to respond to both situations.
The interface in Figure 15-22 includes a Show segue from the Table View Listing 15-42: Editing a book
Controller to the second scene identified with the name "showEditBook". 
When the user selects a cell, we must perform this segue and send the import UIKit
import CoreData
Books object representing the book selected by the user from the
BooksViewController view controller to the EditBookViewController view controller class EditBookViewController: UIViewController {
@IBOutlet weak var bookTitle: UITextField!
@IBOutlet weak var bookYear: UITextField!
Listing 15-43: Defining the request and the snapshot from the model

import UIKit
import CoreData
enum Sections {
case main
}
class ApplicationData {
var dataSourceBooks: MyDataSource!
var dataSourceAuthors: UITableViewDiffableDataSource<Sections, NSManagedObjectID>!
performance. This also applies to other operations with the context. Fetched Results Controller
The context should be saved only after all the operations are

performed.
Storing the results of a request in an array is not recommended in most
situations. Tables and Collection Views can handle thousands of items and
putting all those items in an array might consume too many resources. The
solution is to get only the objects the interface needs at a particular
moment, but this is error prone and demands our code to keep track of the
elements already loaded and request only those that we do not have.
Programming an application to do this work efficiently is difficult. For this
reason, Core Data offers the NSFetchedResultsController class. This class
provides highly optimized code that intermediates between the app and
the Persistent Store; taking care of fetching the objects the interface needs
and updating the list of objects available when some are modified, added,
or removed. To create the controller, the class provides the following
initializer.
NSFetchedResultsController(fetchRequest: NSFetchRequest,
managedObjectContext: NSManagedObjectContext,
sectionNameKeyPath: String?, cacheName: String?)—This
initializer creates an NSFetchedResultsController object that fetches
NSManagedObject objects from the Persistent Store. The fetchRequest
argument is an NSFetchRequest object with the request we want the
controller to use to fetch the objects, the managedObjectContext
argument is a reference to the Core Data context, the
sectionNameKeyPath argument identifies the name of the property used
to create the table’s sections, and the cacheName argument defines the
name of the file used to cache the objects returned by the request.
methods to create the request and the snapshot because now everything super.viewWillAppear(animated)
do {
is managed by the Fetched Results Controller. try AppData.fetchedController.performFetch()
The Fetched Results Controller is created from NSManagedObject objects } catch {
print("Error")
(Books and Authors), but the diffable data source and the snapshots are }
created with their NSManagedObjectID values. To get the NSManagedObject }
func prepareDataSource() {
object from these identifiers, the NSManagedObjectContext class includes the AppData.dataSourceBooks = MyDataSource(tableView: tableView) { tableView, indexPath,
following methods. itemID in
let cell = tableView.dequeueReusableCell(withIdentifier: "booksCell", for: indexPath)
if let item = try? self.context.existingObject(with: itemID) as? Books {
existingObject(with: NSManagedObjectID)—This method var config = cell.defaultContentConfiguration()
returns the object identified with the identifier specified by the with config.text = item.title
config.secondaryText = item.author?.name ?? "Undefined"
argument or nil if no object is found in the context. config.secondaryTextProperties.color = .systemGray
object(with: NSManagedObjectID)—This method returns the if let data = item.thumbnail, let image = UIImage(data:data){
config.image = image
object identified with the identifier specified by the with argument. If } else {
the object is not found in the context, it is fetched from the Persistent config.image = UIImage(named: "nothumbnail")
}
Store. config.imageProperties.maximumSize = CGSize(width: 60, height: 60)
cell.contentConfiguration = config
The following are the changes required in the BooksViewController class to get }
the Books objects from a Fetched Results Controller. return cell
}
}
Listing 15-47: Working with a Fetched Results Controller func prepareFetchedController() {
let request: NSFetchRequest<Books> = Books.fetchRequest()
 let sort = NSSortDescriptor(key: "title", ascending: true, selector:
import UIKit #selector(NSString.caseInsensitiveCompare(_:)))
import CoreData request.sortDescriptors = [sort]
AppData.fetchedController = NSFetchedResultsController(fetchRequest: request,
class BooksViewController: UITableViewController, NSFetchedResultsControllerDelegate { managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
var context: NSManagedObjectContext! AppData.fetchedController.delegate = self
}
override func viewDidLoad() { func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
super.viewDidLoad() didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
let app = UIApplication.shared let newsnapshot = snapshot as NSDiffableDataSourceSnapshot<Sections, NSManagedObjectID>
let appDelegate = app.delegate as! AppDelegate AppData.dataSourceBooks.apply(newsnapshot, animatingDifferences: true)
context = appDelegate.persistentContainer.viewContext }
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "booksCell")
performSegue(withIdentifier: "showEditBook", sender: self)
prepareDataSource()
}
prepareFetchedController()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
}
if segue.identifier == "showEditBook" {
override func viewWillAppear(_ animated: Bool) {
if let path = tableView.indexPathForSelectedRow {
if let itemID = AppData.dataSourceBooks.itemIdentifier(for: path) { Because this process is required every time there are changes in the
if let item = try? context.existingObject(with: itemID) as? Books {
let controller = segue.destination as! EditBookViewController context, we call it in the viewWillAppear() method to make sure that every
controller.selectedBook = item time the user opens this scene, the Fetched Results Controller is updated.
}
}
The diffable data source is the same as before, except this time instead of
} getting the object from an array in the model we call the existingObject(with:)
}
method with the identifier received by the closure to get the object from
}
@IBAction func editBooks(_ sender: UIBarButtonItem) { the context. This returns the Books object associated to that identifier that
let editing = !tableView.isEditing we can use to configure the cell.
tableView.setEditing(editing, animated: true)
} We do the same in the prepare() method. When the user selects a book to
} edit the values, we get the identifier as always, and then call the

existingObject(with:) method again to get the Books object and send it to the
EditBookViewController object.
To define the Fetched Results Controller, we created a method called
prepareFetchedController() and execute it as soon as the view is loaded. The
The existingObject(with:) method must also be implemented in the
MyDataSource class to get the object the user wants to delete, as shown next.
NSFetchedResultsController object requires a request with a sort descriptor to
know the order in which the Books objects are going to be offered to the
Listing 15-48: Working with objects identifiers
table. In this example, we create the NSFetchRequest object for the Books

entity and assign to it a sort descriptor that sorts the books by title.
import UIKit
Besides the request, the NSFetchedResultsController initializer requires three import CoreData
more values: a reference to the context, the name of the property that is
class MyDataSource: UITableViewDiffableDataSource<Sections, NSManagedObjectID> {
going to be used to create the sections for the table, and the name of the override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool
file used to cache the information for better performance. The last two {
return true
parameters are not going to be used in this example, so we declared them }
as nil. override func tableView(_ tableView: UITableView, commit editingStyle:
A Fetched Results Controller calls a protocol method to update the UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
snapshot every time a change in the context is detected. The view if let itemID = AppData.dataSourceBooks.itemIdentifier(for: indexPath) {
controller in Listing 15-47 conforms to the NSFetchedResultsControllerDelegate let app = UIApplication.shared
let appDelegate = app.delegate as! AppDelegate
protocol to be able to implement this method. In the method, all we need let context = appDelegate.persistentContainer.viewContext
to do is to cast the generic snapshot to an object with the right data types if let item = try? context.existingObject(with: itemID) as? Books {
and apply it to the diffable data source. This process updates the Table Task(priority: .high) {
View and shows the new values on the screen. await deleteBook(context: context, item: item)
}
Once we have the NSFetchedResultsController object, we must call the }
performFetch() method to fetch the objects from the Persistent Store. }
}
} Search
func deleteBook(context: NSManagedObjectContext, item: Books) async {
await context.perform {

context.delete(item)
do {
try context.save() The process to allow users to search for values in a Table View associated
} catch {
print("Error: \(error)") with an NSFetchedResultsController object does not differ from any other we
} have seen before. We must define a Search Controller, conform to the
}
} UISearchResultsUpdating and UISearchBarDelegate protocols, and implement their
} methods (see Chapter 10, Listing 10-35). In the updateSearchResults(for:)

method, we must modify the predicate of the request assigned to the
NSFetchedResultsController object to search for the value inserted by the user,
The process is the same as before, but now we call the existingObject(with:)
and we also need to implement the searchBarCancelButtonClicked() method to
method to get the Books object to be deleted. Notice that we also define
assign an empty predicate to clear the controller and list all the books
the data type for the UITableViewDiffableDataSource object as NSManagedObjectID.
available again when the Cancel button is pressed. The following is the
BooksViewController class we need to allow the user to search for books by
Do It Yourself: Update the ApplicationData structure with the code in
title.
Listing 15-46, the BooksViewController class with the code in Listing 15-
47, and the MyDataSource class with the code in Listing 15-48. Run the Listing 15-49: Searching for books by title
application. Everything works as before but now the books are 
provided by the Fetched Results Controller. import UIKit
import CoreData
Using these properties, we can get the name of each section from the
Fetched Results Controller and return it. The following is the method we
need to add to the MyDataSource class.
Listing 15-51: Implementing the protocol method to get the sections' titles

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) ->
String? {
if let sections = AppData.fetchedController.sections {
let sectionInfo = sections[section] 
return sectionInfo.name
}
return nil Do It Yourself: Update the prepareFetchedController() method in the
} class with the code in Listing 15-50. Add the
BooksViewController

method in Listing 15-51 to the MyDataSource class. Run the
application. You should see the books ordered by author, as shown To-Many Relationships
in Figure 15-23.

If we want to incorporate an index for the sections, as we did in the The previous examples assumed that there was only one author per book,
examples of Chapter 10, we must implement the sectionIndexTitles() method but sometimes multiple authors collaborate to write a book. To assign
in the data source and return the array we get from the sectionIndexTitles multiple Authors objects to a book, we must turn the author relationship of
property, as in the following example (the property returns an array with the Books entity into a To-Many relationship, as shown below.
the initial letters of the sections’ names capitalized).
Figure 15-24: Many-To-Many relationship
Listing 15-52: Generating an index

override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return AppData.fetchedController.sectionIndexTitles
}

}
} if selectedBook != nil {
} bookTitle.text = selectedBook.title
} bookYear.text = String(selectedBook.year)
@IBAction func editBooks(_ sender: UIBarButtonItem) {
let editing = !tableView.isEditing var authors: String!
tableView.setEditing(editing, animated: true) if let list = selectedBook.author as? Set<Authors> {
} let listNames = list.map({ $0.name ?? "Undefined" })
} if !listNames.isEmpty {
 authors = listNames.joined(separator: ", ")
}
selectedAuthors = Array(list)
In this example, we cast the value of the author property to a Set<Authors> }
value. This creates a Swift set with Authors objects representing all the authorName.text = authors ?? "Undefined"
}
authors assigned to the book, so we map the values into an array of strings }
and call the joined() method to create a single string with the names @IBAction func saveBook(_ sender: UIBarButtonItem) {
separated by comma. let title = bookTitle.text!.trimmingCharacters(in: .whitespaces)
let year = Int16(bookYear.text!)
Now that the cells can show multiple authors per book, is time to allow the if title != "" && year != nil {
user to assign them. The following are the changes we must introduce to Task(priority: .high) {
await storeBook(title: title, year: year!)
the EditBookViewController class to allow the user to select multiple authors }
per book. }
}
func storeBook(title: String, year: Int16) async {
Listing 15-54: Assigning multiple authors per book await context.perform {
 if self.selectedBook != nil {
self.selectedBook.title = title
import UIKit self.selectedBook.year = year
import CoreData self.selectedBook.author = NSSet(array: self.selectedAuthors)
} else {
class EditBookViewController: UIViewController { let newBook = Books(context: self.context)
@IBOutlet weak var bookTitle: UITextField! newBook.title = title
@IBOutlet weak var bookYear: UITextField! newBook.year = year
@IBOutlet weak var authorName: UILabel! newBook.author = NSSet(array: self.selectedAuthors)
var context: NSManagedObjectContext! }
var selectedAuthors: [Authors] = [] do {
var selectedBook: Books! try self.context.save()
} catch {
override func viewDidLoad() { print("Error: \(error)")
super.viewDidLoad() }
let app = UIApplication.shared }
let appDelegate = app.delegate as! AppDelegate await MainActor.run {
context = appDelegate.persistentContainer.viewContext if let book = self.selectedBook {
var currentSnapshot = AppData.dataSourceBooks.snapshot()
bookTitle.becomeFirstResponder() currentSnapshot.reloadItems([book.objectID])
AppData.dataSourceBooks.apply(currentSnapshot) To show the user the authors currently selected, we implement the prepare()
}
self.closeScene() method and send the value of the selectedAuthors property to the
} AuthorsViewController controller. The controller receives this value, and adds
}
or removes authors from the array according to the selections performed
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "selectAuthor" { by the user on the table, as shown below.
let controller = segue.destination as! AuthorsViewController
controller.selectedAuthors = selectedAuthors
} Listing 15-55: Selecting and deselecting authors
} 
func closeScene() {
navigationController?.popViewController(animated: true) import UIKit
} import CoreData
@IBAction func backAuthor(_ segue: UIStoryboardSegue) {
if segue.identifier == "backFromList" { class AuthorsViewController: UITableViewController {
let controller = segue.source as! AuthorsViewController var context: NSManagedObjectContext!
var selectedAuthors: [Authors]!
selectedAuthors = controller.selectedAuthors
} else if segue.identifier == "backFromNew" {
let controller = segue.source as! EditAuthorViewController override func viewDidLoad() {
super.viewDidLoad()
selectedAuthors.append(controller.selectedAuthor)
} let app = UIApplication.shared
var authors: String! let appDelegate = app.delegate as! AppDelegate
context = appDelegate.persistentContainer.viewContext
let listNames = selectedAuthors.map({ $0.name ?? "Undefined" })
if !listNames.isEmpty {
authors = listNames.joined(separator: ", ") tableView.register(UITableViewCell.self, forCellReuseIdentifier: "authorsCell")
prepareDataSource()
}
authorName.text = authors ?? "Undefined" }
} override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
 Task(priority: .high) {
await loadRequest()
The values are now stored in an array, so we always know which are the await MainActor.run {
prepareSnapshot()
authors selected by the user. If the user is editing a book, we read the }
author property of the selected book and follow the same procedure as }
before to turn the value into a Swift set and show the names of the authors }
func prepareDataSource() {
on the screen. Notice that at the end of the process, we assign the set of AppData.dataSourceAuthors = UITableViewDiffableDataSource<Sections,
Authors objects to the selectedAuthors property to initialize the array NSManagedObjectID>(tableView: tableView) { tableView, indexPath, itemID in
let cell = tableView.dequeueReusableCell(withIdentifier: "authorsCell", for: indexPath)
(selectedAuthors = Array(list)). When the user decides to save the book, we if let item = AppData.listOfAuthors.first(where: { $0.objectID == itemID }) {
perform the inverse procedure. The values in the selectedAuthors array are var config = cell.defaultContentConfiguration()
stored in an NSSet object and assigned to the author property. config.text = item.name ?? "Undefined"
cell.contentConfiguration = config
let selected = self.selectedAuthors.contains(where: { $0.name == item.name }) otherwise, we add it to the array. This makes sure that the array only
cell.accessoryType = selected ? .checkmark : .none
} contains the authors currently selected by the user.
return cell After the user selects or deselects an author, we perform the Unwind
}
}
Segue, get the array back from the EditBookViewController controller, and
func loadRequest() async { update the names on the screen (see the backAuthor() method in Listing 15-
await context.perform { 54). Now the user can select or deselect an author and add as many
let request: NSFetchRequest<Authors> = Authors.fetchRequest()
do { authors to a book as needed.
AppData.listOfAuthors = try self.context.fetch(request)
} catch {
print("Error: \(error)") Do It Yourself: Open the Core Data model. Select the Books entity
} and change the Type of the author relationship to To-Many (Figure
}
} 15-24). Update the BooksViewController class with the code in Listing
func prepareSnapshot() { 15-53, the EditBookViewController class with the code in Listing 15-54,
var snapshot = NSDiffableDataSourceSnapshot<Sections, NSManagedObjectID>()
snapshot.appendSections([.main]) and the AuthorsViewController class with the code in Listing 15-55.
snapshot.appendItems(AppData.listOfAuthors.map({ $0.objectID })) Assign the “selectAuthor" identifier to the segue that connects the
AppData.dataSourceAuthors.apply(snapshot, animatingDifferences: false)
} EditBookViewController scene to the AuthorsViewController scene. Run the
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { application. Press the Add Book button. Press the Select button to
if let itemID = AppData.dataSourceAuthors.itemIdentifier(for: indexPath) {
if let item = AppData.listOfAuthors.first(where: { $0.objectID == itemID }) { add an author. You should be able to add as many authors as you
if selectedAuthors.contains(where: { $0.name == item.name }) {
want.
if let index = selectedAuthors.firstIndex(of: item) {
selectedAuthors.remove(at: index)
} These types of relationships also change the way we search for values. For
} else {
selectedAuthors.append(item) instance, we cannot search for a book by author as we did before because
} now a book may be associated to many authors. In this case, we must tell
}
performSegue(withIdentifier: "backFromList", sender: self)
the predicate to search for the value inside the set of authors. For this
} purpose, predicates can include the following keywords.
}
}
 ANY—This keyword returns true when the condition is true for some
of the values in the set.
This is the view controller that displays all available authors. To show which
ALL—This keyword returns true when the condition is true for all the
one has been previously selected, we add a checkmark to the cell when
values in the set.
the author is already in the selectedAuthors array. Then, when a cell is
selected by the user, we check whether the author was previously selected NONE—This keyword returns true when the condition is false for all
or not. If it was selected, we remove it from the selectedAuthors array, the values in the set.
We have introduced predicate keywords earlier in this chapter. They are The example we have been working on so far turns the NSSet object
included in the format string to determine the way the predicate filters the returned by the author relationship into an array of Authors objects and
data. For our example, we can add the ANY keyword in front of the then adds or removes authors from this array, but if we need to add or
comparison to get the books with an author relationship that contains at remove values directly from the NSSet object, we must turn it into an
least one author with a specific name, as shown next. NSMutableSet object. This class creates a mutable set and therefore it allows
us to add or remove values from the object. To create an NSMutableSet
Listing 15-56: Fetching books by author object from an NSSet object, the NSManagedObject class includes the following
 method.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
do {
mutableSetValue(forKey: String)—This method reads the NSSet
try AppData.fetchedController.performFetch() object of the relationship indicated by the forKey attribute and
} catch {
print("Error")
returns an NSMutableSet with the values.
}
let request: NSFetchRequest<Books> = Books.fetchRequest() The NSMutableSet class includes the following methods to add and remove
request.predicate = NSPredicate(format: "ANY author.name == %@", "Stephen King")
if let list = try? context.fetch(request) { items in the set.
for book in list {
print(book.title!)
} add(Any)—This method adds the object specified by the argument
} to the set.
}
 remove(Any)—This method removes the object specified by the
argument from the set.
This example updates the viewWillAppear() method of the BooksViewController
class to include a request that finds all the books associated with an author The following example shows a possible implementation of these methods.
named "Stephen King". The predicate reads all the Authors objects in the We get the object representing the author with the name "Stephen King"
relationship and returns the book when one of the names matches the and then remove that author from every book.
string.
Listing 15-57: Systematically removing authors from books
Do It Yourself: Update the viewWillAppear() method in the 
class with the code in Listing 15-56. Run the
BooksViewController override func viewWillAppear(_ animated: Bool) {
application and insert a few books with the author Stephen King. You super.viewWillAppear(animated)
do {
should see the names of the books associated with that author try AppData.fetchedController.performFetch()
printed on the console. } catch {
print("Error")
}
let request: NSFetchRequest<Authors> = Authors.fetchRequest() CHAPTER 16 - NOTIFICATIONS
request.predicate = NSPredicate(format: "name == %@", "Stephen King")
if let list = try? context.fetch(request), list.count > 0 {
let author = list[0]
The Notification Center is like a bulletin board; we can post a notification The class includes the following properties to read the values of the
from anywhere in the code and then read it from other objects. The notification.
NotificationCenter class defines the following methods to post and read
notifications. name—This property returns the name of the notification.
object—This property returns a reference to the object that posted
post(name: Name, object: Any?, userInfo: Dictionary)—This
the notification.
method posts a notification to the Notification Center. The name
argument determines the name of the notification, the object
userInfo—This property returns the dictionary attached to the
notification.
argument is a reference to the object that sent the notification, and
the userInfo argument is a dictionary with the information we want to
The name of the notification is created from a structure included in the
pass to the observer.
Notification class called Name. The structure provides the following initializer
notifications(named: Name, object: AnyObject?)—This method to define custom names.
returns a Notifications object with an asynchronous sequence that
contains all the notifications posted to the Notification Center. The
notification's name. The argument is a string with the name we want let center = NotificationCenter.default
to assign to the notification. let name = Notification.Name("Update Data")
center.post(name: name, object: nil, userInfo: nil)
}
Notifications are used for multiple purposes. We can post a notification }
var AppData = ApplicationData()
after a long process is over to tell a view controller that it is time to update 
the interface, we can communicate view controllers with each other in a
large interface, or keep a view controller up to date posting notifications The ApplicationData structure created for this example contains only one
from the model. The following example implements a model that posts a property called names with an array of strings, but we have also included a
notification every time a new value is inserted by the user. For didactic method called addNewName() to assign new values to it. After adding the new
purposes, we are going to use a simple interface that includes just enough value to the names array, the method gets a reference to the NotificationCenter
scenes to show the number of values available and insert new ones. object assigned to our app and posts a notification we called "Update
Data" to report the change.
Figure 16-1: Interface to test notifications The Notification Center creates an asynchronous sequence with all the
notifications received (see Asynchronous Sequences in Chapter 14). The
sequence is managed by a Notifications object that we can get from the
notifications() method. To read this sequence, all we need to do is to create
an asynchronous for in loop with this object.
mutating func addNewName(newName: String) { for await _ in center.notifications(named: name, object: nil) {
let current = AppData.names array, and posts an "Update Data" notification. The Notification Center
await MainActor.run {
self.counter.text = String(current.count) stores the notification in the asynchronous sequence, the for in loop in the
} ViewController object detects that a new value is available in the sequence,
}
}
performs a cycle, and the current number of values stored in the names
} array is shown on the screen.

import Foundation The values from the dictionary are returned as values of type Any, so we
struct ApplicationData { must cast them to the right type. The example in Listing 16-5 reads the
var names: [String] = [] value of the "type" key, cast it as a String, and then compares it with the
string "John". If the values match, we print a message on the console.
mutating func addNewName(newName: String) {
names.append(newName)
Do It Yourself: Update the ApplicationData structure with the code in
let center = NotificationCenter.default
let name = Notification.Name("Update Data") Listing 16-4 and the readNotifications() method in the ViewController class
let info = ["type": newName]
with the code in Listing 16-5. Run the application. A message should
center.post(name: name, object: nil, userInfo: info)
} be printed on the console every time you insert the name John.
}
var AppData = ApplicationData()
 If we do not want to post any more notifications, we can stop the process
in the model (a simple if else statement will suffice), but if we want to stop
The code in Listing 16-4 declares a dictionary with the key "type" and the processing the notifications from a receiver, we must cancel the task. For
value inserted by the user and assigns it to the userInfo argument of the instance, if we have two or more view controllers that are processing the
post() method. Now, we can check this value from our ViewController class. notifications but we want only one of them to stop doing it, we can store
the task in a constant and called the cancel() method, as we did in Chapter
Listing 16-5: Reading the value in the notification 14 (see Listing 14-3).
 The following example cancels the task in our ViewController class after 10
func readNotifications() async { seconds, so the label is no longer updated with any of the values inserted
let center = NotificationCenter.default
by the user after the time expires.
let name = Notification.Name("Update Data")
for await notification in center.notifications(named: name, object: nil) { Listing 16-6: Cancelling the task
if let info = notification.userInfo {
let type = info["type"] as? String 
if type == "John" { override func viewDidLoad() {
print("Our name was inserted") super.viewDidLoad()
} let myTask = Task(priority: .background) {
} await readNotifications()
let current = AppData.names }
await MainActor.run { Timer.scheduledTimer(withTimeInterval: 10.0, repeats: false) { (timer) in
self.counter.text = String(current.count) myTask.cancel()
} }
} }
} 

System Notifications }


Do It Yourself: Create a new project. Add a Text View to the initial
Besides the notifications posted by our app, the system also posts scene. Update the ViewController class with the code in Listing 16-7.
notifications to the Notification Center all the time to report changes in the Run the application and type some text inside the Text View. You
interface or in other objects running the application. There are hundreds of should see a message on the console every time a character is added
these notifications available, including many defined in some of the classes or removed.
we already studied. For example, the UITextView class includes notifications
like textDidChangeNotification to report that the content of the Text View Another useful notification is called didChangeNotification, defined in a
changed. The following is a simple ViewController class we can use to test this structure called UIContentSizeCategory. This notification is posted every time
notification (the example assumes that we have an interface with a Text the user changes the size of the Dynamic Font types from the Settings app.
View). Every time the user changes the text, the Text View posts the We introduced Dynamic Font types in Chapter 5. They can be created from
textDidChangeNotification notification and the view controller responds by code with the preferredFont() method of the UIFont class (see Listing 5-14) or
printing a message on the console. selected from the Attributes Inspector panel. There are different types
available, such as Body, Headline, and more. When we select one of these
Listing 16-7: Responding to the Text View notification types, the system sets the size of the font to match the size selected by the
 user from the Settings app (Accessibility/Display & Text Size/Large Text).
import UIKit The problem is that the views that were already loaded are not
class ViewController: UIViewController { automatically updated. To make sure that all the important text in our
@IBOutlet weak var mainText: UITextView! interface is updated to the current size selected by the user, we must listen
override func viewDidLoad() {
to the didChangeNotification notification and perform the update ourselves.
super.viewDidLoad()
Task(priority: .background) {
Listing 16-8: Responding to font size changes in the Settings app
await receiveNotifications()
} 
} import UIKit
func receiveNotifications() async {
let center = NotificationCenter.default class ViewController: UIViewController {
let name = UITextView.textDidChangeNotification @IBOutlet weak var mainText: UITextView!
for await notification in center.notifications(named: name, object: nil) { override func viewDidLoad() {
if notification.name == name { super.viewDidLoad()
print("The Text View was modified") mainText.font = UIFont.preferredFont(forTextStyle: .body)
}
} Task(priority: .background) {
} await receiveNotifications()
Because we want to detect every rotation while the view is visible, we A different type of notification is the User Notification. These are
enable the accelerometer in the viewWillAppear() method. Every time the notifications that the system shows to the user when the app has an event
system detects a rotation, it posts a notification. When a notification is to report, such as the completion of a task or real-life events that the user
received, we get the current orientation from the orientation property of the wants to be reminded of. There are three different types of User
UIDevice object and print a message on the console. Notifications: alert, badge, and sound. A badge-type notification displays a
Notice that we also called the endGeneratingDeviceOrientationNotifications() badge with a number over the app's icon, a sound-type notification plays a
method in the viewDidDisappear() method. This is not necessary in our sound, and an alert-type notification may be displayed as a banner, an
application because we only have one scene, but in a more complex Alert View, or a message on the lock screen, depending on the current
interface it is good practice to tell the system that we no longer require state of the device and the configuration set by the user. They can be
updates on the state of the device. The system stops posting notifications scheduled all at once or independently. For instance, we can schedule a
notification that displays an alert and plays a sound, another that displays
and powers down the accelerometer if no other part of the application is
an alert and shows a badge, or another that just plays a sound.
using it.
IMPORTANT: User Notifications are divided into Local Notifications
Do It Yourself: To try this last example, remove the Text View from
and Remote Notifications (also known as Push Notifications). Local
the scene and update the ViewController class with the code in Listing
Notifications are notifications generated by the application running
16-9. Run the application and rotate the device. You should see the
on the device, while Remote Notifications are generated by remote
orientation printed on the console every time it changes.
servers and received by the system through the network. In this
chapter, we are going to study Local Notifications. For more
information on Remote Notifications, visit our website and follow
the links for this chapter.
current()—This type method returns a reference to the The framework includes the UNMutableNotificationContent class to store the
UNUserNotificationCenter object assigned to the app. content of a notification. The following are the properties included in this
class to set the notification's values.
From the UNUserNotificationCenter object, we can manage the notifications.
The first step is to request authorization from the user. The class includes title—This property sets or returns the notification's title.
the following methods for this purpose.
subtitle—This property sets or returns the notification's subtitle.
requestAuthorization(options: UNAuthorizationOptions)— body—This property sets or returns the notification's message.
This asynchronous method requests authorization from the user to badge—This property sets or returns a number to show over the
show notifications and returns a Boolean value to report the result. app's icon.
The options argument is a set of properties that determine the type sound—This property sets or returns the sound we want to play
of notifications we want to show. The properties available are badge, when the notification is delivered to the user. It is an object of type
sound, alert, carPlay, criticalAlert, provisional, and announcement. UNNotificationSound.
notificationSettings()—This asynchronous method returns a userInfo—This property sets or returns a dictionary with the
UNNotificationSettingsobject with the current settings. The most useful information we want to send with the notification.
property is authorizationStatus, which returns an enumeration value with
the authorization status (the user may change the status of the These properties define the information the notification is going to show to
authorization anytime from the Settings app). The possible values are the user. Some of these properties store strings, except for the badge
notDetermined, denied, authorized, provisional, and ephemeral. property which takes an NSNumber object, and the sound property which
takes an object of the UNNotificationSound class. This class includes the available for Local Notifications: Time Interval, (the notification is delivered
following initializer and property to get the object. after a certain period of time), Calendar (the notification is delivered on a
specific date), and Location (the notification is delivered in a specific
UNNotificationSound(named: UNNotificationSoundName)— location). The framework defines three classes to create these triggers:
This initializer creates a UNNotificationSound object with the sound UNTimeIntervalNotificationTrigger, UNCalendarNotificationTrigger, and
User Notifications are posted to the User Notification Center and then
UNNotificationRequest(identifier: String, content:
presented by the system when a certain condition is met. These conditions UNNotificationContent, trigger: UNNotificationTrigger?)—This
are established by objects called Triggers. There are three types of triggers initializer creates a request to deliver the notification specified by the
content argument and at the time or place specified by the trigger This example assumes that we have a button on the interface to send
argument. The identifier argument is a string that we can use later to notifications. We first get a reference to the UNUserNotificationCenter object
manage the request. assigned to the app, and then start a task to ask for authorization to show
alert banners and sound. The requestAuthorization() method is asynchronous,
As we already mentioned, before sending user notifications we must ask so we wait for the user to respond and then enabled or disable the button
the user for permission. Apple recommends doing it only when we really according to the value returned by the method (true if the user authorizes
need it. For instance, if our application contains a scene with a switch for the app or false otherwise).
the user to activate notifications, we should ask permission in this scene When the requestAuthorization() method is called, it creates an Alert View with
and not right after the app is launched. The following example illustrates a message and two buttons to let the user decide, as shown below.
how to do it.
Figure 16-2: Authorization to deliver notifications
Listing 16-10: Asking permission to send notifications

import UIKit
import UserNotifications
Task(priority: .high) {
do {
let authorized = try await center.requestAuthorization(options: [.alert, .sound])
await MainActor.run {
self.sendButton.isEnabled = authorized
}
} catch {
print("Error: \(error)")
}
}
}
}


If the user doesn't allow the app to show notifications, it can be done later let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 30, repeats: false)
from the Settings app, so the app should always test whether authorization let id = "reminder-\(UUID())"
was granted or not and warn the user in case an action is required. For this let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
purpose, the UNUserNotificationCenter class includes the notificationSettings() do {
method. We should always consult this method before sending try await center.add(request)
notifications to make sure that the app is still authorized to do it. For await MainActor.run {
self.sendButton.isEnabled = false
instance, we can add an Action for the button to call a method that checks }
the status and sends a notification every time it is pressed. } catch {
print("Error: \(error)")
}
Listing 16-11: Checking authorization status }
 
and 16-12. Download the alarm.mp3 file from our website and add it Media Attachments
to your project (you can drag and drop the file from Finder). The first

time you run the application the system will ask you to allow the app
to deliver notifications. Press the Allow button. The button on the Besides text and sounds, notifications can also include other types of
interface should be enabled. Press this button to send the media, such as images and videos. The UNMutableNotificationContent class
notification. Press the Home button to close the app. You should see includes the following property to attach additional media to the
the notification popping up on the screen after 30 seconds, as shown notification.
in Figure 16-3.
attachments—This property sets or returns an array of
UNNotificationAttachment objects with the media files we want to show
with the notification.
let id = "reminder-\(UUID())"
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
do {
try await center.add(request)
await MainActor.run {
self.sendButton.isEnabled = false
}
} catch {
print("Error: \(error)")
}
}
func getThumbnail(id: String) async -> URL? { 
let manager = FileManager.default
if let docURL = manager.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = docURL.appendingPathComponent("\(id).png") Do It Yourself: Update the sendNotification() method in the
if let image = UIImage(named: "husky") {
ViewControllerclass with the code in Listing 16-13 and add the
if let thumbnail = await image.byPreparingThumbnail(ofSize: CGSize(width: 100, height:
100)) { getThumbnail(id:)method below. Download the husky.png image from
if let imageData = thumbnail.pngData() {
if let _ = try? imageData.write(to: fileURL) {
our website and add it to the Assets Catalog. Run the application.
return fileURL Press the button to post a notification, and press the Home button to
}
} close the app. You should see a notification with the picture of the
} husky, as in Figure 16-4.
}
Provisional Notifications

Asking the user for permission can be a bit disruptive for some
applications. If we consider that due to the characteristics of our app the
user's acceptance to receive notifications may be implicit, we can post
provisional notifications. These are quiet notifications that only show in the
Notification Center (they are not displayed in the Locked or Home screens)
and include buttons for the user to decide whether to keep them or turn
them off. To get our app to post provisional notifications, all we need to do
is to add the provisional option to the requestAuthorization() method, as shown
next.
Task(priority: .background) {
do {
let authorized = try await center.requestAuthorization(options: [.alert, .sound, .provisional])
await MainActor.run {
self.sendButton.isEnabled = authorized
}
} catch {
print("Error: \(error)")
}
}
}

userNotificationCenter(UNUserNotificationCenter,
 didReceive: UNNotificationResponse,
withCompletionHandler: Block)—This method is called by the
Do It Yourself: Update the viewDidLoad() method with the code in User Notification Center when the user interacts with the notification
Listing 16-14 and the checkAuthorization() method with the code in
(performs an action). The didReceive argument is an object with
Listing 16-15. Uninstall the app and run it again from Xcode. Post a
information about the notification and the action performed, and the
notification. Go to the Home screen and drag your finger from the withCompletionHandler argument is a closure we must execute after
top to open the Notification Center. You should see the provisional the response is processed.
notification, as shown in Figure 16-5. When a notification is triggered and the app is being executed, the User
Notification Center calls the userNotificationCenter(UNUserNotificationCenter,
willPresent:, withCompletionHandler:) method on its delegate to ask the
application what to do. In this method, we can perform any task we want
and then execute the closure received by the method to specify what we await self.sendNotification()
}
want the system to do with the notification. If we do not want to show the }
notification, we must execute the closure with no parameters, otherwise }
func sendNotification() async {
we must provide a value that determines the type of notification we want
let content = UNMutableNotificationContent()
to show. content.title = "Reminder"
As always, all we need to do is to declare our view controller as the content.body = "This is the body of the message"
delegate and implement the protocol methods. In the following example, let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
we implement the userNotificationCenter(UNUserNotificationCenter, notification:,
let id = "reminder-\(UUID())"
completionHandler:) method to show the notification while the app is running.
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
do {
Listing 16-16: Showing notifications while the app is running try await center.add(request)
await MainActor.run {
 self.sendButton.isEnabled = false
import UIKit }
import UserNotifications } catch {
print("Error: \(error)")
class ViewController: UIViewController, UNUserNotificationCenterDelegate { }
var center: UNUserNotificationCenter! }
@IBOutlet weak var sendButton: UIButton! func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification:
UNNotification, withCompletionHandler completionHandler: @escaping
override func viewDidLoad() { (UNNotificationPresentationOptions) -> Void) {
center = UNUserNotificationCenter.current() completionHandler([.banner])
center.delegate = self }
}
sendButton.isEnabled = false 
Task(priority: .background) {
do {
Do It Yourself: Update the ViewController class with the code in
let authorized = try await center.requestAuthorization(options: [.alert]) Listing 16-16. For this example, we have removed the provisional
await MainActor.run {
self.sendButton.isEnabled = authorized
parameter added to the configuration in Listing 16-14, so the
} notifications are not provisional anymore. Remove the application
} catch {
print("Error: \(error)")
from the device and run it again. Press the button to post a
} notification. Wait for 10 seconds. You should see the notification on
}
} the screen while the app is running.
@IBAction func checkAuthorization(_ sender: UIButton) {
Task(priority: .background) {
let authorization = await center.notificationSettings()
let status = authorization.authorizationStatus
if status == .authorized {
Groups self.sendButton.isEnabled = authorized
}
 } catch {
print("Error: \(error)")
}
The system automatically groups notifications together by app. For }
}
instance, if our application sends multiple notifications to the Notification @IBAction func checkAuthorization(_ sender: UIButton) {
Center, they will all be grouped together and only the last one will be Task(priority: .background) {
let authorization = await center.notificationSettings()
shown to the user. This is the automatic behavior, but we can separate if authorization.authorizationStatus == .authorized {
them in custom groups using identifiers. The UNMutableNotificationContent class await self.sendNotification()
includes the following property for this purpose. }
}
}
threadIdentifier—This property sets or returns a string used to func sendNotification() async {
for group in listGroups {
identify each group of notifications. for message in listMessages {
let content = UNMutableNotificationContent()
content.title = "Reminder \(group)"
All the notifications with the same identifier will be grouped together. The content.body = message
following example separates notifications in two groups called Group One content.threadIdentifier = group
and Group Two. let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)


Do It Yourself: Update the ViewController class with the code in
Listing 16-17. Run the application. Press the button to post the To configure the summary, we must create a category and add that
notifications. Go to the Lock screen. After 10 seconds you should see category to the User Notification Center. Categories are objects that define
all the notifications organized in two groups (Figure 16-6). actions and behavior associated to a notification or a group of
notifications. The framework offers the UNNotificationCategory class to create
these objects. The following is the initializer used to configure a summary.
summary, and the options argument is an array of properties that func sendNotification() async {
let groupID = "Group One"
determine how the notifications associated to the category are going let summaryFormat = "\(listMessages.count) messages"
to be handled. The properties available are customDismissAction
let category = UNNotificationCategory(identifier: groupID, actions: [], intentIdentifiers: [],
(processes the dismiss action) and allowInCarPlay (allows car play to hiddenPreviewsBodyPlaceholder: nil, categorySummaryFormat: summaryFormat, options: [])
center.setNotificationCategories([category])
show notifications).
for message in listMessages {
The UNUserNotificationCenter class includes the following method to register a let content = UNMutableNotificationContent()
content.title = "Reminder"
category in the User Notification Center. content.body = message
content.threadIdentifier = groupID
setNotificationCategories(Set)—This method configures the User let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
Notification Center to work with the types of notifications and actions
let id = "reminder-\(UUID())"
we want to support. The argument is the set of categories we want to let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
associate to the center. do {
try await center.add(request)
} catch {
When the notifications appeared in a summary, they are sorted according print("Error: \(error)")
}
to their relevance. The system determines this relevance for us, but we can }
suggest a specific order by setting the notification's relevance ourselves. await MainActor.run {
self.sendButton.isEnabled = false
The UNMutableNotificationContent class includes the following property for this }
purpose. }

Below is how the summary created by our app looks like when it is shown Actions
to the user.

Figure 16-8: Notifications summary in the Lock screen
Notifications can show custom actions in the form of buttons and input
fields that the user can interact with to provide feedback without having to
open our app. The actions are defined by two classes: UNNotificationAction
and UNTextInputNotificationAction.
content.categoryIdentifier = groupID
let id = "reminder-\(UUID())"
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
do {
try await center.add(request)
await MainActor.run {
self.sendButton.isEnabled = false
}
} catch {
print("Error: \(error)")
}
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response:
UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
let identifier = response.actionIdentifier 
if identifier == "deleteButton" {
print("Delete Message")
} In the example of Listing 16-19, we implement a simple action that shows a
completionHandler()
}
button when the notification is expanded, but we can also include an
} action that shows an input field, so the user can provide feedback right
 from the screen where the notification is being displayed. The following
are the changes we need to introduce to add an action of this type.
This view controller creates a destructive action with the title Delete, and
includes it in a category called “listActions”. Categories have to be added to Listing 16-20: Processing an input action
the User Notification Center first with the setNotificationCategories() method 
and then assigned to the notification's categoryIdentifier property to configure
func sendNotification() async {
the notification, as we did in Listing 16-18. let groupID = "listActions"
Actions are displayed when the user drags down the notification. In this let actionDelete = UNNotificationAction(identifier: "deleteButton", title: "Delete", options:
.destructive)
case, the notification shows the Delete button. If the user presses this let actionInput = UNTextInputNotificationAction(identifier: "inputField", title: "Message",
button, the User Notification Center calls the delegate method to give our options: [])
application the chance to perform a task. In our example, we read the let category = UNNotificationCategory(identifier: groupID, actions: [actionDelete,
actionIdentifier property, compare it with the string "deleteButton" to confirm actionInput], intentIdentifiers: [], options: [])
that the user pressed the Delete button, and then print a message on the center.setNotificationCategories([category])
let id = "reminder-\(UUID())"
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
do {
try await center.add(request)
await MainActor.run {
self.sendButton.isEnabled = false
}
} catch {
print("Error: \(error)")
}
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response:
UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

let identifier = response.actionIdentifier
if identifier == "deleteButton" { Do It Yourself: Update your ViewController class with the code in
print("Delete Message")
} else if identifier == "inputField" { Listing 16-20 and run the application. Press the button to schedule a
print("Send: \((response as! UNTextInputNotificationResponse).userText)")
notification. Go to the Home screen and wait until the notification is
}
completionHandler() displayed. Expand the notification to see the actions. Click the
}

Message button, insert a value, and press the Send button. You
should see the same text printed on the console.
The text inserted by the user in the text field is sent to the delegate
method. The framework offers a special class to represent the response
called UNTextInputNotificationResponse. To access the value inserted by the user,
we must cast the response object to this class and then read its userText
property, as we did in Listing 16-20. The result is shown below.
KVO (Key/Value Observing) is a system we can use to perform a task when For the KVO system to be able to monitor the values of a property, the
the value of a property is modified. This is similar to what the didSet() object in which the property was defined must comply to the
method does for the properties of our own structures and classes (see NSKeyValueCoding protocol. This protocol defines another system called KVC
Property Observers in Chapter 3), with the difference that some predefined (Key/Value Coding) that allows us to access properties using their names as
classes provided by Apple come ready to work with this system and keys. Instead of using dot notation to access the values of a property, we
therefore we can monitor the values of their properties as we do with our call methods defined in this protocol with a string that identifies the
own from anywhere in the code. property we want to modify. The following are the most frequently used.
There are several situations in which KVO may be useful. For example, we
may have a property that reflects the status of an operation. The problem setValue(Any?, forKeyPath: String)—This method assigns a value
is that we do not know when the status is going to change, and which are to the property specified by the forKeyPath argument. The first
the objects that must be notified when a change occurs. Constantly argument is the value we want to assign to the property and the
checking the property from every object to see if its value has changed is
forKeyPath argument is a string with the name of the property.
not practical. KVO allows us to add observers to every object that needs to
keep an eye on the property. Every time the property is modified, the value(forKey: String)—This method retrieves the value of the
system notifies these observers, so the objects can perform the task they property identified by the forKey argument. The argument is a string
need, such as updating the interface or initiating a new operation. with the name of the property.
Some of the frameworks provided by Apple include classes that are ready
to work with KVC, but we can define our own. An easy way to make our
custom classes KVC compliant is to declare them as subclasses of the
NSObject class. This is the class from which the rest of the Objective-C
classes are created, and it already includes an implementation of the
protocol. The following example illustrates how to define a subclass of
NSObject called MyControl. Notice that because KVC is a system developed in
Objective-C the properties must be preceded by the @objc prefix.
Do It Yourself: Create a new project. Add a Stepper and a label to class MyControl: NSObject {
the scene. Connect the Stepper with an Action called updateValue() and @objc var round = false {
didSet {
the label with an Outlet called counterLabel. Create a Swift file called self.didChangeValue(forKey: "round")
MyControl for the code in Listing 16-21. Complete the ViewController }
willSet {
class with the code in Listing 16-23 and run the application. Press the self.willChangeValue(forKey: "round")
Stepper to change the value. The counterLabel label should turn red }
}
every time the value is multiple of 10. }

Setting the value of the property with the setValue() method prompts the
property to notify the change, but this means that every time we want the Now, we can assign new values to the round property as we always do and
changes in a property to be noticed by the observers we have to perform get the property to report the changes. The following example shows the
those changes calling the setValue() method. If we want every change to be new action for the Stepper.
These days, users own more than one device. If we create an application iCloud must be enabled for each application. The system requires
that works on multiple devices, we must provide a way for users to share entitlements to authorize our app to use the service and a container where
their data, otherwise they will have to insert the same information on our app’s data will be stored. Fortunately, Xcode can set up everything for
every device they own. But the only way to do it effectively is through a us by just selecting an option in the Signing & Capabilities panel, found
server. Data from one device is stored on a server so that it can be inside the app’s settings window. The panel includes a + button at the top-
retrieved later from other devices. Setting up a server to run this kind of left corner to add a new capability to the application (Figure 17-1, number
system is complicated and costly. To provide a standard solution, Apple 1). The button opens a view with all the capabilities available, as shown
created a free system called iCloud. iCloud allows applications to below. The capability is added by clicking on it and pressing Return.
synchronize data across devices using Apple servers. It provides three basic
services: Key-Value Storage, to store single values, Document Storage, to Figure 17-1: Activating iCloud for our app
store files, and CloudKit Storage, to store structured data.
After iCloud is added to the app, it is shown on the panel, below the
signing section. From here, we can select the services we want to activate
for our app and Xcode takes care of creating the entitlements. Figure 17-2,
below, shows the panel with the Key-Value storage service activated.
Testing Devices

The best way to test iCloud is by running the app in two different devices,
 but Apple has made iCloud services available in the simulator as well.
Thanks to this feature, we can synchronize data between a device and the
simulator to test our app.
Do It Yourself: Create a new project. Click on the app’s settings
For the devices and the simulators to be able to access iCloud services, we
option at the top of the Navigator Area (Figure 5-12, number 6) and
must register our iCloud account in the Settings app. We have to go to the
open the Signing & Capabilities panel. Click on the + button at the
Home screen, access the Settings app, tap on the option Sign in to your
top-left corner to add a capability (Figure 17-1, number 1). Select the iPhone/iPad (Figure 17-3, center), and sign in to Apple’s services with our
iCloud option, press return, and check the option Key-value storage. Apple ID. The process must be repeated for every device or simulator we
This iCloud service is now available in your application. want to use.

17.2 Key-Value Storage To store or access a value, we must initialize an NSUbiquitousKeyValueStore
object and then call its methods. The object takes care of establishing the

connection with iCloud and downloading or uploading the values. As we
already mentioned, the system is used to storing discrete values that
The Key-Value storage system is the User Defaults system for iCloud. It
represent the user’s preferences or the app’s state. For example, we may
works the same way, but all the data is stored on iCloud servers instead of
have a Stepper that lets the user set a limit on the number of items the
the device. We can use it to store the app’s preferences, states, or any
app can manage.
other value that we need to automatically set on each device owned by the
user. Foundation defines the NSUbiquitousKeyValueStore class to provide access
Figure 17-4: Interface to test Key-Value storage
to this system. The class includes the following methods to store and
retrieve values.
longLong(forKey: String)—This method retrieves a value of type Listing 17-1: Storing setting values in iCloud
Int64. 
string(forKey: String)—This method retrieves a value of type String. import UIKit
counter.text = String(control) Listing 17-1. Run the application. Press the Stepper’s button to
}
@IBAction func changeValue(_ sender: UIStepper) { change the value to 5. Wait a few seconds for the information to be
let current = stepper.value uploaded. Stop the application and run it again. The label should
counter.text = String(current)
kvStorage.set(current, forKey: "control") contain the value 5 this time. If you run the app on a different device
kvStorage.synchronize()
}
or simulator, you should see the value 5 again on the screen
} (remember to activate the same iCloud account in any of the

simulators or devices you try).
This example creates the NSUbiquitousKeyValueStore object as soon as the view
is loaded and reads a value of type Double identified with the key "control".
IMPORTANT: The simulator does not update the information
Like it happens with some of the methods included in the UserDefaults class, automatically. Most of the time, you must force the update. If you
the double() method used in this example returns 0 if the value is not found, modify the value on your device and do not see it changing on the
so the first time the application is executed, the value assigned to the label simulator, open the Features menu at the top of the screen and
will be 0. To update the value, we have created an Action for the Stepper select the option Trigger iCloud Sync. This will synchronize the
called changeValue(). In this method, we get the current value of the Stepper, application with iCloud and update the values right away.
assign it to the label to reflect the change on the screen, and then store the
value in iCloud with the "control" key using the set() method. If the user The previous example stores in iCloud a value associated with the "control"
opens the application in a different device, the process starts again by key, and every time the app is executed the value is retrieved from iCloud
reading the value associated with the "control" key from iCloud, but this servers and shown on the screen. The problem is that at this moment the
time instead of returning 0 it will find the last number stored by the set() application does not know when the value was modified from another
method. device. If we launch the app on an iPhone, set the value to 5, and then
Notice that we have executed a method called synchronize() after storing a launch it again on an iPad, we will see the value 5 on the iPad’s screen, but
new value. This is a method provided by the NSUbiquitousKeyValueStore class modifying the value thereafter will not produce any effect on the other
to force the system to send the new value to iCloud right away. It is not device. To report the changes, the NSUbiquitousKeyValueStore class defines the
always necessary, only when we want to make sure that the value is there following notification.
as soon as possible.
didChangeExternallyNotification—This notification is posted by
Do It Yourself: Create a new project. Add a Stepper and a label to the system when a change in the values of the Key-Value storage is
the scene, as illustrated in Figure 17-4. Connect the Stepper to the detected.
ViewController class with an Outlet called stepper and the label with an
Outlet called counter. Connect the Stepper with an Action called In the following example, we listen to this notification to update the value
changeValue() and complete the ViewController class with the code in every time is changed from another device.
The first thing we do in this view controller is to create a task in the
Listing 17-2: Updating the interface when a new value is received viewDidLoad() method to call an asynchronous method that listens to
 didChangeExternallyNotification notifications, so every device running the
import UIKit application knows when the value was modified from another device.
When a notification is received, we read the new value and update the
class ViewController: UIViewController {
@IBOutlet weak var stepper: UIStepper! interface. Now, we can have the application running simultaneously on
@IBOutlet weak var counter: UILabel! different devices and the user will see the value automatically changing on
var kvStorage: NSUbiquitousKeyValueStore!
the screen when it is modified from another device.
override func viewDidLoad() {
super.viewDidLoad()
kvStorage = NSUbiquitousKeyValueStore()
Do It Yourself: Update the ViewController class with the code in
let control = kvStorage.double(forKey: "control") Listing 17-2. Run the application simultaneously in two devices.
stepper.value = control
counter.text = String(control)
Modify the value on one device and check the other to see how the
label changes (it may take up to a few minutes for the data to be
Task(priority: .high) {
await receiveNotifications() transferred from one device to another). If you use the simulator,
} remember to select the option Trigger iCloud Sync from the Features
}
func receiveNotifications() async { menu.
let center = NotificationCenter.default
let name = NSUbiquitousKeyValueStore.didChangeExternallyNotification
for await notification in center.notifications(named: name, object: kvStorage) {
if notification.name == name {
await MainActor.run {
let control = kvStorage.double(forKey: "control")
stepper.value = control
counter.text = String(control)
}
}
}
}
@IBAction func changeValue(_ sender: UIStepper) {
let current = stepper.value
counter.text = String(current)
kvStorage.set(current, forKey: "control")
kvStorage.synchronize()
}
}

The Key-Value storage system was developed to store small values. The
purpose is to allow users to set preferences or configuration parameters
across devices. Although very useful, it presents some limitations,
especially on the amount of data we can store (currently no more than 1
megabyte). If what we need is to store large amounts of data, we can
activate the iCloud Documents option in the Capabilities panel and upload 
files instead.
The name must be unique, and the best way to guarantee this is to use the
Figure 17-5: iCloud Documents service app's bundle identifier. In Figure 17-6, we use the bundle identifier to
create the container for an app called TestiCloud. Once the container is
created, we should press the Refresh button to make sure the information
is uploaded to Apple servers right away (Figure 17-5, number 2). After this,
the container is added and selected as the active container for the
application.
An iCloud container is called ubiquitous container because its content is
shared with other devices and therefore available everywhere. The
FileManager class includes properties and methods to work with a ubiquitous
container. The following are the most frequently used.

url(forUbiquityContainerIdentifier: String?)—This method
The operative system uses a container to store iCloud files. This container is returns the URL of the app's iCloud container. The
a folder in the app's storage space where iCloud files are created. Once a forUbiquityContainerIdentifier argument is the name of the
file is added, modified, or removed from this container, the system container we want to access. The value nil returns the container which
automatically reports the changes to iCloud, so the copies of the app
name matches the Bundle identifier.
running in other devices can modify their own container to stay
synchronized. To create a container for our app, we must press the + evictUbiquitousItem(at: URL)—This method removes the local
button (Figure 17-5, number 1). This opens a window to insert the name. copy of the document at the URL specified by the at argument.
Metadata Query result(at: Int)—This method returns the NSMetadataItem object from
the query's results array at the index specified by the at argument.

start()—This method initiates the query.
Accessing the files is also complicated in iCloud. We cannot just get a list of stop()—This method stops the query.
files with methods like contentsOfDirectory() from the FileManager class because
enableUpdates()—This method enables query updates.
there could be some files that have not been downloaded yet to the
device. What we can do instead is to get the information pertaining to the disableUpdates()—This method disables query updates.
files. This data is called metadata, and refers to all the information
associated with a particular file, such as its name, the date it was created, The NSMetadataQuery class also includes some notifications to report when
etc. To get the files' metadata, Foundation defines the NSMetadataQuery class. new data is available. The following are the most frequently used.
This class provides the properties and methods necessary to retrieve the
information and watch for updates. NSMetadataQueryDidUpdate—This notification is posted when
the results of the query changed.
predicate—This property sets or returns the predicate for the query. NSMetadataQueryDidFinishGathering—This notification is
It is an optional of type NSPredicate. posted when the query finishes retrieving all the information.
sortDescriptors—This property sets or returns the sort descriptors
for the query. It is an array of NSSortDescriptor objects. The results of a query are returned by the results property in the form of an
array of NSMetadataItem objects. This is a simple class created to contain the
searchScopes—This property sets or returns a value that indicates
arguments of a file. The class provides the following method to retrieve the
the scope of the query. It is an array with constants that represent a
values.
predefined scope. The constants available for iOS are
NSMetadataQueryUbiquitousDocumentsScope (searches all files in the
value(forAttribute: String)—This method returns the value of the
Documents directory of the iCloud’s container) and file’s attribute determined by the forAtttribute argument. The
NSMetadataQueryUbiquitousDataScope (searches all the files that are not in
NSMetadataItem class defines a list of constants to represent the
the Documents directory of the iCloud’s container). attributes. The constants available are NSMetadataItemFSNameKey (file’s
results—This property returns an array with the query’s results. By name), NSMetadataItemDisplayNameKey (document’s name),
default, the array contains NSMetadataItem objects with the metadata of NSMetadataItemURLKey (file’s URL), NSMetadataItemPathKey (file’s path),
every file found. NSMetadataItemFSSizeKey (file’s size), NSMetadataItemFSCreationDateKey
resultCount—This property returns the number of results produced (date), and NSMetadataItemFSContentChangeDateKey (date the file was last
by the query. modified).
Single Document identifier on top of the iCloud section). If the name of the container
appears in red, press the Refresh button to upload the information

to Apple servers (Figure 17-5, number 2).
The interface for an application capable of processing documents must
include a way for the user to select the document and the tools to edit its The first step to set up the system is to create the UIDocument subclass that
content. For the following example, we will work with only one document is going to take care of our documents. As always, it is recommendable to
to keep it simple. The initial scene includes a button connected to a second create a separate file for the subclass. For this example, we call it
MyDocument.
scene containing a Text View to edit the document’s content.
Figure 17-7: Interface to edit a document Listing 17-3: Creating the document

import UIKit
close it, and the closeScene() method to remove the scene. In the Multiple Documents
storeDocument() method, we get the text from the Text View, assign it to the

fileContent property of the MyDocument object, and then call the save() method
to save it on file. The operation assigned to the method is forOverwriting
The initial scene of our previous example offers a single button to open a
because at this point, we already know that there is a file available, and we
document and display its content on the screen, however, most
just need to update its content. To make sure that the document is always
applications allow users to create and manage all the documents they
closed, we call the closeDocument() method after the document is saved and
need. An NSMetadataQuery object generates a live query. The query keeps
when the scene is remove (the user pressed the Back button).
looking for documents until we tell it to stop, and this is how we work with
multiple documents in iCloud. To illustrate how the process works, we will
Do It Yourself: Create a new file with a subclass of UIDocument called
replace the initial scene of our example with a Table View Controller, as
MyDocument and complete the class with the code in Listing 17-3.
shown next.
Create a subclass of UIViewController called EditionViewController and
assign it to the second scene. Connect the Text View with an Outlet Figure 17-8: Interface to work with multiple documents
called mycontent. Complete the class with the code in Listing 17-4 and
the methods in Listing 17-5. Connect the Save button with an action
called saveDocument() and complete the method and the class with the
code in Listing 17-6. Run the application. Press the Edit Document
button, insert some text in the Text View and press the Save button.
Run the app on a different device and press the Edit Document
button again. You should see the text inserted before on the screen.
If you use the simulator, remember to select the Trigger iCloud Sync

option from the Features menu to force the synchronization.
Do It Yourself: Delete the initial scene of the interface introduced
in Figure 17-7. Add a Table View Controller in its place. Connect the
Navigation Controller to the Table View Controller with a Root View
Controller segue. Connect the Table View Controller to the second
scene with a Show segue and give the segue the identifier
"showDocument". Add a bar button to the Table View Controller
with the title Add Document. Keep the design of the second scene
the same as before.
The Table View Controller must create a query to get the documents
available and then show their names on the table. We called this class Because we are not only getting the list of documents available but also
DocumentsViewController. The following code defines the properties we need monitoring iCloud for changes, we check two notifications: the
to manage the data and configure the query. NSMetadataQueryDidFinishGathering notification to know when the process of
gathering the initial data is over, and the NSMetadataQueryDidUpdate
Listing 17-7: Initiating the query notification to know when updates become available. In the view
 controller of Listing 17-7, we call a method to perform two tasks, one for
import UIKit each notification. When a notification is received, we call the updateList()
method to update the table with the current files.
class DocumentsViewController: UITableViewController {
var dataSource: UITableViewDiffableDataSource<Int, String>!
var documentsList: [String] = [] Listing 17-8: Keeping the table up to date
var metaData: NSMetadataQuery!

override func viewDidLoad() { func updateList(notification: NSNotification) {
super.viewDidLoad() metaData.disableUpdates()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "myCell") for item in metaData.results as! [NSMetadataItem] {
prepareDataSource() let name = item.value(forAttribute: NSMetadataItemFSNameKey) as! String
prepareSnapshot() if documentsList.firstIndex(of: name) == nil {
documentsList.append(name)
receiveMetadata() }
}
metaData = NSMetadataQuery() documentsList.sort(by: { (value1, value2) in value1 < value2 })
metaData.searchScopes = [NSMetadataQueryUbiquitousDocumentsScope] metaData.enableUpdates()
metaData.start()
} Task(priority: .high) {
func receiveMetadata() { await MainActor.run {
let center = NotificationCenter.default prepareSnapshot()
Task(priority: .high) { }
let name1 = NSNotification.Name.NSMetadataQueryDidFinishGathering }
for await notification in center.notifications(named: name1, object: nil) { }
updateList(notification: notification as NSNotification) 
}
}
Task(priority: .high) { Every time the query gets information, it posts notifications to let our code
let name2 = NSNotification.Name.NSMetadataQueryDidUpdate know what happened. To avoid conflicts with the data being processed and
for await notification in center.notifications(named: name2, object: nil) {
updateList(notification: notification as NSNotification)
the new data received, we must pause the query until the process is over.
} This is done by the disableUpdates() and enableUpdates() methods of the
}
NSMetadataQuery class. In the code of Listing 17-8, we call these methods at
}
} the beginning and the end to make sure that the updateList() method is not
 executed again until we get the new information and refresh the table.
Usually, an application like this gets the values it needs from the results Every time a cell is selected, we must send the file's name to the second
returned by the query and stores them in a place the rest of the view scene. For this purpose, we need to implement the tableView(UITableView,
controller can read, such as a property, a model, or a more complex didSelectRowAt:) method to perform the segue and the prepare() method to
storage system like Core Data. For this example, we defined a single send the value.
property called documentsList to contain an array with the names of the
documents. Because the updateList() method is called when the query Listing 17-10: Sending the name of the selected document to the second
finishes gathering the data and also when it finds changes, we need to scene
check for duplicates before introducing a new name into this array. The 
code in Listing 17-8 creates a loop to go through every new value, gets the override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
attribute for the name, and checks if it already exists in the array with the performSegue(withIdentifier: "showDocument", sender: self)
}
firstIndex(of:) method. Comparing the value returned by this method to nil, override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
we make sure that there are no duplicates on the list. After the list is if segue.identifier == "showDocument" {
let controller = segue.destination as! EditionViewController
updated, the snapshot is recreated, and the new information is shown on if let path = tableView.indexPathForSelectedRow {
the screen. if let item = dataSource.itemIdentifier(for: path) {
controller.selected = item
The methods to create the diffable data source and the snapshot don't }
require anything new, but for simplicity, we are going to use an integer to }
identify the sections (Integers conform to the Hashable protocol). }
}

Listing 17-9: Populating the table
 The DocumentsViewController class is not complete until we include the code
func prepareDataSource() { necessary for the user to create new documents. To simplify this example,
dataSource = UITableViewDiffableDataSource<Int, String>(tableView: tableView) { tableView,
we decided to create an Alert View with a Text Field to insert the name of
indexPath, item in
let cell = self.tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) the new document. The following example defines the Action for the Add
var config = cell.defaultContentConfiguration() Document button required to create the new document and store it in
config.text = item
cell.contentConfiguration = config iCloud.
return cell
}
}
Listing 17-11: Creating new documents
func prepareSnapshot() { 
var snapshot = NSDiffableDataSourceSnapshot<Int, String>()
@IBAction func addDocument(_ sender: UIBarButtonItem) {
snapshot.appendSections([0])
let alert = UIAlertController(title: "New File", message: nil, preferredStyle: .alert)
snapshot.appendItems(documentsList)
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
dataSource.apply(snapshot)
alert.addAction(cancel)
}
let action = UIAlertAction(title: "Create", style: .default, handler: { (action) in

if let fields = alert.textFields, let file = fields.first?.text {
if !file.isEmpty && self.documentsList.firstIndex(of: file) == nil {
Task(priority: .high) { In the EditionViewController class, we do not need to create a new document
await self.createNewDocument(fileName: file)
await MainActor.run { anymore, we just have to read the selected document, show it on the
self.prepareSnapshot() screen, and save the changes introduced by the user.
}
}
} Listing 17-12: Reading and modifying a document
} 
})
alert.addAction(action) import UIKit
alert.addTextField(configurationHandler: { (textField) in
textField.placeholder = "Insert name and extension" class EditionViewController: UIViewController {
}) @IBOutlet weak var mycontent: UITextView!
present(alert, animated: true, completion: nil) var document: MyDocument!
} var documentURL: URL!
func createNewDocument(fileName: String) async { var selected: String!
let manager = FileManager.default
if let fileURL = manager.url(forUbiquityContainerIdentifier: nil) { override func viewDidLoad() {
let documentURL = fileURL.appendingPathComponent("Documents/\(fileName)") super.viewDidLoad()
let document = MyDocument(fileURL: documentURL) Task(priority: .high) {
await document.save(to: documentURL, for: .forCreating) await openDocument()
} }
} }
 func openDocument() async {
let manager = FileManager.default
if let fileURL = manager.url(forUbiquityContainerIdentifier: nil) {
The code in Listing 17-11 creates an Alert View with a Text Field and a documentURL = fileURL.appendingPathComponent("Documents/\(selected!)")
button with the title Create. When this button is pressed, we get the text in document = MyDocument(fileURL: documentURL)
let success = await document.open()
the Text Field (the first in the array returned by the textFields property), if success {
confirm that the string is not empty, check if a file with that name already if let data = self.document.fileContent {
await MainActor.run {
exists, and if not, call an asynchronous method to create the new self.mycontent.text = String(data: data, encoding: .utf8)
document and save it. }
}
}
Do It Yourself: Create a subclass of UITableViewController called }
DocumentsViewControllerand assign it to the Table View Controller }
@IBAction func saveDocument(_ sender: UIBarButtonItem) {
added as the initial scene. Update the class with the code in Listing Task(priority: .high) {
await storeDocument()
17-7. Add the methods in Listings 17-8, 17-9, 17-10 and 17-11 to the await closeDocument()
class, and connect the Add Document button to the addDocument() await MainActor.run {
closeScene()
Action. }
}
}
When this scene is loaded, it opens the file with the name specified by the
selected property, reads its content, creates the string from the data, and
assigns it to the Text View to show the content on the screen. If the user
taps the Save button, the process is inverted; we convert the text into data
and save the file.
Container

Records are identified with an object that includes a name and a reference
to the zone the record belongs to (if a custom identifier is not specified,
the record is stored with an identifier generated by CloudKit). To create and
access the identifier and its values, the CKRecord class defines the ID class
with the following initializers and properties.

CKRecord.ID(recordName: String)—This initializer creates a
object to identify a record. The recordName argument is
CKRecord.ID
the name we want to give to the record.
CKRecord.ID(recordName: String, zoneID: CKRecordZone.ID)
—This initializer creates a CKRecord.ID object to identify a record
stored with the name and in the zone specified by the arguments. The
recordName argument is the name we want to give to the record, and
the zoneID argument is the identifier of the custom zone where we
want to store the record.
recordName—This property returns a string with the name of the as a generic CKRecordValue type that we must cast to the right data
record. type.
zoneID—This property returns a CKRecordZone.ID object with the
identifier of the zone to which the record belongs.
The CKRecord class offers properties to set or get the record's identifier and
other attributes. The following are the most frequently used.
Because the values of a record are stored as key/value pairs, we can use
square brackets to read and modify them (as we do with dictionaries), but
the class also includes the following methods.
As illustrated in Figure 17-12, the Public database can only store records in When we want to access data stored in CloudKit, we must download the
the default zone, but the Private and Shared databases can include custom records from the database and read their values. Records may be fetched
zones. In the case of the Private database, the custom zones are optional from a database one by one using their identifiers or in a batch using a
(although they are required for synchronization, as we will see later). Zones query. To define a query, the framework provides the CKQuery class. The
are like sections inside a database to separate records that are not related. class includes the following initializer and properties.
For example, we may have an app that stores locations, like the names of
cities and countries, but also allows the user to store a list of Christmas CKQuery(recordType: String, predicate: NSPredicate)—This
gifts. In cases like this, we can create a zone to store the records that initializer creates a CKQuery object to fetch multiple records from a
include information about cities and countries and another zone to store database. The recordType argument specifies the type of records we
the records that include information about the gifts. The CloudKit want to fetch, and the predicate argument determines the matching
framework provides the CKRecordZone class to represent the zones. The
criteria we want to use to select the records.
class includes an initializer to create new custom zones and a type method
to get a reference to the zone by default. recordType—This property sets or returns a string that determines
the type of records we want to fetch.
CKRecordZone(zoneName: String)—This initializer creates a predicate—This property sets or returns an NSPredicate object that
object to represent a zone with the name specified by
CKRecordZone defines the matching criteria for the query.
the zoneName argument. sortDescriptors—This property sets or returns an array of
default()—This type method returns the CKRecordZone object that objects that determine the order of the records
NSSortDescriptor
represents the zone by default in the database. returned by the query.
If we click on the CloudKit Database button, a panel is loaded to edit the modify others that the app may need later. For this purpose, the
database. The panel includes an option at the top to select the container dashboard provides access to the schema on the left-side bar.
(Figure 17-15, number 1), a bar on the left to edit the data and the schema
(the model), and a bar on the right to show and edit the values. Figure 17-16: Database schema


From the Record Types option, we can add, modify, or delete record types
The bar on the left includes a button to select from two configurations:
(Entities) and their values (Attributes). The option to add a new record type
Development or Production (Figure 17-15, number 2). The Development
is at the top of the panel (Figure 17-17, number 1), and the record types
option shows the configuration of the database we use during
already created are listed below (Figure 17-17, number 2).
development. This is the database we use while we are developing our
app. The Production option shows the configuration of the database that
Figure 17-17: Record types
we are going to deliver with our app (the one that is going to be available
to our users).
During development, we can store information in the database for testing.
Below the configuration option is the Data section (Figure 17-16, number
3) where we can edit the data store by our application, including records,
zones, and subscriptions. The panel also offer an option on the right to add 
records (Figure 17-15, number 4), and a list of values to edit.
As we already explained, the schema (the database model) is automatically
created by CloudKit servers when we save records from our app during
development. For instance, if our app creates a Books record, the server
creates a record type called Books and adds it to the database model. In
theory, this is enough to create the model, but in practice we always need
to erase record types or values that we don't use anymore and add or
Implementing CloudKit database, and define the diffable data sources. As always, we call it
ApplicationData.

Listing 17-13: Creating the model
To illustrate how to work with CloudKit databases, we are going to create a

simple application that allows the user to save a list of locations. The
import UIKit
interface presents two Table View Controllers inside a Navigation import CloudKit
Controller to insert and list the names of countries and cities.
enum Sections {
case main
Figure 17-18: Interface to work with CloudKit }
class ApplicationData {
var database: CKDatabase!
var selectedCountry: CKRecord.ID!
init() {
let container = CKContainer.default()
database = container.privateCloudDatabase
 }
}
Do It Yourself: Create a new project. Remove the initial scene and var AppData = ApplicationData()

add a Table View Controller. Embed the scene in a Navigation
Controller. Assign the Navigation Controller as the initial scene from Besides the properties to store the diffable data sources for the countries
the Attributes Inspector panel. Add a second Table View Controller. and cities and the arrays to store the records, we define two more
Connect the first scene to the second scene with a Show segue. Give properties: the database property to store a reference to the CloudKit's
the segue the identifier "showCities". Add a bar button of type Add database and the selectedCountry property to know which country was
to both scenes. Assign the title Countries to the first scene and Cities selected by the user from the initial scene. The database property is
to the second scene, as shown in Figure 17-18. initialized in the init() method with a reference to the Private Database (we
use the private database because we only want the user to be able to
Although this time we are downloading the information from the CloudKit share the data between his or her own devices).
In this example, we get a reference to the CloudKit container with the
servers, we still need a model to temporarily store the records, manage the
default() method, but if the name of the container is different from the app's
bundle, we need to implement the CKContainer initializer with the right
matchResults value, add them to the listCountries array, and update the class CitiesViewController: UITableViewController {
snapshot. override func viewDidLoad() {
super.viewDidLoad()
To let the user add a new country, we define an Action for the bar button tableView.register(UITableViewCell.self, forCellReuseIdentifier: "citiesCell")
called addCountry(). When the user presses the button, we create an Alert
Task(priority: .high) {
View with a Text Field inside. After the user inserts a name and presses the
await readCities()
Save button, we call the insertCountry() method in the model with this value }
to add the record to the database. This method defines a record identifier prepareDataSource()
}
with the string "idcountry" followed by a random value generated by the func readCities() async {
UUID() function (the identifiers must be unique). With this identifier, we if AppData.selectedCountry != nil {
let predicate = NSPredicate(format: "country = %@", AppData.selectedCountry)
create a CKRecord object of type Countries, add a property called name let query = CKQuery(recordType: "Cities", predicate: predicate)
with the value received by the method, and finally save it in CloudKit
servers with the save() method of the CKDatabase object. This method do {
let list = try await AppData.database.records(matching: query, inZoneWith: nil, desiredKeys: present(alert, animated: true, completion: nil)
nil, resultsLimit: 0) }
AppData.listCities = [] func insertCity(name: String) async {
for (_, result) in list.matchResults { let text = name.trimmingCharacters(in: .whitespaces)
if let record = try? result.get() { if !text.isEmpty {
AppData.listCities.append(record) let id = CKRecord.ID(recordName: "idcity-\(UUID())")
} let record = CKRecord(recordType: "Cities", recordID: id)
} record.setObject(text as NSString, forKey: "name")
await MainActor.run { let reference = CKRecord.Reference(recordID: AppData.selectedCountry, action:
prepareSnapshot() .deleteSelf)
} record.setObject(reference, forKey: "country")
} catch {
print("Error: \(error)") do {
} try await AppData.database.save(record)
} AppData.listCities.append(record)
}
func prepareDataSource() { await MainActor.run {
AppData.dataSourceCities = UITableViewDiffableDataSource<Sections, CKRecord.ID> prepareSnapshot()
(tableView: tableView) { tableView, indexPath, recordID in }
let cell = tableView.dequeueReusableCell(withIdentifier: "citiesCell", for: indexPath) } catch {
if let item = AppData.listCities.first(where: { $0.recordID == recordID }) { print("Error: \(error)")
var config = cell.defaultContentConfiguration() }
config.text = item["name"] as? String }
cell.contentConfiguration = config }
} }
return cell 
}
}
func prepareSnapshot() { This is also a long view controller, but it performs the same tasks as the
var snapshot = NSDiffableDataSourceSnapshot<Sections, CKRecord.ID>() view controller for the countries. The only difference is that instead of
snapshot.appendSections([.main])
snapshot.appendItems(AppData.listCities.map({ $0.recordID }))
getting all the cities stored in the database, we only get the cities that
AppData.dataSourceCities.apply(snapshot) belong to the country selected by the user. For this purpose, the predicate
} gets only the cities with a country key equal to the value of the
@IBAction func addCity(_ sender: UIBarButtonItem) {
let alert = UIAlertController(title: "Insert City", message: nil, preferredStyle: .alert) selectedCountry property.
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) The value of this property is also required when the user inserts a new city.
alert.addAction(cancel)
let action = UIAlertAction(title: "Save", style: .default, handler: { (action) in The record is created as before, but this time we create a Reference object
if let fields = alert.textFields, let name = fields.first?.text { with the country's identifier and an action of type deleteSelf, so when the
Task(priority: .high) {
record of the country is deleted, this record is deleted as well.
await self.insertCity(name: name)
}
} Do It Yourself: Create a Swift file called ApplicationData.swift for
})
alert.addAction(action) the code in Listing 17-13. Create a subclass of UITableViewController
alert.addTextField(configurationHandler: nil)
called CountriesViewController, assign it to the initial scene, and
complete it with the code in Listing 17-14. Connect the bar button of
this scene with the Action addCountry(). Create another subclass of
UITableViewController called CitiesViewController, assign it to the second
scene, and complete it with the code in Listing 17-15. Connect the
bar button of this scene with the Action addCity(). Run the application
and press the bar button to insert a country.

The application is ready. When the user inserts a new value, the code
creates a record and uploads it to CloudKit servers, but if we stop and start To add an index, we must press the Add Basic Index button at the bottom
the application again, the countries are not shown on the screen anymore. of the list. The panel opens a form to configure a new index.
This is because we haven't defined the required indexes. CloudKit
automatically creates indexes for every key we include in the records, Figure 17-20: Index configuration
except for the record’s identifier. Therefore, when we query the Cities
records by their country attribute to get the cities that belong to the
selected country, CloudKit knows how to find and return those records, but
when we try to retrieve the Countries records without a predicate,
CloudKit tries to fetch them by the record identifiers and fails because
there is no index associated to that attribute (called recordName). To

create an index for this attribute, we need to go to the dashboard, click on
the Indexes option in the Schema section, and click on the Countries record
There are three types of indexes: Queryable (it can be included in a query),
type to modify it.
Searchable (it can be searched), and Sortable (it can be sorted). By default,
When we click on a record, the panel shows the list of indexes available. By all these indexes are associated to custom attributes but not to the record's
default, the Countries records contains three indexes for the name identifier. When we query the database from the readCountries() method in
attribute, but no index for the record's identifier. the model, we do not specify any field in the predicate and therefore the
system fetches the records by their identifiers, which is described in the
Figure 17-19: Record's indexes database as recordID (recordName). For this reason, to retrieve the
countries in our example, we must add a Queryable index to the recordID
field of the Countries record type, as shown in figure 17-20.
Once we select the recordID field and the Queryable index, we can press
the Save button to save the changes. Now, if we run the application again
from Xcode, the records added to the database are shown on the table.
Assets

Records may include files, and the files may contain anything from pictures
to sound or even videos. To add a file to a record, we must create an asset
with the CKAsset class. The class includes the following initializer.
Assets are added to a record with a key, as any other value. For our
example, we are going to store a picture in the Cities records and add a
scene to show the picture when a city is selected.
To simplify the code, we moved the statements to an additional method receives a value of type CKRecordZone.ID with the identifier of the zone
called downloadUpdates(). So after we confirm that the zone was created, we that changed.
call this method with a reference to the closure received by the
changeTokenUpdatedBlock—This property sets a closure that is
checkUpdates() method. (We pass the closure from one method to another
executed to provide the last database token. The closure receives an
and execute it after all the operations are over, as we will see next.)
object of type CKServerChangeToken with the current token that we can
IMPORTANT: Passing closures from one method to another is a store to send to subsequent operations.
way to control the order in which the code is executed when we use fetchDatabaseChangesResultBlock—This property sets a closure
concurrent operations. We chose this programming pattern for this that is executed when the operation is over. The closure receives a
example because it simplifies the code, but as we mentioned before, Result enumeration to report the success or failure of the operation.
in some cases may be better to implement Swift concurrency. For The enumeration value includes a tuple and a CKError value to report
more information, see Chapter 14. errors. The tuple includes two values: a CKServerChangeToken object
with the last token and a Boolean value that indicates if there are
Before implementing the downloadUpdates() method and process the changes
more changes available.
in the database, we ought to study the operations provided by the CloudKit
framework for this purpose. The operation to fetch the list of changes
After the completion of this operation, we must perform another
available in the database is created from a subclass of the
operation to download the changes. For this purpose, the framework
CKDatabaseOperation class called CKFetchDatabaseChangesOperation. This class
includes the CKFetchRecordZoneChangesOperation class with the following
includes the following initializer.
initializer.
CKFetchDatabaseChangesOperation(previousServerChangeT
CKFetchRecordZoneChangesOperation(recordZoneIDs:
oken: CKServerChangeToken?)—This initializer creates an
[CKRecordZone.ID], configurationsByRecordZoneID:
operation to fetch changes from a database. The argument is a token
Dictionary)—This initializer creates an operation to download
that determines which changes were already fetched. If we specify a
changes from a database. The recordZoneIDs argument is an array
token, only the changes that occurred after the token was created are
with the IDs of all the zones that present changes, and the
fetched.
configurationsByRecordZoneID argument is a dictionary with
configuration values for each zone. The dictionary takes
The class also includes properties to define completion handlers for every
CKRecordZone.ID objects as keys and options determined by an object
step of the process.
of the ZoneConfiguration class included in the
CKFetchRecordZoneChangesOperation class. The class includes three
recordZoneWithIDChangedBlock—This property sets a closure
that is executed to report which zones present changes. The closure properties to define the options: desiredKeys (array of strings with the
keys we want to retrieve), previousServerChangeToken (CKServerChangeToken three values: a CKServerChangeToken object with the current token, a
object with the current token), and resultsLimit (integer that determines Data structure with the last token sent to the server, and a Boolean
the number of records to retrieve). value that indicates if there are more changes available.
fetchRecordZoneChangesResultBlock—This property sets a
The CKFetchRecordZoneChangesOperation class also includes properties to define closure that is executed after the operation is over. The closure
completion handlers for every step of the process.
receives a Result enumeration value to report errors.
recordWasChangedBlock—This property sets a closure that is CloudKit servers use tokens to know which changes were already sent to
executed when a new or updated record is downloaded. The closure every copy of the app, so the information is not downloaded twice from
receives two values: a CKRecord.ID with the identifier of the record the same device. If a device stores or modifies a record, the server
that changed, and a Result enumeration value to report the success or generates a new token, so next time a device accesses the servers only the
failure of the operation. The enumeration includes two values: a changes introduced after the last token will be downloaded, as shown in
CKRecord object with the record that changed and a CKError value to Figure 17-22.
report errors.
recordWithIDWasDeletedBlock—This property sets a closure that Figure 17-22: Tokens
is executed when the operation finds a deleted record. The closure
receives two values: a CKRecord.ID object with the identifier of the
record that was deleted, and a string with the record's type.
recordZoneChangeTokensUpdatedBlock—This property sets a
closure that is executed when the change token for the zone is
updated. The closure receives three values: a CKRecordZone.ID with the
identifier of the zone associated to the token, a CKServerChangeToken 
object with the current token, and a Data structure with the last token
sent by the app to the server. In the process depicted in Figure 17-22, the app in Device 1 stores a new
record in the server (Record 1). To report the changes, the server generates
recordZoneFetchResultBlock—This property sets a closure that is a new token (A). When the app in Device 2 connects to the server, the
executed when the operation finishes downloading the changes of a server detects that this device does not have the latest token, so it returns
zone. The closure receives two values: a CKRecordZone.ID with the Record 1 and the current token (A) to update the state in this device. If
zone's identifier, and a Result enumeration value to report the success later the user decides to create a new record from Device 2 (Record 2), a
or failure of the operation. The enumeration includes two values: a new token will be created (B). The next time Device 1 connects to the
tuple and a CKError value to report errors. In turn, the tuple includes
server, it will find that its token is different from the server's token, so it changeToken = token
}
will download the modifications inserted after token A. operation.fetchDatabaseChangesResultBlock = { result in
Tokens are great because they allow us to only get the latest changes, but guard let values = try? result.get() else {
finishClosure(UIBackgroundFetchResult.failed)
this process is not automatic, we are responsible of storing the current
return
tokens and preserve the state of our app. The server creates a token for }
the database and a token for each of the custom zones. For our example, if zonesIDs.isEmpty {
finishClosure(UIBackgroundFetchResult.noData)
we need two tokens: one to keep track of the changes in the database and } else {
another for the custom zone created by the configureDatabase() method. To changeToken = values.serverChangeToken
work with these values, we are going to use two variables changeToken for let configuration = CKFetchRecordZoneChangesOperation.ZoneConfiguration()
the database token and fetchChangeToken for the token of our custom zone, configuration.previousServerChangeToken = changeZoneToken
and we are going to store them permanently in User Settings for future let fetchOperation = CKFetchRecordZoneChangesOperation(recordZoneIDs: zonesIDs,
configurationsByRecordZoneID: [zonesIDs[0]: configuration])
reference. All this process is performed by the downloadUpdates() method, as
shown next. fetchOperation.recordWasChangedBlock = { recordID, result in
guard let record = try? result.get() else {
print("Error")
Listing 17-22: Getting the updates from the server return
}

if record.recordType == "Countries" {
func downloadUpdates(finishClosure: @escaping (UIBackgroundFetchResult) -> Void) { let index = self.listCountries.firstIndex(where: { item in
var changeToken: CKServerChangeToken! return item.recordID == recordID
var changeZoneToken: CKServerChangeToken! })
if index != nil {
let userSettings = UserDefaults.standard self.listCountries[index!] = record
if let data = userSettings.value(forKey: "changeToken") as? Data { } else {
if let token = try? NSKeyedUnarchiver.unarchivedObject(ofClass: CKServerChangeToken.self, self.listCountries.append(record)
from: data) { }
changeToken = token } else if record.recordType == "Cities" {
} if let country = record["country"] as? CKRecord.Reference {
} if country.recordID == self.selectedCountry {
if let data = userSettings.value(forKey: "changeZoneToken") as? Data { let index = self.listCities.firstIndex(where: { item in
if let token = try? NSKeyedUnarchiver.unarchivedObject(ofClass: CKServerChangeToken.self, return item.recordID == record.recordID
from: data) { })
changeZoneToken = token if index != nil {
} self.listCities[index!] = record
} } else {
var zonesIDs: [CKRecordZone.ID] = [] self.listCities.append(record)
let operation = CKFetchDatabaseChangesOperation(previousServerChangeToken: }
changeToken) }
operation.recordZoneWithIDChangedBlock = { zoneID in }
zonesIDs.append(zoneID) }
} }
operation.changeTokenUpdatedBlock = { token in fetchOperation.recordWithIDWasDeletedBlock = { recordID, recordType in
if recordType == "Countries" { await self.updateInterface()
let index = self.listCountries.firstIndex(where: { item in }
return item.recordID == recordID finishClosure(UIBackgroundFetchResult.newData)
}) }
if index != nil { self.database.add(fetchOperation)
self.listCountries.remove(at: index!) }
} }
} else if recordType == "Cities" { database.add(operation)
let index = self.listCities.firstIndex(where: { item in }
return item.recordID == recordID func updateInterface() async {
}) await MainActor.run {
if index != nil { var snapshotCountries = NSDiffableDataSourceSnapshot<Sections, CKRecord.ID>()
self.listCities.remove(at: index!) snapshotCountries.appendSections([.main])
} snapshotCountries.appendItems(listCountries.map({ $0.recordID }))
} dataSourceCountries?.apply(snapshotCountries)
}
fetchOperation.recordZoneChangeTokensUpdatedBlock = { zoneID, token, data in var snapshotCities = NSDiffableDataSourceSnapshot<Sections, CKRecord.ID>()
changeZoneToken = token snapshotCities.appendSections([.main])
} snapshotCities.appendItems(listCities.map({ $0.recordID }))
fetchOperation.recordZoneFetchResultBlock = { zoneID, result in dataSourceCities?.apply(snapshotCities)
guard let values = try? result.get() else { }
print("Error") }
return 
}
changeZoneToken = values.serverChangeToken
} This is a very long method that we need to study piece by piece. As
fetchOperation.fetchRecordZoneChangesResultBlock = { result in mentioned before, we start by defining the properties we are going to use
switch result {
case .failure(_):
to store the tokens (one for the database and another for the custom
finishClosure(UIBackgroundFetchResult.failed) zone). Next, we check if there are tokens already stored in the User
return Defaults database. Because the tokens are instances of the
default:
break CKServerChangeToken class, we can't store their values directly in User
} Defaults, we must first convert them into Data structures. This is the reason
if changeToken != nil {
if let data = try? NSKeyedArchiver.archivedData(withRootObject: changeToken!, why, when we read the values, we cast them as Data with the as? operator
requiringSecureCoding: false) { and then unarchive them with the unarchivedObject() method of the
userSettings.set(data, forKey: "changeToken")
NSKeyedUnarchiver class (see Chapter 15).
}
} Next, we configure the operations necessary to get the updates from the
if changeZoneToken != nil { server. We must perform two operations on the database, one to
if let data = try? NSKeyedArchiver.archivedData(withRootObject: changeZoneToken!,
requiringSecureCoding: false) { download the list of changes available and another to download the actual
userSettings.set(data, forKey: "changeZoneToken") changes and show them to the user. The operations are performed and
}
}
then the results are reported to the closures assigned to their properties.
Task(priority: .background) {
The first operation we need to perform is the CKFetchDatabaseChangesOperation dictionary with the zone identifiers as keys and ZoneConfiguration objects that
operation. The initializer requires the previous token to get only the include the previous token for each zone as values. Because in this example
changes that are not available on the device, so we pass the value of the we only work with one zone, we read the first element of the zonesIDs array
changeToken property. Next, we define the closures for each of its properties. to get the identifier of our custom zone and provide a ZoneConfiguration
This operation includes three properties, one to report the zones that object with the current token for the zone stored in the changeZoneToken
changed, one to report the creation of a new database token, and another property.
to report the conclusion of the operation. The first property defined in our This operation works like the previous one. The changes are fetched, and
example is recordZoneWithIDChangedBlock. The closure assigned to this the results are reported to the closures assigned to its properties. The first
property is executed every time the system finds a zone whose content has property declared in Listing 17-22 is recordWasChangedBlock. The closure
changed. In this closure, we add the zone ID to an array to keep a reference assigned to this property is called every time a new or updated record is
of each zone that changed. received. Here, we check if the record is of type Countries or Cities and
Something similar happens with the closure assigned next to the store it in the corresponding array. When the record is of type Countries,
changeTokenUpdatedBlock property. This closure is executed every time the we use the firstIndex(where:) method to look for duplicates. If the record
system decides to perform the operation again to download the changes in already exists in the array, we update its values, otherwise, we add the
separate processes. To make sure that we only receive the changes that we record to the list. We do something similar for the Cities records, except
did not process yet, we use this closure to update the changeToken property this time we first check whether the record contains a reference to a
with the current token. country and only update or add the record to the array if the reference
The last property we have defined for this operation is corresponds to the country currently selected by the user (the listCities array
fetchDatabaseChangesResultBlock. The closure assigned to this property is only contains the cities of the selected country).
executed to let the app know that the operation is over, and this is how we The closure of the recordWithIDWasDeletedBlock property defined next is
know that we have all the information we need to begin downloading the executed every time the app receives the ID of a deleted record (a record
changes with the second operation. This closure receives a Result that was deleted from the CloudKit database). In this case, we do the same
enumeration value, which includes a tuple with two values and an Error as before but instead of updating or adding the record we remove it from
value to report errors. If no values are returned, we execute the finishClosure the list with the remove() method.
closure with the value failed and the operation is over. On the other hand, if The closures of the next two properties, recordZoneChangeTokensUpdatedBlock
there are values available, we check if the zoneIDs array contains any zone and recordZoneFetchResultBlock, are executed when the process completes a
ID. If it is empty, it means that there are no changes available and therefore cycle, either because the system decides to download the data in multiple
we execute the finishClosure closure with the value noData, but if the array is processes, or the operation finished fetching the changes in a zone.
not empty, we store the last token in the changeToken variable and configure Depending on the characteristics of our application, we may need to
the CKFetchRecordZoneChangesOperation operation to download the changes. perform some tasks in these closures, but in our example, we just store the
The CKFetchRecordZoneChangesOperation operation is performed over the zones current token in the changeZoneToken property so the next time the operation
that changed, so we must initialize it with the array of zone identifiers is performed we only get the changes we have not downloaded yet.
generated by the previous operation. The initializer also requires a
Finally, the closure assigned to the fetchRecordZoneChangesResultBlock property } catch {
print("Error: \(error)")
is executed to report that the operation is over. The closure receives a Result }
value to report errors. If there is an error, we call the finishClosure closure }
}
with the value failed to tell the system that the operation failed, otherwise, 
we store the current tokens in the User Defaults database, call the
updateInterface() method to update the interface, and finally call the All we need to do to store a record in a custom zone is to create the
finishClosure closure with the value newData, to tell the system that new data CKRecordZone object and use the zone identifier to create the CKRecord.ID
has been downloaded. Notice that to store the tokens we must turn them object, as we did in this example.
into Data structures and encode them with the archivedData() method of the The following are the same changes applied to the insertCity() method of the
NSKeyedArchiver class (see Chapter 15). CitiesViewController class to store the Cities records in the custom zone.
Lastly, after the definition of each operation and their properties, we call
the add() method of the CKDatabase object to add them to the database. Listing 17-24: Storing the Cities records in a custom zone
There is one more change we must perform for the subscription to work. 
So far, we have stored the records in the zone by default, but as we already func insertCity(name: String) async {
await AppData.configureDatabase()
mentioned, subscriptions require the records to be stored in a custom
zone. The following are the changes we must introduce to the let text = name.trimmingCharacters(in: .whitespaces)
if !text.isEmpty {
insertCountry()method of the CountriesViewController class to store the records
let zone = CKRecordZone(zoneName: "listPlaces")
inside the listPlaces zone created before. let id = CKRecord.ID(recordName: "idcity-\(UUID())", zoneID: zone.zoneID)
let record = CKRecord(recordType: "Cities", recordID: id)
record.setObject(text as NSString, forKey: "name")
Listing 17-23: Storing the Countries records in a custom zone let reference = CKRecord.Reference(recordID: AppData.selectedCountry, action: .deleteSelf)
 record.setObject(reference, forKey: "country")
func insertCountry(name: String) async {
let bundle = Bundle.main
await AppData.configureDatabase()
if let fileURL = bundle.url(forResource: "Toronto", withExtension: "jpg") {
let asset = CKAsset(fileURL: fileURL)
let text = name.trimmingCharacters(in: .whitespaces)
record.setObject(asset, forKey: "picture")
if !text.isEmpty {
}
let zone = CKRecordZone(zoneName: "listPlaces")
do {
let id = CKRecord.ID(recordName: "idcountry-\(UUID())", zoneID: zone.zoneID)
try await AppData.database.save(record)
let record = CKRecord(recordType: "Countries", recordID: id)
AppData.listCities.append(record)
record.setObject(text as NSString, forKey: "name")
await MainActor.run {
do {
prepareSnapshot()
try await AppData.database.save(record)
}
AppData.listCountries.append(record)
} catch {
print("Error: \(error)")
await MainActor.run {
}
prepareSnapshot()
}
}
} Errors


Notice that the first thing we do in both methods is to call the
configureDatabase() method. We call this method again, so every time a record Errors are an important part of CloudKit. The service is highly dependable
is inserted, we check that the subscription and the zone were already on the network and how reliable it is. If the device is disconnected or the
added to the database. connection is not good enough, the operations are not performed or may
be lost. CloudKit does not provide a standard solution for these situations,
Do It Yourself: Update the AppDelegate class with the code in Listing it just returns an error and expects our app to solve the problem. If the
17-19. Add the methods in Listings 17-20, 17-21, and 17-22 to the user creates a new record but at that moment the device is disconnected
ApplicationData class. Update the insertCountry() method of the from the Internet, our app is responsible for registering the incident and
CountriesViewController class with the code in Listing 17-23 and the trying again later.
insertCity() method of the CitiesViewController class with the code in The most common error is related to the user's iCloud account. Every user
Listing 17-24. Run the application in two different devices and insert must have an iCloud account to access CloudKit servers. If an iCloud
account is not set on the device or has restrictions due to Parental Control
a new country. You should see the country appear on the screen of
or Device Management, the app will not be able to connect to the servers.
the second device.
The CKContainer class offers the following method to check the status of the
user's account.
IMPORTANT: Remote Notifications can only be tested on a real
device (they do not work on the simulator). If you only have one accountStatus()—This asynchronous method attempts to access the
device, you can test your applications by adding records from the user's iCloud account and returns a CKAccountStatus enumeration to
CloudKit dashboard.
report the current state. The enumeration includes the values
couldNotDetermine, available, restricted, and noAccount.
If the status of the iCloud account changes while the app is running, the
system posts a notification that we can use to perform updates and
synchronization tasks.
Listing 17-25: Checking CloudKit availability Do It Yourself: Update the insertCountry() method in the
 class with the code in Listing 17-25. Run the
CountriesViewController
func insertCountry(name: String) async { application in a device and activate Airplane Mode from Settings.
await AppData.configureDatabase()
do { Add a new country. You should see a CKError on the console that
let container = CKContainer.default() reads "Network Unavailable".
let status = try await container.accountStatus()
if status != CKAccountStatus.available {
print("iCloud Not Available") IMPORTANT: Of course, this example is just for didactic purposes.
return
} The information inserted by the user should be stored locally and
} catch { then uploaded to the servers when the connection becomes
print("Error: \(error)")
return available again. This requires storing the information locally,
} checking for errors, marking every value inserted by the user as
let text = name.trimmingCharacters(in: .whitespaces)
if !text.isEmpty { uploaded or not, and constantly trying to upload the values again
let zone = CKRecordZone(zoneName: "listPlaces")
let id = CKRecord.ID(recordName: "idcountry-\(UUID())", zoneID: zone.zoneID)
when the operation failed before, which may involve hundreds of
let record = CKRecord(recordType: "Countries", recordID: id) lines of code. Fortunately, Apple offers a better solution that
record.setObject(text as NSString, forKey: "name")
integrates CloudKit with Core Data and takes care of everything for
do { us. We will learn more about it in the next section of this chapter.
try await AppData.database.save(record)
AppData.listCountries.append(record)
In the last example, we just checked whether an error occurred or not and
await MainActor.run { proceeded accordingly, but we can also identify the type of error returned
prepareSnapshot()
} by the operation. Errors are objects that conform to the Error protocol.
} catch { Every time we want to read an error, we must cast it to the right type. In
print("Error: \(error)")
} CloudKit, the errors are of type CKError, which is a structure initialized from
} an object of type NSError and therefore it inherits the following property to
}

return the error code.
code—This property returns a value that identifies the error found. message "Not Found" on the console (there is no zone called
The property is of type CKError.Code; an enumeration inside the "myNewZone").
CKError structure with values that represent all the errors produced by
CloudKit. The list of values available is extensive. The most frequently
used are partialFailure, networkUnavailable, networkFailure, serviceUnavailable,
unknownItem, operationCancelled, changeTokenExpired, quotaExceeded,
zoneNotFound, and limitExceeded.
Persistent Store and the context. The Core Data framework defines entity to the other (the relationship in the Countries entity must be set to
global variables to set standard policies. The NSErrorMergePolicy variable To-Many because a country can have many cities).
returns an error if the objects are different, the There is one more requirement for the model to be ready to work with
NSMergeByPropertyStoreTrumpMergePolicy variable replaces the changes in CloudKit. We must select the Configuration (Figure 17-24, number 1) and
memory by the external changes, the check the option Used with CloudKit in the Data Model Inspector panel
(Figure 17-24, number 2), so all the objects stored for these entities are
NSMergeByPropertyObjectTrumpMergePolicy replaces the external changes
synchronized with CloudKit.
by the changes in memory, the NSOverwriteMergePolicy variable replaces
the values in the Persistent Store by the current changes, and the
Figure 17-24: Used with CloudKit option
NSRollbackMergePolicy uses the version of the objects in the Persistent
Store.
CloudKit. After this, the objects stored for those Entities will not be synchronize the Persistent Store with CloudKit servers. Another difference
uploaded to CloudKit. with previous examples is that now we must tell the context that we need
to merge the changes and how to do it. For this purpose, we assign the
value true to the automaticallyMergesChangesFromParent property and the value
Once the application is configured to work with CloudKit and the Core Data
NSMergeByPropertyObjectTrumpMergePolicy to the mergePolicy property.
model is ready, we can work on our code. First, we must initialize the Core
And that's all it takes. From now on, all the changes introduced in the
Data stack in the AppDelegate class, as we did in Chapter 15.
Persistent Store are going to be uploaded to CloudKit and every device
running the application is going to be automatically synchronized. For this
Listing 17-27: Preparing Core Data to work with CloudKit
example, we are going to implement the same interface as before, with

two Table View Controllers to list the countries and cities, as shown below.
import UIKit
import CoreData
Figure 17-25: Interface to work with Core Data and CloudKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let container = NSPersistentCloudKitContainer(name: "AnotherTest")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true

container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return container In the model, we only need properties to store the diffable data sources
}()
func application(_ application: UIApplication, configurationForConnecting and the Fetched Results Controllers for both Table View Controllers.
connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) ->
UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole:
Listing 17-28: Defining the model to work with CloudKit
connectingSceneSession.role) 
}
import UIKit
}
import CoreData

import CloudKit
AppData.dataSourceCountries = UITableViewDiffableDataSource<Sections,
enum Sections { NSManagedObjectID>(tableView: tableView) { tableView, indexPath, countryID in
case main let cell = tableView.dequeueReusableCell(withIdentifier: "countriesCell", for: indexPath)
} if let country = try? self.context.existingObject(with: countryID) as? Countries {
class ApplicationData { var config = cell.defaultContentConfiguration()
var selectedCountry: Countries! config.text = country.name
cell.contentConfiguration = config
var dataSourceCountries: UITableViewDiffableDataSource<Sections, NSManagedObjectID>! }
var fetchedControllerCountries: NSFetchedResultsController<Countries>! return cell
}
var dataSourceCities: UITableViewDiffableDataSource<Sections, NSManagedObjectID>! }
var fetchedControllerCities: NSFetchedResultsController<Cities>! func prepareFetchedController() {
} let request: NSFetchRequest<Countries> = Countries.fetchRequest()
var AppData = ApplicationData() let sort = NSSortDescriptor(key: "name", ascending: true, selector:
 #selector(NSString.caseInsensitiveCompare(_:)))
request.sortDescriptors = [sort]
AppData.fetchedControllerCountries = NSFetchedResultsController(fetchRequest: request,
The Table View Controllers are the same as before. All we need to do is to managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
define the Fetched Results Controller and add or remove objects from the AppData.fetchedControllerCountries.delegate = self
try? AppData.fetchedControllerCountries.performFetch()
Core Data Persistent Store. The system takes care of uploading the changes }
to CloudKit servers and updating the Persistent Store with the changes func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
introduced from other devices. The following is the view controller to list let newsnapshot = snapshot as NSDiffableDataSourceSnapshot<Sections, NSManagedObjectID>
and add countries. AppData.dataSourceCountries.apply(newsnapshot)
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
Listing 17-29: Listing the countries stored in the Persistent Store performSegue(withIdentifier: "showCities", sender: self)
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
import UIKit if segue.identifier == "showCities" {
import CoreData if let path = tableView.indexPathForSelectedRow {
if let countryID = AppData.dataSourceCountries.itemIdentifier(for: path) {
class CountriesViewController: UITableViewController, NSFetchedResultsControllerDelegate { if let country = try? context.existingObject(with: countryID) as? Countries {
var context: NSManagedObjectContext! AppData.selectedCountry = country
}
override func viewDidLoad() { }
super.viewDidLoad() }
let app = UIApplication.shared }
let appDelegate = app.delegate as! AppDelegate }
context = appDelegate.persistentContainer.viewContext @IBAction func addCountry(_ sender: UIBarButtonItem) {
let alert = UIAlertController(title: "Insert Country", message: nil, preferredStyle: .alert)
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "countriesCell") let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
prepareDataSource() alert.addAction(cancel)
prepareFetchedController() let action = UIAlertAction(title: "Save", style: .default, handler: { (action) in
} if let fields = alert.textFields, let text = fields.first?.text{
func prepareDataSource() { let name = text.trimmingCharacters(in: .whitespaces)
if !name.isEmpty {
let newCountry = Countries(context: self.context) var context: NSManagedObjectContext!
newCountry.name = name
try? self.context.save() override func viewDidLoad() {
super.viewDidLoad()
do { let app = UIApplication.shared
try AppData.fetchedControllerCountries.performFetch() let appDelegate = app.delegate as! AppDelegate
} catch { context = appDelegate.persistentContainer.viewContext
print("Error") tableView.register(UITableViewCell.self, forCellReuseIdentifier: "citiesCell")
} prepareDataSource()
} prepareFetchedController()
} }
}) func prepareDataSource() {
alert.addAction(action) AppData.dataSourceCities = UITableViewDiffableDataSource<Sections, NSManagedObjectID>
alert.addTextField(configurationHandler: nil) (tableView: tableView) {tableView, indexPath, cityID in
present(alert, animated: true, completion: nil) let cell = tableView.dequeueReusableCell(withIdentifier: "citiesCell", for: indexPath)
} if let city = try? self.context.existingObject(with: cityID) as? Cities {
} var config = cell.defaultContentConfiguration()
 config.text = city.name
cell.contentConfiguration = config
}
The code is the same implemented to work with Core Data before. We set return cell
up the Fetched Results Controller to get the Countries objects, and the }
diffable data source to feed the table with this data. To allow the user to }
func prepareFetchedController() {
add new countries, we create an Alert View with a Text Field and a Save let request: NSFetchRequest<Cities> = Cities.fetchRequest()
button. When the user inserts a name and taps the Save button, the request.predicate = NSPredicate(format: "country = %@", AppData.selectedCountry)
let sort = NSSortDescriptor(key: "name", ascending: true, selector:
closure for the button creates a new Countries object and saves the context. #selector(NSString.caseInsensitiveCompare(_:)))
The system updates the Persistent Store with this information, but because request.sortDescriptors = [sort]
AppData.fetchedControllerCities = NSFetchedResultsController(fetchRequest: request,
we use an NSPersistentCloudKitContainer object to manage it, the Countries managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
object is automatically turned into a CKRecord and uploaded to CloudKit AppData.fetchedControllerCities.delegate = self
try? AppData.fetchedControllerCities.performFetch()
servers. When we open the application on a second device, the process is }
inverted; the CKRecord object is downloaded from CloudKit, turned into a func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
Countries object, and added to the Persistent Store. didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) {
let newsnapshot = snapshot as NSDiffableDataSourceSnapshot<Sections, NSManagedObjectID>
The view controller for the second scene is similar, but this time we are AppData.dataSourceCities.apply(newsnapshot)
processing Cities objects instead. }
@IBAction func addCity(_ sender: UIBarButtonItem) {
let alert = UIAlertController(title: "Insert City", message: nil, preferredStyle: .alert)
Listing 17-30: Listing the cities of a country let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alert.addAction(cancel)
 let action = UIAlertAction(title: "Save", style: .default, handler: { (action) in
import UIKit if let fields = alert.textFields, let text = fields.first?.text{
import CoreData let name = text.trimmingCharacters(in: .whitespaces)
if !name.isEmpty {
class CitiesViewController: UITableViewController, NSFetchedResultsControllerDelegate { let newCity = Cities(context: self.context)
newCity.name = name Connect the buttons on each scene with Actions called addCountry()
newCity.country = AppData.selectedCountry
try? self.context.save() and addCity() respectively. Update the view controllers with the codes
do { in Listings 17-29 and 17-30. Run the application in two devices. Press
try AppData.fetchedControllerCities.performFetch() the Add Country button and insert a new country. After a few
} catch {
print("Error") seconds, you should see the same value appear on the second
}
} device. Repeat the process for the cities.
}
})
alert.addAction(action)
Because we are using a Fetched Results Controller to manage the data for
alert.addTextField(configurationHandler: nil) the Table Views, the Countries and Cities objects added to the Persistent
present(alert, animated: true, completion: nil)
Store and their relationships are automatically updated and shown to the
}
} user. No matter where the new values are added from, locally or remotely,
 they always appear on the screen in every device that is running our
application. But when we are not presenting the values with a Fetched
The view controller gets the Countries object representing the country Results Controller, we must take care of updating the interface ourselves.
selected by the user from the selectedCountry property and creates a For this purpose, we must configure the Core Data stack to publish a
predicate to only retrieve the cities that belong to that country. To create notification when something changes in the Persistent Store, and perform
the relationship, we also assign this value to the country property of the new the necessary updates every time that notification is received.
Cities object when the user inserts a new city. The system uploads this
The first thing we need to control this process is a reference to the object
object to CloudKit servers and creates the connection between the records in charge of creating and loading the Persistent Store. This is an object
for us. defined by the NSPersistentStoreDescription class. The NSPersistentContainer class
includes the following property to get it.
Do It Yourself: Update the AppDelegate class with the code in Listing
17-27 (remember to replace the name in the persistentStoreDescriptions—This property returns an array
NSPersistentCloudKitContainer initializer with the name of your Core Data containing the NSPersistentStoreDescription objects in charge of every
model). Replace the initial scene with two Table View Controllers Persistent Store managed by the Core Data stack.
embedded in a Navigation Controller (Figure 17-25). Connect the
first scene to the second scene with a Show segue and identify the Once we get the NSPersistentStoreDescription object that represents the
segue with the name "showCities". Add bar buttons of type Add to Persistent Store, we must set an option in the object to get it to post
both scenes (Figure 17-25). Create a Swift file called notifications every time the content of the Persistent Store changes. The
ApplicationData.swift for the model in Listing 17-28. Create a class includes the following method for this purpose.
UITableViewController subclass call CountriesViewController for the first
scene and another called CitiesViewController for the second scene.
setOption(Value, forKey: String)—This method sets an option in if let description = container.persistentStoreDescriptions.first {
description.setOption(true as NSNumber, forKey:
the NSPersistentStoreDescription object for the key specified by the forKey NSPersistentStoreRemoteChangeNotificationPostOptionKey)
argument and with a value determined by the first argument. }
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
The Core Data framework defines a global variable called fatalError("Unresolved error \(error), \(error.userInfo)")
}
NSPersistentStoreRemoteChangeNotificationPostOptionKey with the key required to })
get the NSPersistentStoreDescription object to post notifications every time it container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
detects changes in the Persistent Store. return container
The notification posted by the NSPersistentStoreDescription object is called }()
NSPersistentStoreRemoteChange, but this notification is sent by an object that func application(_ application: UIApplication, configurationForConnecting
connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) ->
coordinates the information between the Persistent Store and the context. UISceneConfiguration {
This object is created from the NSPersistentStoreCoordinator class. The return UISceneConfiguration(name: "Default Configuration", sessionRole:
connectingSceneSession.role)
NSManagedObjectContext class includes the following property to get a
}
reference to this object. }

These days, personal devices are primarily used for processing pictures,
videos, and sound, and Apple devices are no exception. UIKit can display an
image with an UIImageView view, but other frameworks are required to
process the image, present a video on the screen, or play sounds. In this
chapter, we introduce the tools provided by Apple for this purpose.
imagePickerController(UIImagePickerController, If we move the mouse over an item, the editor shows a + button to add
didFinishPickingMediaWithInfo: Dictionary)—This method is another item to the item’s hierarchy. For example, to add a new option to
called on the delegate when the user finishes taking the image or the main list, we click on the + button of the root item called "Information
recording the video. The second argument contains a dictionary with Property List" (circled in Figure 18-1). The editor now shows a list of
the information about the media. The values in the dictionary are possible options to choose from. The option to request authorization to
identified with properties of the InfoKey structure included in the use the camera is called "Privacy - Camera Usage Description". After the
UIImagePickerController class. The properties available are cropRect, option is selected, we must insert the text we want to show to the user as
editedImage, imageURL, livePhoto, mediaMetadata, mediaType, mediaURL, and
the option's value, as illustrated below.
originalImage.
Figure 18-2: Camera Usage Description option
imagePickerControllerDidCancel(UIImagePickerController)—
This method is called on the delegate when the user cancels the

process.
Once the configuration is ready, we can begin working on our app. Because
These are all the tools we need to take a picture or record a video and
the Image Picker Controller opens a new scene, the interface usually
process it. But before accessing the camera, we must ask the user for
includes an action to open it. For this example, we have decided to keep it
authorization. The process is automatic, but it requires adding an option in
simple and include just an Image View to show the image taken by the
the info.plist file that tells the user what the application is going to do with
user, and a button called Take Picture to open the scene generated by the
the camera.
controller.
We have introduced the info.plist file before. This is a file included in every
Xcode template that contains configuration values for the application. The
Figure 18-3: Interface to work with the camera
values are identified with keys and each key may contain other keys, in a
hierarchical structure. When we select this file from the Navigator Area,
Xcode shows an editor with the current values and a button at the top to
add more.
if sourceAvailable {
mediaPicker.sourceType = .camera
mediaPicker.mediaTypes = ["public.image"]
mediaPicker.allowsEditing = false
mediaPicker.cameraCaptureMode = .photo
present(mediaPicker, animated: true, completion: nil)
} else {
print("The media is not available")
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo
info: [UIImagePickerController.InfoKey : Any]) {
let picture = info[.originalImage] as! UIImage
pictureView.image = picture
dismiss(animated: true, completion: nil)
}
}


When the button is pressed, the takePicture() method creates an instance of
The view controller that acts as the delegate of the UIImagePickerController the UIImagePickerController class and assigns the ViewController object as its
controller is required to conform to two protocols: delegate. Next, it checks if the camera is available and presents the
UINavigationControllerDelegate and UIImagePickerControllerDelegate. The following controller in case of success, or shows a message on the console
example includes an Action for the Take Picture button that creates, otherwise.
configures, and presents a UIImagePickerController controller to take pictures Before calling the present() method to show the scene on the screen, the
from the camera. The class also implements the method defined in the code configures the controller. The sourceType property is assigned the value
UIImagePickerControllerDelegate protocol to process the media. camera to tell the controller that we want to get a picture from the camera,
the mediaTypes property is assigned the value public.image to set images as
Listing 18-1: Taking a picture the only media we want to retrieve, the allowEditing property is set as false to
 not let the user edit the image, and the cameraCaptureMode property is
import UIKit assigned the value photo to only allow the user to take pictures. The scene
class ViewController: UIViewController, UINavigationControllerDelegate,
created by the controller is shown below (center).
UIImagePickerControllerDelegate {
Figure 18-4: Camera’s interface
The camera’s interface includes buttons to control the camera and take the
picture. After a picture is taken, a new set of buttons is shown to allow the
user to select the picture or take another one. If the user decides to use
the current picture, the controller calls the imagePickerController() method on
its delegate to report the action. This method receives a value called info
that we can read to get the media returned by the controller and process it
(store it in a file, Core Data, or show it on the screen). In our example, we
read the value of the originalImage key to get a UIImage object that represents
the picture taken by the user and assign this object to the Image View to
show it on the screen (Figure 18-4, right).
Storing Pictures and for videos the parameters are (video: String,
contextInfo: UnsafeRawPointer)
didFinishSavingWithError error: NSError?, contextInfo: UnsafeRawPointer).
In the

following example, we show the picture on the screen, store it in the Photo
Library, and open an Alert View to inform the user that the picture was
In the previous example, we show the picture on the screen, but we can
stored on the device.
store it in a file or in a Core Data Persistent Store. An alternative,
sometimes useful when working with the camera, is to store the picture in
Listing 18-2: Storing pictures in the Photo Library
the device’s Photo Library so that it is accessible to other applications. The

UIKit framework offers two functions to store images and videos.
import UIKit
selection—This property sets or returns a value that determines the operations, but it is also implemented by the PHPickerViewController class to
type of selection to perform when multiple selection is allowed. It is return the images or videos selected by the user. The NSItemProvider class
an enumeration called Selection with the values ordered and default. includes the following methods for this purpose.
preselectedAssetIdentifiers—This property sets or returns an
canLoadObject(ofClass: Class)—This method returns a Boolean
array with strings that identify the selected items. It is used to show
value that indicates whether the resource contained by the
the picker with preselected items.
NSItemProvider object can be converted to an object of the class
specified by the ofClass argument.
Like the UIImagePickerController class, the PHPickerViewController class calls a
method on its delegate to provide information about the media selected loadObject(ofClass: Class, completionHandler: Closure)—This
by the user. The class includes the delegate property to set this delegate and method asynchronously loads the resource contained by the
the framework includes the PHPickerViewControllerDelegate protocol, which NSItemProvider object, cast the object to the class specified by the
defines the following method. ofClass argument, and calls a closure with the result. The closure
receives two values: an object that conforms to the
picker(PHPickerViewController, didFinishPicking: NSItemProviderReading protocol with the actual data, and a second value
[PHPickerResult])—This method is called on the delegate after the to report errors.
user selects an image or a video from the picker. The didFinishPicking
argument is an array of PHPickerResult structures representing each The PHPickerViewController class generates a modal scene to allow the user to
asset selected by the user. select an image or a video from the Photo Library, so we also have to
present the scene with the present() method, as we did for the
The resources selected by the user are returned as PHPickerResult structures. UIImagePickerController controller. But first, we must define a
This is a wrapper with information about the asset and an NSItemProvider PHPickerConfiguration object to configure the picker, as shown in the following
object with the actual resource. The PHPickerResult structure includes two example.
properties to return these values.
Listing 18-3: Selecting a picture from the Photo Library
itemProvider—This property returns the NSItemProvider object with 
the images or videos selected by the user. import UIKit
import PhotosUI
assetIdentifier—This property returns a string with the name
assigned to the resource. class ViewController: UIViewController, PHPickerViewControllerDelegate {
@IBOutlet weak var pictureView: UIImageView!
The NSItemProvider class was design to transmit resources, such as data or @IBAction func takePicture(_ sender: UIButton) {
var configuration = PHPickerConfiguration(photoLibrary: .shared())
files, between services. It is used during drag and drop or copy and paste configuration.filter = .images
configuration.selectionLimit = 1 an image), and call the loadObject(ofClass:) method to load the image. The
let picker = PHPickerViewController(configuration: configuration) result is shown below.
picker.delegate = self
present(picker, animated: true)
Figure 18-5: Photo Library’s interface (center)
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
if let itemProvider = results.first?.itemProvider {
if itemProvider.canLoadObject(ofClass: UIImage.self) {
itemProvider.loadObject(ofClass: UIImage.self) { item, error in
if let image = item as? UIImage {
Task(priority: .background) {
await MainActor.run {
self.pictureView.image = image
}
}
}
}
}
}
dismiss(animated: true, completion: nil) 
}
}
 By default, the PHPickerViewController is presented in a full screen sheet, but
we can take advantage of the bigger screens offered by iPads to show the
In this example, we import the PhotosUI framework to be able to present a scene with a different format, such as a popover. All we need to do is to
PHPickerViewController controller and get our view controller to conform to configure the presentation style of the view controller and set the required
the PHPickerViewControllerDelegate protocol to process the response. The code parameters. The following example shows the scene in a popover.
assumes that we are using the same interface introduced in Figure 18-3.
When the user presses the Take Picture button, we define the Listing 18-4: Showing the picker in a popover
PHPickerConfiguration class to configure the picker to show images and allow 
the user to select only one. Next, the PHPickerViewController object is created, import UIKit
import PhotosUI
and the picker is presented on the screen.
When the user selects a resource, the PHPickerViewController object calls the class ViewController: UIViewController, PHPickerViewControllerDelegate {
protocol method on its delegate. The method receives an array of @IBOutlet weak var pictureView: UIImageView!
@IBOutlet weak var pictureButton: UIButton!
PHPickerResult structures with all the resources selected by the user, but
because our picker was configured to only allow the user to select one @IBAction func takePicture(_ sender: UIButton) {
var configuration = PHPickerConfiguration(photoLibrary: .shared())
picture, we read the first value to get the NSItemProvider object that contains configuration.filter = .images
this asset. Next, we check that the asset can be converted into a UIImage configuration.selectionLimit = 1
object with the canLoadObject() method (making sure that the user selected
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self press the Take Picture button. The Photo Library should open in a
picker.modalPresentationStyle = .popover popover.
if let popover = picker.popoverPresentationController {
popover.sourceView = pictureButton
popover.sourceRect = pictureButton.bounds In these examples, we have set the selectionLimit property to 1, so the user
popover.permittedArrowDirections = [.up] could only select one picture at a time, but the PHPickerViewController allows
}
present(picker, animated: true)
multiple selection. All we need to do is to assign the value 0 to the
} selectionLimit property and the feature is enabled. But if we want to allow
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { the user to modify the selection later, we must store the selected pictures
if let itemProvider = results.first?.itemProvider {
if itemProvider.canLoadObject(ofClass: UIImage.self) { in a model and configure the picker with these values so that the user can
itemProvider.loadObject(ofClass: UIImage.self) { item, error in see which pictures were selected before and make changes. There are
if let image = item as? UIImage {
Task(priority: .background) { multiple ways to achieve this. For our example, we are going to use a
await MainActor.run { simple model, like those implemented before, with a custom class to store
self.pictureView.image = image
}
all the information.
}
} Listing 18-5: Defining the model to keep track of the selection
}
} 
} import UIKit
dismiss(animated: true, completion: nil)
} enum Sections {
} case main
 }
class ItemsData: Identifiable {
let id: String!
In this example, we anchor the popover to the Take Picture button, so the let image: UIImage!
scene is shown below the button, close to where the user’s finger is. If we let title: String!
open the scene on a device with a small screen, the system shows it full
init(_ id: String, _ image: UIImage, _ title: String) {
screen, but if we do it on an iPad, the system creates a popover self.id = id
presentation controller and assigns it to the popoverPresentationController self.image = image
self.title = title
property. If this property is not nil, we set the values for the presentation }
and the scene is presented as a popover (see Action Sheets in Chapter 13, }
Listing 13-6). class ApplicationData {
var listPictures: [ItemsData] = []
var dataSource: UITableViewDiffableDataSource<Sections, ItemsData.ID>!
Do It Yourself: Connect the Take Picture button to the ViewController }
var AppData = ApplicationData()
class with an Outlet called pictureButton. Update the ViewController class 
with the code in Listing 18-4. Run the application on an iPad and
The ItemsData class includes three properties: the id property to store the import PhotosUI
picture's identifier, a UIImage object with the image, and a string with the class PicturesViewController: UITableViewController, PHPickerViewControllerDelegate {
name suggested by the picker for the picture. As always, we store these override func viewDidLoad() {
super.viewDidLoad()
values in an array and define the diffable data source with the id property.
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "myCell")
To display the list of pictures selected by the user, we need a small prepareDataSource()
interface with a Table View Controller embedded in a Navigation Controller prepareSnapshot()
}
and a button in the Navigation Bar to open the picker. func prepareDataSource() {
AppData.dataSource = UITableViewDiffableDataSource<Sections, ItemsData.ID>(tableView:
tableView) { tableView, indexPath, itemID in
Figure 18-6: Interface to test multiple selection let cell = self.tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath)
if let item = AppData.listPictures.first(where: { $0.id == itemID }) {
var config = cell.defaultContentConfiguration()
config.text = item.title
config.image = item.image
config.imageProperties.maximumSize = CGSize(width: 100, height: 80)
config.imageProperties.cornerRadius = 10
config.imageProperties.reservedLayoutSize = CGSize(width: 100, height: 80)
cell.contentConfiguration = config
}
return cell
}
}
func prepareSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
snapshot.appendSections([.main])
snapshot.appendItems(AppData.listPictures.map({ $0.id }))
AppData.dataSource.apply(snapshot)
}
@IBAction func takePicture(_ sender: UIBarButtonItem) {
 var configuration = PHPickerConfiguration(photoLibrary: .shared())
configuration.filter = .images
configuration.selectionLimit = 0
The purpose of the application is to show the list of pictures selected by configuration.preselectedAssetIdentifiers = AppData.listPictures.map { $0.id }
the user on the table and allow the user to open the picker and change the
selection at any time. The pictures are stored in an array in the model, and let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
each picture is identified by a unique identifier assigned by the picker. The present(picker, animated: true)
following is a possible implementation for the view controller. }
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
AppData.listPictures = AppData.listPictures.filter { value in
Listing 18-6: Displaying the pictures selected by the user if results.contains(where: { $0.assetIdentifier == value.id }) {
return true

} else {
import UIKit return false
} The code begins by defining the diffable data source and the snapshot,
}
prepareSnapshot() with only a minor change from previous examples. This is because the
images retrieved from the Photo Library may be of different sizes, so we
for result in results {
if !AppData.listPictures.contains(where: { $0.id == result.assetIdentifier }) {
set the reservedLayoutSize property to configure the size of the column and
loadItem(item: result) make sure the pictures and the text are aligned.
} The picker is configured as before, but now we assign the value 0 to the
}
dismiss(animated: true, completion: nil) selectionLimit property to allow multiple selection and an array of identifiers
} to the preselectedAssetIdentifiers property so that the pictures previously
func loadItem(item: PHPickerResult) {
let provider = item.itemProvider
selected by the user are already selected in the picker.
let fileName = provider.suggestedName ?? "Undefined" In the delegate method, we perform two operations. First, we filter the
let assetID = item.assetIdentifier ?? "" values in the model. This is to remove from the model the pictures that
if provider.canLoadObject(ofClass: UIImage.self) { were deselected by the user, so if the user selects a picture and then opens
provider.loadObject(ofClass: UIImage.self) { item, error in the picker again and deselects it, the picture is removed from the model
if let image = item as? UIImage {
if let thumbnail = self.getThumbnail(image: image) { and not shown on the table anymore. After the listPictures array is filtered,
let newItem = ItemsData(assetID, thumbnail, fileName) we update the snapshot and then proceed to load the new items selected
AppData.listPictures.append(newItem)
Task(priority: .high) {
by the user. To simplify the code, we moved the loading process to another
await MainActor.run { method called loadItem(). In this method, we get the name and the identifier
self.prepareSnapshot() assigned to the picture from the suggestedName and assetIdentifier properties,
}
} then check if the image can be extracted from the asset as we did in
} previous examples, and finally create the new ItemsData object and add it to
}
}
the model. Notice that so save resources, we do not store the original
} image in the model, we create a thumbnail with the preparingThumbnail()
}
method and store this image instead (see Images in Chapter 5).
func getThumbnail(image: UIImage) -> UIImage? {
let imageWidth = image.size.width
let imageHeight = image.size.height Figure 18-7: Multiple selection
let scaleFactor = (imageWidth > imageHeight) ? 100 / imageWidth : 80 / imageHeight
if let thumbnail = image.preparingThumbnail(of: CGSize(width: imageWidth * scaleFactor,
height: imageHeight * scaleFactor)) {
return thumbnail
} else {
return nil
}
}
}

Custom Camera

the media specified by the argument. The for argument is a structure and AVCaptureAudioDataOutput to get the audio, but the most frequently used
of type AVMediaType with properties to define the type of media. The is the AVCapturePhotoOutput to capture a single video frame (take a picture).
properties available to work with the cameras and microphones are This class works with a delegate that conforms to the
video and audio. AVCapturePhotoCaptureDelegate protocol, which among other methods defines
the following to return a still image.
requestAccess(for: AVMediaType)—This asynchronous type
method asks the user for permission to access the device. The for
photoOutput(AVCapturePhotoOutput,
argument is a structure of type AVMediaType with properties to define
didFinishProcessingPhoto: AVCapturePhoto, error: Error?)—
the type of media. The properties available to work with the cameras This method is called on the delegate after the image is captured. The
and microphones are video and audio. didFinishProcessingPhoto argument is a container with information
authorizationStatus(for: AVMediaType)—This type method about the image, and the error argument is used to report errors.
returns a value that determines the status of the authorization to use
the device. The for argument is a structure of type AVMediaType with To control the flow of data from input to output, the framework defines
properties to define the type of media. The properties available to the AVCaptureSession class. From an instance of this class, we can control the
work with the cameras and microphones are video and audio. The inputs and outputs and determine when the process begins and ends by
method returns an enumeration of type AVAuthorizationStatus with the calling the following methods.
values notDetermined, restricted, denied, and authorized.
addInput(AVCaptureInput)—This method adds an input to the
An instance of the AVCaptureDevice class represents a capture device. To capture session. The argument represents the input device we want
define this device as an input device, we must create an object that to add.
controls the ports and connections. The framework defines the addOutput(AVCaptureOutput)—This method adds an output to
AVCaptureDeviceInput class for this purpose. The class includes the following the capture session. The argument represents the output we want to
initializer to create the input object for the device. generate from the capture session.

The input, output, and preview layers are connected to the capture session
by objects of the AVCaptureConnection class. The class manages the
information of the connection, including ports and data. The following are
Do It Yourself: Create a new project. Embed the initial scene in a
some of the properties provided by this class. Navigation Controller. Add an Image View and a bar button to the
initial scene (Figure 18-9, center). Add a new scene to the
videoOrientation—This property sets or returns the orientation of Storyboard. Connect the bar button to the second scene with a Show
the video. It is an enumeration of type AVCaptureVideoOrientation with segue. Add an empty view to the second scene with a gray
the values portrait, portraitUpsideDown, landscapeRight, and landscapeLeft. background and a Height constraint of 60 points (the option in the
Library to add empty views is called View). Click on the background
isVideoOrientationSupported—This property returns a Boolean
color of this view and select the option Custom to open the color
value that determines whether it is possible to set the video
picker. Set an Opacity value of 50% to make it translucent. Pin this
orientation or not.
view to the bottom of the main view and add a button at the center
Because the scene is not generated by a controller anymore, we must (Figure 18-9, right). Select an SF Symbol for the button from the
create our own. Figure 18-9, below, introduces the interface we have Attributes Inspector panel. Add another empty view for the preview
created for this example. The initial scene was embedded in a Navigation layer and pin it to the edges of the main view to adopt the size of the
Controller and an additional scene was included to manage the camera. screen (highlighted in Figure 18-9, right). Move this view to the back
The initial scene includes an Image View to show the picture taken by the from the Document Outline panel or from the Editor menu to
user, and a bar button to open the second scene. The second scene has an position it behind the toolbar.
empty view to which we will add the preview layer, and a narrow view at
The view controller for the initial scene only needs to get the picture from var previewLayer: AVCaptureVideoPreviewLayer!
var picture: UIImage!
the second scene and show it on the screen. For this example, we have var imageOrientation: UIImage.Orientation!
decided to create an Action that we are going to connect to an Unwind
override func viewDidLoad() {
Segue to get the image from the second scene and assign it to the Image super.viewDidLoad()
View (the image will be stored in a property called picture). let device = UIDevice.current
device.beginGeneratingDeviceOrientationNotifications()
Listing 18-7: Displaying the picture on the screen let status = AVCaptureDevice.authorizationStatus(for: .video)
 if status == .authorized {
prepareCamera()
import UIKit } else if status == .notDetermined {
Task(priority: .high) {
class ViewController: UIViewController {
await askAuthorization()
@IBOutlet weak var pictureView: UIImageView! }
} else {
@IBAction func goBack(_ segue: UIStoryboardSegue) {
notAuthorized()
let controller = segue.source as! CameraViewController }
pictureView.image = controller.picture }
}
func askAuthorization() async {
} let granted = await AVCaptureDevice.requestAccess(for: .video)
 await MainActor.run {
if granted {
For the second scene, we have created a subclass of UIViewController called self.prepareCamera()
} else {
CameraViewController. In this view controller, we must follow a series of steps self.notAuthorized()
to activate the camera, let the user take a picture, and process the image. }
}
But before even accessing the camera we must ask the user for permission. }
This is done automatically when we use a UIImagePickerController controller, }
but in a custom controller we must do it ourselves with the type methods 
layer. But setting the position and size of the layer does not determine how 
its content is going to be shown. The video coming from the camera could func getCurrentOrientation() -> AVCaptureVideoOrientation {
have a different size and orientation. How the video is going to adjust to var currentOrientation: AVCaptureVideoOrientation!
let deviceOrientation = UIDevice.current.orientation
the size of the layer is determined by the value of the layer’s videoGravity
property, but the orientation is set from the connection between the switch deviceOrientation {
case .landscapeLeft:
capture session and the preview layer. This is the reason why, after setting currentOrientation = AVCaptureVideoOrientation.landscapeRight
the value of the videoGravity property and the layer’s frame, we get a imageOrientation = .up
case .landscapeRight:
reference to the connection from the layer’s connection property. By
currentOrientation = AVCaptureVideoOrientation.landscapeLeft
modifying the videoOrientation property of the connection, we can adjust the imageOrientation = .down
orientation according to the device’s orientation and finally add the case .portrait:
currentOrientation = AVCaptureVideoOrientation.portrait
sublayer to the view’s layer with the addSublayer() method. When the imageOrientation = .right
sublayer is ready, the capture session can be initiated with the startRunning() case .portraitUpsideDown:
currentOrientation = AVCaptureVideoOrientation.portraitUpsideDown
method. imageOrientation = .left
default:
if UIDevice.current.orientation.isLandscape {
IMPORTANT: When a view draws its content, the graphics are currentOrientation = AVCaptureVideoOrientation.landscapeRight
stored in a layer that can be processed by the hardware and imageOrientation = .up
} else {
presented on the screen. The view layer is created from a class currentOrientation = AVCaptureVideoOrientation.portrait
provided by the Core Animation framework called CALayer. In iOS, imageOrientation = .right
}
every view automatically includes a layer. To provide access to this break
layer, the UIView class includes the layer property (implemented in }
return currentOrientation
Listing 18-11). For more information, visit our website and follow the }
links for this chapter. 
To determine the orientation, we define a method called IMPORTANT: The camera always encodes the image in its native
getCurrentOrientation(). We need to know the device's orientation to be able orientation, which is landscape-right. In consequence, when the
to define the orientation of the preview layer and the orientation of the device is in portrait mode, we must set the orientation of the image
image taken by the camera. For these reasons, our method returns the to right, when it is in landscape-left mode, we must set the image's
AVCaptureVideoOrientation value we need to set the orientation of the preview orientation to up, and when it is in landscape-right mode, we must
layer and stores a UIImage.Orientation value in the imageOrientation property to set it to down. Also, the landscape orientation of the video is the
set the image's orientation later. opposite of the device, therefore when the device is in the
landscape-right orientation, the video orientation is landscape-left,
Listing 18-12: Detecting the device's orientation
and vice versa.
previewPhotoFormat—This property set or returns a dictionary
At this point, the video is playing on the screen and the system is ready to with keys and values that determine the characteristics of the preview
perform a capture. To allow the user to take a picture, we must connect image. The keys available are kCVPixelBufferPixelFormatTypeKey
the button added at the bottom of the view (Figure 18-9, right) to an (uncompressed format), kCVPixelBufferWidthKey (maximum width) and
Action in the CameraViewController class. The process to capture an image is
kCVPixelBufferHeightKey (maximum height).
initiated by the output object. The AVCapturePhotoOutput class we use to
capture a still image offers the following method for this purpose. flashMode—This property sets or returns the flash mode used when
the image is captured. It is an enumeration of type FlashMode with the
capturePhoto(with: AVCapturePhotoSettings, delegate: values on, off, and auto.
AVCapturePhotoCaptureDelegate)—This method initiates a photo isHighResolutionPhotoEnabled—This property is a Boolean value
capture with the settings specified by the with argument. The that determines if the image is going to be taken in high resolution.
delegate argument is a reference to the object that implements the
methods of the AVCapturePhotoCaptureDelegate protocol to receive the To capture an image, we prepare the settings with an AVCapturePhotoSettings
data generated by the output. object, call the capturePhoto() method of the AVCapturePhotoOutput object, and
define the delegate method that is going to process the image returned.
The type of photo captured by the output is determined by an The following example implements the delegate method and an Action to
AVCapturePhotoSettings object. The class includes multiple initializers. The initiate the process when the user presses the button.
following are the most frequently used.
Listing 18-13: Taking a picture
AVCapturePhotoSettings()—This initializer creates an 
@IBAction func takePicture(_ sender: UIButton) {
AVCapturePhotoSettings object with the format by default. let settings = AVCapturePhotoSettings()
AVCapturePhotoSettings(format: Dictionary)—This initializer stillImage.capturePhoto(with: settings, delegate: self)
}
creates a AVCapturePhotoSettings object with the format specified by the func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo:
format argument. The argument is a dictionary with keys and values AVCapturePhoto, error: Error?) {
let scale = UIScreen.main.scale
to set the characteristics of the image. Some of the keys available are
if let imageData = photo.cgImageRepresentation() {
kCVPixelBufferPixelFormatTypeKey (uncompressed format), AVVideoCodecKey
picture = UIImage(cgImage: imageData, scale: scale, orientation: imageOrientation)
(compressed format), AVVideoQualityKey (compression quality). performSegue(withIdentifier: "goBackSegue", sender: self)
}
}
The following are some of the properties available in this class to configure 
the image and the preview.
When the user presses the button to take the picture, the takePicture() the orientation of the device. As we have done before for other projects in
method calls the capturePhoto() method to ask the output object to capture this book, we can implement the viewWillTransition() method to get the new
an image. After the image is captured, this object sends the result to the size of the main view and adjust the preview layer accordingly.
delegate method. The value received by the method is an object of type
AVCapturePhoto, which is a container with information about the image. The Listing 18-14: Rotating the preview layer
class includes two convenient methods to get the data representing the 
image. override func viewWillTransition(to size: CGSize, with coordinator:
UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
fileDataRepresentation()—This method returns a data if previewLayer != nil {
representation of the image that we can use to create a UIImage previewLayer.frame.size = size
picture was taken, so the image is always displayed correctly. Unwind Segue between this scene and the goBack() method of the
After the image is processed, we trigger the Unwind Segue identified with ViewController class and give it the identifier "goBackSegue".
the string "goBackSegue" to take the user back to the initial scene. This is Remember to add the option "Privacy - Camera Usage Description"
the Unwind Segue we must create for the goBack() Action included in the to the info.plist file. Run the application and take a picture.
ViewController class of Listing 18-7.
The controller for the camera is ready, but there is one more method we
need to add to the CameraViewController class to adapt the preview layer to
AVKit Framework rate—This property sets or returns a Float value that determines the
rate at which the media is being played. A value of 0.0 pauses the

video and 1.0 sets the normal rate.
In addition to the scene created by the UIImagePickerController class to take play()—This method begins playback.
pictures, we can also create a scene with standard controls to play videos. pause()—This method pauses playback.
The framework that provides this feature is called AVKit, and the class is
called AVPlayerViewController. This class creates a player with the controls
addPeriodicTimeObserver(forInterval: CMTime, queue:
necessary for the user to play a video. The following are some of its DispatchQueue?, using: Closure)—This method adds an observer
properties, used to define and configure the player. that executes a closure every certain period of time. The forInterval
argument determines the time between executions, the queue
player—This property sets the player that provides the media for the argument is the queue in which the closure should be executed (the
controller to play. It is an object of type AVPlayer. main queue is recommended), and the using argument is the closure
showsPlaybackControls—This property is a Boolean value that we want to execute. The closure receives a value of type CMTime with
the time at which the closure was called.
determines whether the player is going to show the controls or just
the video.
The following example shows how to create, configure, and open an
AVPlayerViewController controller.
The controller defines the player and presents the interface for the user to
control the video, but the video is played by an object of the AVPlayer class.
Listing 18-15: Using a standard video player
The class includes the following initializer to use with AVKit.

import UIKit
AVPlayer(url: URL)—This initializer creates an AVPlayer object to play import AVFoundation
the media stored in the URL indicated by the argument. import AVKit
This example assumes that we have a button in the initial scene connected
to an Action in the ViewController class called playVideo(). When the button is
pressed, the method creates the AVPlayer object from a URL, initializes the
AVPlayerViewController controller, assigns the player object to the controller’s
player property, and presents the controller on the screen. In this example
we use the completion handler of the present() method to play the video as
soon as the view is loaded, but we can let the user decide.
Let's see the elements one by one, starting from the asset. An asset is The AVPlayerItem object manages the information necessary for playback
composed of one or more tracks of media, including video, audio, subtitles, but it does not play the media; this is done by an instance of the AVPlayer
etc. The AVFoundation framework defines a class called AVAsset to load an class. This is the same class we used with the AVPlayerViewController object to
asset. The class includes the following initializer. play videos. The class includes the following initializer to create a player
from an AVPlayerItem object.
AVAsset(url: URL)—This initializer creates an AVAsset object with the
media in the location indicated by the url argument. The argument is AVPlayer(playerItem: AVPlayerItem)—This initializer creates an
a URL structure with the location of a local or remote resource. object to play the media represented by the playerItem
AVPlayer
argument.
An asset contains static information and cannot manage its status when it
is being played. To control the asset, the framework defines the AVPlayerItem The last object required by the structure is the one in charge of displaying
class. With this class, we can reference an asset and manage its timeline. the media on the screen. This is a subclass of the CALayer class called
The class includes multiple initializers, including the following. AVPlayerLayer that provides the code necessary to draw the frames. The
class includes the following initializer and property to create and configure value of 1 preserves the value in seconds assigned to the first
the layer. argument.
zero—This type property returns a CMTime structure with a value of 0.
AVPlayerLayer(player: AVPlayer)—This initializer creates an
AVPlayerLayer object associated with the player specified by the player The CMTime structure also includes multiple properties to set and
argument. retrieve the values. The following are the most frequently used.
videoGravity—This property defines how the video adjusts its size
to the preview layer’s size. It is a AVLayerVideoGravity structure with the seconds—This property returns the time in seconds. It is of type
type properties resize, resizeAspect, and resizeAspectFill. Double.
player = AVPlayer(playerItem: playerItem) Do It Yourself: Create a project. Add an empty view to the scene.
playerLayer = AVPlayerLayer(player: player) Connect the view to the ViewController class with an Outlet called
playerLayer.frame = view.bounds videoView. Complete the class with the code in Listing 18-16.
let layer = videoView.layer
layer.addSublayer(playerLayer)
Download the file trailer.mp4 from our website and add it to your
} project (remember to mark the option Add to Target). Run the
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change:
[NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
application. The video should start playing as soon as the application
if keyPath == "status" { is launched.
if playerItem.status == .readyToPlay {
playerItem.removeObserver(self, forKeyPath: "status")
player.play() The previous example plays the video, but it does not provide any tools for
} the user to control the process. The AVPlayer class includes methods to play,
}
} pause, and check the state of the media, but we are responsible for
override func viewWillTransition(to size: CGSize, with coordinator: creating the interface. Figure 18-12, below, introduces a new interface with
UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator) the view to show the video and an additional view in front of it with a
playerLayer.frame.size = size button and a Progress View to allow the user to play the video and see the
}
}
progression over time.

Figure 18-12: Controls for a custom video player
In this example, we load the video from the bundle and create the player
structure as soon as the scene is loaded. The player is associated with an
AVPlayerLayer layer and the layer is added as a sublayer of the view where
How we control the process and respond to the interface depends on the In Listing 18-17, we add a few more Outlets to the ViewController class to
requirements of our application. For this example, we have decided to reference the elements of the interface and disable the Play button until
define two properties to keep track of the state of the media, ready and the video is ready to play, but the code to prepare the player remains the
playing. The ready property will be true when the media is ready to play and
same. The difference is in how the video is played because this time we
the playing property will be true while the media is being played. The need to wait until the user presses the Play button. This change appears in
following are the definition of the properties and the initial configuration the observeValue() method. Instead of playing the video when the value of
required by our application. the status property changes to readyToPlay, we assign the value true to the
ready property to let the rest of the code know that the video is ready to be
Listing 18-17: Preparing the video player played. But this is not all the method has to do. We must also register an

observer to keep the progress bar updated while the video is being played.
import UIKit
The AV Foundation framework offers the addPeriodicTimeObserver() method to
import AVFoundation create an observer. The method requires a CMTime value to determine the
frequency in which the code will be executed, a reference to the main
class ViewController: UIViewController {
@IBOutlet weak var videoView: UIView! queue (the Main Actor), and a closure with the code we want to execute
@IBOutlet weak var playButton: UIButton! every time the observer is triggered.
@IBOutlet weak var progressBar: UIProgressView!
var playerItem: AVPlayerItem!
var player: AVPlayer! Listing 18-18: Updating the progress bar
var playerLayer: AVPlayerLayer!

var ready = false
var playing = false override func observeValue(forKeyPath keyPath: String?, of object: Any?, change:
[NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "status" { The player and the progress bar are ready, so the only thing left is to add
if playerItem.status == .readyToPlay {
ready = true the Action for the Play button. This is a simple method that executes the
playButton.isEnabled = true player’s play() or pause() methods depending on the value of the playing
playerItem.removeObserver(self, forKeyPath: "status")
property.
let interval = CMTime(value: 1, timescale: 2)
player.addPeriodicTimeObserver(forInterval: interval, queue: .main, using: { [unowned Listing 18-19: Playing and pausing the video
self] time in
let duration = self.playerItem.duration 
let position = time.seconds / duration.seconds @IBAction func playVideo(_ sender: UIButton) {
self.progressBar.progress = Float(position) if ready {
}) if playing {
} player.pause()
} playing = false
}
 var current = playButton.configuration
current?.title = "Play"
playButton.configuration = current
In Listing 18-18, we create a CMTime value to represent a time of 0.5 } else {
seconds, and then use it in the call of the addPeriodicTimeObserver() method to player.play()
register the observer. After this, the closure provided to the observer will playing = true
be executed every 0.5 seconds during playback. In this closure, we get the var current = playButton.configuration
current time and the duration of the video in seconds and calculate the current?.title = "Pause"
playButton.configuration = current
right position of the progress bar by turning seconds into a value between }
0.0 and 1.0 (the minimum and maximum values of a Progress View by }
default). }

IMPORTANT: The addPeriodicTimeObserver() method doesn't work with Figure 18-13: Custom video player
Swift concurrency. Instead, it requires the thread to be defined by a © Copyright 2008, Blender Foundation / www.bigbuckbunny.org
DispatchQueue object. This is an old class defined by the Dispatch
framework to create asynchronous tasks. The class includes a type
property called main to define a task for the main queue (the Main
Actor), and this is how we make sure that the closure assigned to
this method runs in the main queue. Notice that this closure is
required to define self as unowned to ensure that the reference does
not create a strong reference cycle.
servers (only URLs starting with https://). In Chapter 19, we will learn
how to create URL structures to access remote documents and how
to configure the application to be able to work with unsecured URLs.
the bottom and the sides of the main view to represent the control func checkEnding() async {
let center = NotificationCenter.default
bar for the video player. Change the color of the bar to gray. Click on let name = NSNotification.Name.AVPlayerItemDidPlayToEndTime
the color, select the Custom option to open the Color Picker, and for await _ in center.notifications(named: name, object: playerItem) {
await playerItem.seek(to: CMTime.zero)
declare an opacity of 50%. Include a button called Play and a
Progress View inside (Figure 18-12). Select the Progress View and await MainActor.run {
var current = self.playButton.configuration
assign the value 0 to it from the Attributes Inspector panel. Connect current?.title = "Play"
the button, and the Progress View with Outlets called playButton and self.playButton.configuration = current
self.playing = false
progressBar. Connect the Play button with an Action called playVideo(). }
}
Update the ViewController class with the codes in Listings 18-17, 18-18, }
and 18-19. Remember to add the viewWillTransition() method of Listing 
18-16 to adapt the layer to any orientation. Run the application and
press Play. This method creates a for in loop to listen to the AVPlayerItemDidPlayToEndTime
notification. Every time a video finishes playing, the player posts this
notification. Inside the loop, we call the seek() method to move the player
IMPORTANT: You cannot only play videos from files added to the
back to the beginning (time zero), and then reset the controls to allow the
project (local), but also from the Web (remote). All you need to do is
user to play the video again.
to create a URL structure with the URL pointing to the file you want
To start running this loop, we must call the checkEnding() method from an
to play and use it as the source for the asset. There is a restriction asynchronous task as soon as the scene is loaded. The following is the code
though. You can only play remote files that are stored in secure
we need to add to the viewDidLoad() method of our view controller to begin All we need to do to play a sequence of videos is to create the
this process. AVPlayerItem objects to load each video and create an AVQueuePlayer object to
replace the AVPlayer object we have used so far. The following example
Listing 18-21: Initiating the asynchronous task to listen to player introduces the necessary modifications to the ViewController class of Listing
notifications 18-17 to play two videos called videobeaches.mp4 and videotrees.mp4.

Task(priority: .high) { Listing 18-22: Playing a list of videos
await checkEnding()
} 
 import UIKit
import AVFoundation
Do It Yourself: Add the method in Listing 18-20 to the ViewController class ViewController: UIViewController {
class and the code in Listing 18-21 to the viewDidLoad() method. Run @IBOutlet weak var videoView: UIView!
@IBOutlet weak var playButton: UIButton!
the application, press the Play button, and let the video play to the @IBOutlet weak var progressBar: UIProgressView!
end. Press the Play button to play it again. var playerItem: AVPlayerItem!
var player: AVQueuePlayer!
var playerLayer: AVPlayerLayer!
If we want to play multiple videos in sequence, we could use this var ready = false
var playing = false
notification to assign a new asset to the AVPlayer object, but the framework
offers a subclass of the AVPlayer class called AVQueuePlayer designed override func viewDidLoad() {
super.viewDidLoad()
specifically for this purpose. The class creates a playlist from an array of playButton.isEnabled = false
AVPlayerItem objects. The following are the class initializer and some of its
let bundle = Bundle.main
methods. let videoURL1 = bundle.url(forResource: "videobeaches", withExtension: "mp4")
let asset1 = AVAsset(url: videoURL1!)
playerItem = AVPlayerItem(asset: asset1)
AVQueuePlayer(items: [AVPlayerItem])—This initializer creates a playerItem.addObserver(self, forKeyPath: "status", options: [], context: nil)
play list with the items specified by the items argument.
let videoURL2 = bundle.url(forResource: "videotrees", withExtension: "mp4")
advanceToNextItem()—This method plays the next item on the list. let asset2 = AVAsset(url: videoURL2!)
let playerItem2 = AVPlayerItem(asset: asset2)
insert(AVPlayerItem, after: AVPlayerItem?)—This method inserts
a new item to the list. player = AVQueuePlayer(items: [playerItem, playerItem2])
colorPickerViewControllerDidFinish(UIColorPickerViewContro
ller)—This method is called on the delegate when the user closes the
picker.
colorPickerViewController(UIColorPickerViewController,
didSelect: UIColor, continuously: Bool)—This method is called on
the delegate when the user selects a color.
The following example illustrates how to set up a color picker, configure its
values, and get the color selected by the user. The code assumes that we
have a button on the interface connected to an Action in the ViewController
class called openColorPicker().
The most important aspect of the Web is the ease with which we can
access documents with a simple link. A link is a short text or an image that
is associated with a URL that determines the location of a document. When
the user clicks or taps the text or image representing the link, the
document is opened. Links were designed for the Web, but we can add
them to our applications and let the system decide where to open the
document. For instance, if the link contains a web address, the system
opens the browser to load the document.
Web addresses are created from URL structures. We have used these types
of structures before to determine the location of files, but we can also use
them to access remote documents. The class includes the following
initializers to create a URL.
The URL structure is just a container for the location of the document we Do It Yourself: Create a new project. Add a button to the initial
want to open, but the document is opened from the Scene (the window). scene and connect it to the ViewController class with an Action called
The UIScene class offers the following method for this purpose. openWeb(). Complete the ViewController class with the code in Listing 19-
1. Run the application and press the button. The system should open
open(URL, options: OpenExternalURLOptions?)—This the browser and load the website.
asynchronous method opens the URL specified by the first argument.
The options argument is an object that configures the operation In the last example, we have defined the URL in code, but sometimes the
(defined as nil to set standard options). The method returns a Boolean URL is provided by the user or taken from another document. In cases like
value that determines whether the document was opened or not. this, the URL may contain characters that are not allowed and can cause
the location to be impossible to identify. To make sure that the URL is valid,
The following example opens the website www.formasterminds.com when we must turn unsafe characters into percent-encoding characters. These
a button is pressed. The code creates the URL structure from a string, gets are characters represented by the % sign followed by a hexadecimal
a reference to the Scene managing the window from the windowScene number. Fortunately, there are methods that can correct unsafe characters
property provided by the UIWindow class, and finally calls the open() method in a string for us. We have studied a method like this provided by the String
on this Scene to open the URL. The system reads the URL, detects that it is structure in Chapter 15. The data(using: Encoding, allowLossyConversion: Bool)
a web address and opens the browser to load the website. method turns a string into a Data structure using a type of encoding that
automatically corrects the characters.
Listing 19-1: Opening a website
 Listing 19-2: Encoding URLs
import UIKit 
import UIKit
class ViewController: UIViewController {
@IBAction func openWeb(_ sender: UIButton) { class ViewController: UIViewController {
let web = "http://www.formasterminds.com" @IBAction func openWeb(_ sender: UIButton) {
if let webURL = URL(string: web) { let web = "http://www.formasterminds.com"
Task(priority: .high) { if let dataURL = web.data(using: String.Encoding.utf8, allowLossyConversion: false) {
await openURL(url: webURL) if let webURL = URL(dataRepresentation: dataURL, relativeTo: nil, isAbsolute: true) {
} Task(priority: .high) {
} await openURL(url: webURL)
} }
@MainActor func openURL(url: URL) async { }
let scene = view.window?.windowScene }
await scene?.open(url, options: nil) }
Listing 19-3. Run the application and press the button. The system present(controller, animated: true, completion: nil)
opens a scene with a browser and all the tools required for }
}
navigation, including a Done button to close it. 
The SFSafariViewController class also offers the following properties for The code in this example also modifies the dismissButtonStyle property to
configuration. change the type of button shown by the browser to close the scene from
Done to Close.
dismissButtonStyle—This property sets or returns a value that
determines the type of button the view controller is going to show to Figure 19-1: Custom Safari View Controller
dismiss the scene. It is an enumeration of type DismissButtonStyle with
the values done (default), close, and cancel.
preferredBarTintColor—This property sets or returns a UIColor
value that determines the color of the bars.
preferredControlTintColor—This property sets or returns a UIColor
value that determines the color of the controls.

The following example takes advantage of these properties to get the
colors of the bars to match the colors used by the When the user scrolls the page, the controller collapses the bars to make
www.formasterminds.com website. more room for the content. This makes difficult for the user to dismiss the
scene or use the tools. If we think that it would be better for our app to
always keep the bars visible and at their original size, we can initialize the safariViewControllerDidFinish(SFSafariViewController)—This
controller with a configuration object and assign the value false to the method is called by the controller when the scene is dismissed (the
barCollapsingEnabled property. user pressed the Done button).
The Configuration class provides a simple initializer with no parameters. Once
the object is created, we can configure its properties and assign it to the The Safari View Controller includes the delegate property to define a
Safari View Controller from the controller’s initializer, as shown in the delegate. The following example assigns the ViewController class as the
following example. delegate and implements the safariViewControllerDidFinish() method to
deactivate the button on the interface when the user dismisses the scene
Listing 19-5: Displaying the bars in their original size (the user is only able to open the scene once).

import UIKit Listing 19-6: Assigning a delegate to the Safari View Controller
import SafariServices

class ViewController: UIViewController { import UIKit
@IBAction func openWeb(_ sender: UIButton) { import SafariServices
let url = URL(string: "http://www.formasterminds.com")
class ViewController: UIViewController, SFSafariViewControllerDelegate {
let config = SFSafariViewController.Configuration()
@IBOutlet weak var openButton: UIButton!
config.barCollapsingEnabled = false
let controller = SFSafariViewController(url: url!, configuration: config) @IBAction func openWeb(_ sender: UIButton) {
present(controller, animated: true, completion: nil) let url = URL(string: "http://www.formasterminds.com")
} let controller = SFSafariViewController(url: url!)
} controller.delegate = self
 present(controller, animated: true, completion: nil)
}
Do It Yourself: Update the ViewController class with the code in func safariViewControllerDidFinish(_ controller: SFSafariViewController){
openButton.isEnabled = false
Listing 19-5. Run the application and scroll the page. The bars should }
stay at the same size and the buttons should always be visible. }

argument (the information of the protocol is taken from the URL delegate when an error occurs.
itself). The cachePolicy argument is an enumeration that determines webView(WKWebView,
how the request will work with the cache. The possible values are: didReceiveServerRedirectForProvisionalNavigation:
useProtocolCachePolicy (default), reloadIgnoringLocalCacheData, WKNavigation!)—This method is called on the delegate when the
reloadIgnoringLocalAndRemoteCacheData, reloadIgnoringCacheData, server redirects the navigator to a different destination.
returnCacheDataElseLoad, returnCacheDataDontLoad, and
reloadRevalidatingCacheData. The timeoutInterval argument is the The process to load a website in a WebKit View is simple. We get the URL,
maximum time allowed for the system to process the request (60.0 create a request, and ask the view to load it.
seconds by default).
Listing 19-7: Loading a website with a WebKit View
A WebKit View can report the state of the content to a delegate. For this 
purpose, the framework defines the WKNavigationDelegate protocol. The import UIKit
import WebKit
following are some of its methods.
class ViewController: UIViewController {
webView(WKWebView, decidePolicyFor: @IBOutlet weak var webView: WKWebView!
WKNavigationAction, decisionHandler: Block)—This method is
override func viewDidLoad() {
called on the delegate to determine if the view should process a super.viewDidLoad()
request. The decidePolicyFor argument is an object with information
if let webURL = URL(string: "https://www.google.com") {
about the request, and the decisionHandler argument is a closure let request = URLRequest(url: webURL)
that we must execute to communicate our decision to the system. The webView.load(request)
}
closure takes a value of type WKNavigationActionPolicy, an enumeration }
with the properties cancel and allow. }

webView(WKWebView, didStartProvisionalNavigation:
WKNavigation!)—This method is called on the delegate when the This example assumes that we have added a WebKit View to the interface
view begins loading new content. from the Library, and it is connected to the ViewController class with an
webView(WKWebView, didFinish: WKNavigation!)—This Outlet called webView. To prepare the request, we get the URL structure
method is called on the delegate when the view finishes loading the with the address we want to access and initialize the URLRequest structure
content. with values by default (only the URL is required). Finally, the request is
loaded with the load() method and the website is shown on the screen.
webView(WKWebView, didFailProvisionalNavigation:
WKNavigation!, withError: Error)—This method is called on the
Do It Yourself: Create a project. Add a WebKit View to the scene. In this interface, we have embedded the scene in a Navigation Controller
Connect the WebKit View to the ViewController class with an Outlet and added three buttons to the Navigation Bar to let the user go back,
called webView. Complete the ViewController class with the code in move forward, and refresh the page.
Listing 19-7. Run the application. You should see Google’s website on Before modifying the content of a WebKit View we need to know what we
the screen. can and cannot do. For example, we can only go back if the user has
already navigated forward. This is when the delegate methods become
useful. Implementing the protocol methods, we can check the status of the
With this process, we can load any website we want, including those
content every time a new document is loaded, as shown next.
specified or selected by the user (we just need to prepare the URL the way
we did before for links in Listing 19-2). But users need more control over
Listing 19-8: Implementing a custom web browser
the content. For instance, if we use the previous example to perform a

Google search, we will notice right away that there is no way to go back to
import UIKit
the previous page or navigate back to the beginning. WebKit Views offer import WebKit
several methods to manipulate their content, and delegate methods to
respond to changes, but we must provide the tools for navigation, as class ViewController: UIViewController, WKNavigationDelegate {
@IBOutlet weak var backButton: UIBarButtonItem!
shown in the following example. @IBOutlet weak var forwardButton: UIBarButtonItem!
@IBOutlet weak var refreshButton: UIBarButtonItem!
@IBOutlet weak var webView: WKWebView!
Figure 19-3: Custom web browser for our application
override func viewDidLoad() {
super.viewDidLoad()
updateButtons()
webView.navigationDelegate = self
if let webURL = URL(string: "https://www.google.com") {
let request = URLRequest(url: webURL)
webView.load(request)
}
}
@IBAction func moveBack(_ sender: UIBarButtonItem) {
webView.goBack()
}
@IBAction func moveForward(_ sender: UIBarButtonItem) {
webView.goForward()
}
@IBAction func refresh(_ sender: UIBarButtonItem) {
webView.reload()
 }
func webView(_ webView: WKWebView, decidePolicyFor navigationAction:
WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if webView.isLoading {
updateButtons() results. The buttons in the Navigation Bar should be enabled and
}
decisionHandler(.allow) disabled according to your location in the browsing history.
}
func updateButtons() {
backButton.isEnabled = webView.canGoBack
forwardButton.isEnabled = webView.canGoForward
}
}

In the latest examples, we open secure URLs (URLs that begin with the 
prefix https://), because these are the URLs allowed by default. iOS
implements a system called App Transport Security (ATS) to block insecure The Allow Arbitrary Loads key takes a Boolean value specified with the
URLs, like those starting with the prefix http:// (without the s). If we need strings YES and NO (or 1 and 0, respectively). Setting this key to YES (1)
to load insecure URLs, such as http://www.formasterminds.com, we can allows any URL to be opened. If what we want is to allow only specific
configure our app from the info.plist file to circumvent this security domains, we must use the Exception Domains key and add to the key
measure for all the websites or specific domains. The option to configure additional items with the domains we want to include. These items in turn
the App Transport Security system is called "App Transport Security require at least three more items with the keys NSIncludesSubdomains
Settings", as shown below. (Boolean), NSTemporaryExceptionAllowsInsecureHTTPLoads (Boolean), and
NSTemporaryExceptionMinimumTLSVersion (String). For example, the following
Figure 19-4: Option to configure App Transport Security configuration allows documents from the formasterminds.com domain to
be opened.
The interface includes a + button on the right side of the item to add values
to that item. In this case, the button has two functions: if the arrow on the
left is pointing to the item (closed), a new item is added to the main list,
but if the arrow is pointing down (expanded), the new item is added to the
item as a new value (we can click the arrow to close or expand the item).
Values added to an item are shown below the item with a little indentation
to reflect the hierarchy. The value we need to add to the App Transport 
Security Settings item to allow insecure URLs to be opened is called "Allow
Arbitrary Loads", as shown below.
properties to set a specific configuration. The following is the type property await loadWebsite()
}
we can use to get a configuration object with values by default. }
func loadWebsite() async {
let config = URLSessionConfiguration.default
default—This property returns a URLSessionConfiguration object with config.waitsForConnectivity = true
default settings. let session = URLSession(configuration: config)
let webURL = URL(string: "https://www.yahoo.com")
Listing 19-14: Decoding JSON data The previous example explains how to read and decode a JSON file stored
 in the bundle, but this information is usually downloaded from online
import UIKit services. The JSON documents provided by these services are dynamically
class ViewController: UIViewController { generated and contain only the information requested by the application.
override func viewDidLoad() { For instance, the website www.openweathermap.org offers a service that
super.viewDidLoad()
let bundle = Bundle.main
generates JSON documents with information about the weather for
let jsonURL = bundle.url(forResource: "books", withExtension: "json") specific locations (https://openweathermap.org/api).
let jsonData = FileManager.default.contents(atPath: jsonURL!.path) To illustrate how to access and process the documents produced by these
let decoder = JSONDecoder() services, we are going to read posts from a website called JSONPlaceholder
do { that generates phony documents. The process doesn't introduce anything
let info = try decoder.decode(Book.self, from: jsonData!)
print("Title: \(info.title)")
new. We must load the document with a URLSession and then decode it with
print("Author: \(info.author)") a JSONDecoder object, as we did before.
} catch { The document we are going to download for this example is located at
print("Error: \(error)")
} https://jsonplaceholder.typicode.com/posts. The JSON code generated by
} this document includes multiple items, each with four values called userId,
}
 id, title, and body. The following model includes the structure we need to
decode these values.
This example assumes that we have created a file called books.json with
the JSON code of Listing 19-12. Once we read the file from the bundle and Listing 19-15: Defining a model to decode posts
get its content, we create a JSONDecoder object and then call the decode() 
method on it to turn the data into a Book structure. The values of this import UIKit
structure are finally printed on the console to confirm that the process was enum Sections {
successful. case main
}
struct Post: Codable, Identifiable {
Do It Yourself: Create a new project. Create a text file called var id: Int
var userId: Int
books.json with the JSON code of Listing 19-12 and add it to the
var title: String
project. Create a Swift file called Book.swift with the structure in var body: String
}
Listing 19-13. Update the ViewController class with the code in Listing
class ApplicationData {
var listPosts: [Post] = []
var dataSource: UITableViewDiffableDataSource<Sections, Post.ID>!
Task(priority: .high) {
await loadWebsite()
This example assumes that the initial scene in the interface has been
} replaced by a Table View Controller. When the scene is loaded, we create
} an asynchronous task to download the JSON file at the URL
func loadWebsite() async {
let session = URLSession.shared https://jsonplaceholder.typicode.com/posts. If the status of the response is
let webURL = URL(string: "https://jsonplaceholder.typicode.com/posts") 200, which means the data has been downloaded successfully, we decode
do {
let (data, response) = try await session.data(from: webURL!)
it into an array of Post structures, assign the array to the listPosts property in
if let resp = response as? HTTPURLResponse { the model, and update the snapshot to display the values on the screen.
let status = resp.statusCode The result is shown below.
if status == 200 {
let decoder = JSONDecoder()
AppData.listPosts = (try? decoder.decode([Post].self, from: data)) ?? [] Figure 19-7: List of posts downloaded from the Web
await MainActor.run {
self.prepareSnapshot()
}
}
}
} catch {
print("Error: \(error)")
}
}
func prepareDataSource() {

Often, users need to visualize their location or the places they want to go
on a map to position themselves in the world. For these kinds of
applications, Apple offers the MapKit framework. The framework includes
all the tools necessary to create and configure maps with which users can
interact to find places and obtain information.
Maps are presented on the screen with a view created from a subclass of
UIView called MKMapView. The view takes care of loading the map, managing
user interaction, and displaying custom content generated by our
application.
Like a regular view, a Map Kit View can be pinned to the sides of the main
view or other elements. Figure 20-2 introduces the interface we are going
to use for the following examples. The scene includes a Map Kit View and a
bar button to interact with the map.
A Map Kit View can show the map in different styles, and let the user
zoom, pan, or rotate the map to find a location. The MKMapView class
includes properties to configure these features.
To modify the map, we must import the MapKit framework and then
change the values of these properties. The following example defines the
style of the map and disables rotation.
typealias of Double).
override func viewDidLoad() {
super.viewDidLoad() altitude—This property returns the altitude of the location in
mapView.mapType = .satellite
mapView.isRotateEnabled = false meters. It is of type CLLocationDistance (a typealias of Double).
}
}
 The CLLocation class also includes a practical method to calculate the
distance between two CLLocation objects.
Do It Yourself: Connect the Map Kit View to the ViewController class
with an Outlet called mapView. Complete the class with the code in distance(from: CLLocation)—This method returns a value that
Listing 20-1 and run the application. You should see a satellite map determines the distance between the location in the CLLocation object
of the world. and the location determined by the from argument. The argument is
a CLLocation object with the location we want to compare. The value
Users can zoom in and out to find a specific place on the map, but we can returned is of type CLLocationDistance (a typealias of Double).
also do it from code. To determine locations in the map, the MapKit
framework implements values defined in a framework called Core These values define the location, but the map is scrolled to those locations
Location. The most important class is CLLocation, designed to store by properties and methods of the MKMapView class. The following are the
information about a location. The class includes an initializer to create the ones available to get the values of the area currently displayed on the
object and properties to retrieve the values. screen or set a new one.
MKCoordinateRegion(center: CLLocationCoordinate2D,
latitudinalMeters: CLLocationDistance, longitudinalMeters:
CLLocationDistance)—This initializer creates an MKCoordinateRegion
structure with the values determined by its arguments. The first
argument specifies the region’s coordinates, and the second and third
arguments determine the vertical and horizontal size of the region in
meters.
Annotations

The previous example sets the visible area around the Apple Store.
Because this is a relevant location, the map shows an icon with the name
of the store, but this is not always the case. Most locations do not show
any reference at all, and the user has to guess where the exact location
actually is. To add graphics to the map to mark a location, we use
annotations.
Annotations provide additional information of a particular location. They
are associated with a view that can display an image to represent the
annotation, such as a pin, the title and subtitle of the location, and a
subview, called Callout, to show additional information. Figure 20-4, below,
illustrates how an annotation looks like on the map.
What the framework describes as annotations are objects that define the
basic aspects of an annotation, like the coordinates in which the
annotation will appear, the image that will represent the location, and the
title and subtitle to show along with it. The MapKit framework includes the
MKAnnotation protocol to create these objects. The protocol defines the
following properties.
override func viewDidLoad() { IMPORTANT: The MapKit framework includes the MKPointAnnotation
super.viewDidLoad()
class to create basic annotations. If your annotations only require
let location = CLLocation(latitude: 40.7637825011971, longitude: -73.9731328627541) the values of the coordinate, title, and subtitle properties, you can create
let region = MKCoordinateRegion(center: location.coordinate, latitudinalMeters: 1000,
longitudinalMeters: 1000) them from this class instead of defining your own (see Listing 20-12,
mapView.setRegion(region, animated: false)
below).
let annotation = MyAnnotation(coordinate: location.coordinate)
annotation.title = "Apple Store" As we already mentioned, an annotation object determines where the
annotation.subtitle = "Think Different"
mapView.addAnnotation(annotation) annotation is located, but the annotation view is the element responsible
} for displaying the graphic that points to the coordinates of the annotation.
}
If we do not provide a view for the annotation, the system creates one by

default from a class called MKMarkerAnnotationView. The views created from
By default, the Map Kit View generates a view to show the annotation on this class include the balloon shown in Figure 20-5, always with the same
the map. The view contains an image that represents a pin and the title icon and in the same color. If we want to change the configuration of the
below. When the annotation is selected, the view shows a larger image and view, we must create our own objects. For this purpose, the
the subtitle, as illustrated next. MKMarkerAnnotationView class includes the following initializer and
properties.
Figure 20-5: Annotation with a view by default
MKMarkerAnnotationView(annotation: MKAnnotation?,
reuseIdentifier: String?)—This initializer creates an annotation
view of type marker for the annotation specified by the annotation
argument. The annotation argument is a reference to the annotation
object we want to associate with the view, and the reuseIdentifier
argument is a string the Map Kit View needs to be able to reuse the
view to display multiple annotations.

glyphText—This property sets or returns the text displayed in the
Do It Yourself: Add to your project a Swift file called balloon.
MyAnnotation.swift for the code in Listing 20-3. Update the markerTintColor—This property sets or returns a UIColor value that
ViewController class with the code in Listing 20-4. Run the application determines the color of the balloon.
and tap on the pin to expand it (Figure 20-5). glyphTintColor—This property sets or returns a UIColor value that
determines the color of the text in the balloon.
glyphImage—This property sets or returns a UIImage object with the dequeueReusableAnnotationView(withIdentifier: String)—
image displayed in the balloon. The image must be of a size of 20 x 20 This method returns an MKAnnotationView object with the view
points. identified by the withIdentifier argument. The argument is the same
selectedGlyphImage—This property sets or returns a UIImage object string declared when the view was created.
with the image displayed in the balloon when it is selected. The image
must be of a size of 40 x 40 points. The ViewController class in the following example conforms to the
MKMapViewDelegate protocol and implements the delegate method to create
titleVisibility—This property sets or returns a value that determines a custom view for our annotation.
whether the title will be visible or not. It is an enumeration of type
MKFeatureVisibility with the values adaptive, hidden, and visible. Listing 20-5: Configuring the annotation view
subtitleVisibility—This property sets or returns a value that 
import UIKit
determines whether the subtitle will be visible or not. It is an import MapKit
enumeration of type MKFeatureVisibility with the values adaptive, hidden,
class ViewController: UIViewController, MKMapViewDelegate {
and visible. @IBOutlet weak var mapView: MKMapView!
} else {
aView?.annotation = annotation
}
return aView
}
return nil
}
}


The code in Listing 20-5 sets the region we want to show, adds an
annotation to the map at the coordinates of the Apple Store, and If we want to replace the balloon altogether, instead of using an object
designates the ViewController class as the Map Kit View’s delegate. The of the MKMarkerAnnotationView class, we must define our own annotation
protocol method is implemented next to provide a custom view for this view with an object of a basic class called MKAnnotationView (this is the
superclass of the MKMarkerAnnotationView class). The class includes the
annotation.
following initializer and properties to create and configure the view.
When the Map Kit View is showing the selected area, it detects that there
is an annotation inside and calls the mapView(MKMapView, viewFor:
MKAnnotation) method to get its view. This method checks whether the
MKAnnotationView(annotation: MKAnnotation?,
annotation is one of our custom annotations, looks for a view with the reuseIdentifier: String?)—This initializer creates an annotation
"Pins" identifier, and if there is no view, it creates a new one. Notice that view for the annotation specified by the annotation argument. The
the dequeueReusableAnnotationView() method may return a view that was annotation argument is a reference to the annotation object we want
previously used for another annotation, so we must assign the new to associate with the view, and the reuseIdentifier argument is a
annotation to it before returning the value (aView?.annotation = annotation). string the Map Kit View needs to be able to reuse the view to display
The configuration defined for our annotation view includes a title for the multiple annotations.
balloon, a blue background, and it hides the annotation’s title and subtitle.
image—This property sets or returns the image for the view.
The result is shown below.
leftCalloutAccessoryView—This property sets or returns the view
Figure 20-6: Custom balloon displayed on the left side of the callout bubble.
rightCalloutAccessoryView—This property sets or returns the
view displayed on the right side of the callout bubble.
displayPriority—This property sets or returns a value that
determines the annotation’s priority. It is a structure of type
MKFeatureDisplayPriority that can be initialized with a value from 0 to
1000. The structure defines three type properties to return an object
with standard values: required (1000), defaultHigh (750), and defaultLow 
(250).
clusteringIdentifier—This property sets or returns a string with the Our image replaces the balloon, and now the information is shown in a
callout bubble when the annotation is selected, as shown below.
name of the group to which the annotation belongs. This identifier is
used to cluster annotations when they are too close to each other.
Figure 20-7: Custom annotation view
isEnabled—This property sets or returns a Boolean value that
determines if the view can be selected.
The process to create a custom view is the same as before, but instead of
using the MKMarkerAnnotationView class to create the view, we must use the
MKAnnotationView class.
Also, these custom views present the information in a bubble on top of the
icon when the annotation is selected, so we must assign the value true to
the canShowCallout property if we want the user to be able to see it. The
following method creates a view with a custom image and a bubble to 
display the title and subtitle (the iconmap.png file is available on our
website). Do It Yourself: Download the iconmap.png image from our website
and add it to the Assets Catalog. Update the ViewController class from
Listing 20-6: Defining a custom annotation the previous example with the code in Listing 20-6. Run the
 application and tap on the icon. You should see something like Figure
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) ->
MKAnnotationView? {
20-7.
if let temp = annotation as? MyAnnotation {
var aView = mapView.dequeueReusableAnnotationView(withIdentifier: "Pins") The annotation view is responsible for the subview that presents the
if aView == nil {
aView = MKAnnotationView(annotation: temp, reuseIdentifier: "Pins") annotation’s callout bubble. Besides the title and the subtitle provided by
aView?.image = UIImage(named: "iconmap") the annotation, this subview can also include two small views or controls
aView?.canShowCallout = true on the sides. Because these views usually contain information specific for
} else {
aView?.annotation = annotation each annotation, we must define the data required to create the views in
} the annotation object. This may be images, text, decoration views, etc. For
return aView example, we can add a property to the MyAnnotation class to include a
}
return nil thumbnail that helps the user identify the location.
}
Displaying the user's location on a map is easy with a Map Kit View, all we
need to do is to assign some values to a few properties provided by the
MKMapView class and the Map Kit View takes care of detecting the user's
current location and show it on the map. The following are some of the
properties provided by the class for this purpose.
requestWhenInUseAuthorization()—This method asks for The first thing we do in this view controller is to ask the user for
authorization to get the location while the app is in use. permission. The instance of the CLLocationManager object must remain in
memory, so we store it in a property called manager. To ask for permission,
requestAlwaysAuthorization()—This method asks for
we call the requestWhenInUseAuthorization() method, and to get the location, we
authorization to get the location when the app is active or in the
set the Map Kit View's showsUserLocation property to true.
background.
The process is automatic, but we must define an option in the info.plist file
called "Privacy - Location When In Use Usage Description" with a string
If the app is authorized to access the user's location, the Map Kit View
that explains the user why we need to access his or her location.
shows a circle to indicate the location, but the configuration of the map
The view controller also includes an Action for the Show button to zoom in
doesn't change. If we want the map to show the area around the location,
on the area, so once the location is determined, the user can tap this
we must set the visible region with the setRegion() method, as shown next.
button to get a close-up view of his or her location.
Listing 20-9: Detecting and showing the user's location Figure 20-9: User’s location on the map

import UIKit
import MapKit
mapView.mapType = .standard
mapView.isRotateEnabled = false
mapView.showsUserLocation = true 
}
@IBAction func showLocation(_ sender: UIBarButtonItem) {
let location = mapView.userLocation Do It Yourself: Connect the Show button with an Action called
let region = MKCoordinateRegion(center: location.coordinate, latitudinalMeters: 1000,
longitudinalMeters: 1000) showLocation().
Update the ViewController class with the code in Listing
mapView.setRegion(region, animated: true) 20-9. Add the "Privacy - Location When In Use Usage Description"
}
} option to the info.plist file. Run the application on a device. You
 should see a window asking for authorization. Authorize the app to
access your location and press the Show button to see it on the map.
includes two options: Allow Once and Allow While Using App. If the user
The Map Kit View constantly updates the user’s location. To help us keep allows the app only once, every time the app is opened, it will ask for
track of these changes over time and detect errors, the MKMapViewDelegate permission again, which could be frustrating. To improve the user
protocol defines the following methods. experience, Apple provides a framework called CoreLocationUI which
includes a class called CLLocationButton to create a button that automatically
mapView(MKMapView, didUpdate: MKUserLocation)—This authorizes the app to access the user's location once. The class defines the
method is called on the delegate every time a new location is following properties to configure the aspect of the button.
determined. The didUpdate argument is an object with the user’s
current location. icon—This property sets or returns a value that defines the graphic to
display on the button. It is an enumeration of type CLLocationButtonIcon
mapView(MKMapView, didFailToLocateUserWithError: Error)
with the values none, arrowFilled, and arrowOutline.
—This method is called on the delegate when the Map Kit View
cannot determine the user’s location. label—This property sets or returns a value that determines the text
to display on the button. It is an enumeration of type
When the Map Kit View detects a new location, it calls the CLLocationButtonLabel with values that represent predefined titles. The
mapView(MKMapView, didUpdate:) method to report the result. Inside this values available are none, currentLocation ("Current Location"),
method, we can move the center of the region to the new location or sendCurrentLocation ("Send Current Location"), sendMyCurrentLocation
perform any other task necessary. ("Send My Current Location"), shareCurrentLocation ("Share Current
Location"), and shareMyCurrentLocation ("Share My Current Location").
Listing 20-10: Keeping track of the user’s location

cornerRadius—This property sets or returns a CGFloat value that
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) { determines the radius of the button's corners.
mapView.setCenter(userLocation.coordinate, animated: true)
} fontSize—This property sets or returns a CGFloat value that
 determines the size of the font.
import UIKit
import MapKit
import CoreLocationUI
mapView.mapType = .standard
mapView.isRotateEnabled = false
mapView.showsUserLocation = true

mapView.delegate = self
}
IMPORTANT: The button must be visible and easy to read. This is func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
the reason why the size of the Stack View is important. If Xcode let location = mapView.userLocation
let region = MKCoordinateRegion(center: location.coordinate, latitudinalMeters: 1000,
considers that either the title is not readable or the icon is not longitudinalMeters: 1000)
visible, it will show a warning message. You should always make sure mapView.setRegion(region, animated: true)
}
that there is enough space to display the title and icon in full. }

If the user has not yet authorized the app to access his or her location, an
Figure 20-11: User location button
Alert View is displayed on the screen to warn the user of what the app is
about to do, but if authorization has already been granted, the location is
determined immediately without prior notice. For this reason, we cannot
use the location when the button is pressed, we need to wait until we are
sure the location is available. And the best way to do it is by implementing
the delegate method introduced before. In the following example, we
show how to create a user location button and implement the delegate
method to zoom in to the location when it becomes available.
MKLocalSearch(request: MKLocalSearchRequest)—This
initializer creates an MKLocalSearch object to perform a search request.
start()—This asynchronous method performs a search and returns an
MKLocalSearchResponse object with the results.
url—This property sets or returns a URL value with the URL of the let search = MKLocalSearch(request: request)
do {
place’s website. let results = try await search.start()
placemark—This property sets or returns an MKPlacemark object with let items = results.mapItems
The setAnnotations() method of our example looks for places only in the
current region. If the user scrolls the map to find new places, the
information is not updated. To improve the application, we can take
advantage of two methods defined in the MKMapViewDelegate protocol that
are called when the region changes.
Directions The request is sent to Apple servers for processing. The framework defines
the MKDirections class to perform the request and process the results. The

class includes the following initializer and method.
Maps are not only used to find places but also to find routes to get from
MKDirections(request: Request)—This initializer creates an
one place to another. The MapKit framework includes a set of classes to
MKDirections object with the request specified by the request
calculate a route and draw it on the map. The first class we need to
implement is called Request, which is defined inside the MKDirections class. argument.
The Request class generates a request for a route between two locations. calculate()—This asynchronous method performs the request and
The following are the properties available in this class to configure the returns an MKDirectionsResponse object with the routes found.
request.
The routes are returned as objects of the MKRoute class. The class includes
source—This property sets or returns the route’s starting point. It is the following properties to get the route’s information.
of type MKMapItem.
destination—This property sets or returns the route’s destination. It polyline—This property sets or returns the route’s geometry that we
is of type MKMapItem. can use to draw the route on the map. It is an object of the MKPolyline
class.
requestsAlternateRoutes—This property sets or returns a Boolean
value that determines whether multiple routes will be returned when steps—This property sets or returns an array of MKRouteStep objects
available. that describe every step the user needs to take to reach the
destination.
transportType—This property sets or returns a value that
determines the type of transportation used to travel the route. It is an advisoryNotices—This property sets or returns an array of strings
MKDirectionsTransportType structure with the properties automobile, walking,
with additional information that the user may need to travel the
transit, and any.
route, such as traffic jams or interruptions.
departureDate—This property sets or returns a Date structure that distance—This property sets or returns a CLLocationDistance value (a
determines the date of departure to help the system estimate the typealias of Double) with the route distance in meters.
better route. expectedTravelTime—This property sets or returns a TimeInterval
arrivalDate—This property sets or returns a Date structure that value with the expected travel time in seconds.
determines the date of arrival to help the system estimate the better
After the server returns the MKRoute object describing the route, we must
route.
present it to the user. There are different types of information we can
extract from these objects, but the most interesting is the geometry
provided by the polyline property, which allows us to draw the route on the From this method, we must return a renderer configured according to how
map. we want the layer to be drawn. The framework defines the
The Map Kit View displays the graphics in layers. There is a layer for the MKPolylineRenderer class (a subclass of a subclass of the MKOverlayRenderer
map, another for the labels and roads, and we can also add custom layers class) to create a renderer for a Polyline overlay.
to display our own graphics, including the route. The value returned by the
polyline property contains all the information required to create a layer that
MKPolylineRenderer(polyline: MKPolyline)—This initializer
we can add to the Map Kit View to draw the route. The MKMapView class
creates a renderer for a Polyline overlay.
offers several methods to add and remove layers. The following are the
most frequently used.
The MKPolylineRenderer class inherits from its superclasses a set of properties
we can use to configure the renderer. The following are the most
addOverlay(MKOverlay, level: MKOverlayLevel)—This method
frequently used.
adds a layer to the Map Kit View. The first argument is an object that
defines the layer, and the level argument is an enumeration of type
fillColor—This property sets or returns a UIColor object with the color
MKOverlayLevel that defines the level in which the layer will be placed.
to fill the path.
The possible values are aboveRoads (places the layer above roads but
below labels) and aboveLabels (places the layer above roads and labels). strokeColor—This property sets or returns a UIColor object with the
color of the stroke.
removeOverlay(MKOverlay)—This method removes a layer from
the Map Kit View. lineWidth—This property sets or returns a CGFloat value that
determines the path's width.
Adding the layer to the Map Kit View is like adding an annotation. As with
annotations, the Map Kit View calls a delegate method to get the The route’s origin and destination are set with MKMapItem objects (the
information necessary to draw the layer. The following is the method same type of objects we receive when we perform a search). The class
defined in the MKMapViewDelegate protocol for this purpose. includes the following initializer to create an MKMapItem object from an
MKPlacemark object.
mapView(MKMapView, rendererFor: MKOverlay)—This
method is called on the delegate when the Map Kit View needs a MKMapItem(placemark: MKPlacemark)—This initializer creates
renderer to draw a layer. The method must return an object of the an MKMapItem object with the information provided by the argument.
MKOverlayRenderer class (or any of its subclasses) with the renderer we The placemark argument is an object with the information about the
want to use to render the graphics. The rendererFor argument is a place (location, name, etc.).
reference to the object that represents the layer we want to draw.
The MKPlacemark class offers its own initializer to create the MKPlacemark
object we need to initialize the MKMapItem object. The code in Listing 20-14 defines two properties (origin and destination) to
store the information for the route’s starting point and destination. When
MKPlacemark(coordinate: CLLocationCoordinate2D)—This the scene is loaded, these values are initialized with the coordinates of two
initializer creates an MKPlacemark object with the coordinates specified places in New York (the Apple Store and Grand Central Terminal) and the
by the coordinate argument. visible region is set around the Apple Store area. For this example, we
decided to get the route when the Show button is pressed. The following
The first step to create a route is to define the MKMapItem objects for the are the Action for the button and the asynchronous method we need to
starting point and destination, as we do in the following example. calculate the route.
Listing 20-14: Setting the route’s starting point and destination Listing 20-15: Calculating the route
 
import UIKit @IBAction func showLocation(_ sender: UIBarButtonItem) {
import MapKit let request = MKDirections.Request()
request.source = origin
class ViewController: UIViewController, MKMapViewDelegate { request.destination = destination
@IBOutlet weak var mapView: MKMapView! request.requestsAlternateRoutes = false
var origin: MKMapItem!
var destination: MKMapItem! Task(priority: .high) {
await calculateRoute(request: request)
override func viewDidLoad() { }
super.viewDidLoad() }
func calculateRoute(request: MKDirections.Request) async {
let coordOrigin = CLLocationCoordinate2D(latitude: 40.7637825011971, longitude: let directions = MKDirections(request: request)
-73.9731328627541) do {
let placeOrigin = MKPlacemark(coordinate: coordOrigin) let results = try await directions.calculate()
origin = MKMapItem(placemark: placeOrigin) await MainActor.run {
let routes = results.routes
let coordDestination = CLLocationCoordinate2D(latitude: 40.7523809365088, longitude: let route = routes.first!
-73.9778321046893) self.mapView.addOverlay(route.polyline, level: .aboveRoads)
let placeDestination = MKPlacemark(coordinate: coordDestination) }
destination = MKMapItem(placemark: placeDestination) } catch {
print("Error: \(error)")
let region = MKCoordinateRegion(center: coordOrigin, latitudinalMeters: 2000, }
longitudinalMeters: 2000) }
mapView.setRegion(region, animated: false) 
mapView.delegate = self
}
} The showLocation() method creates a request, assigns the location values to

the origin and destination properties, and then runs an asynchronous task to
perform the request. The asynchronous method creates the MKDirections
object to process the request and then calls the calculate() method to get the
route. This method returns the results in an MKDirectionsResponse object,
which contains the routes property that returns the MKRoute objects with the
routes. Because we set the requestsAlternateRoutes property to false, the array
returned by this property only contains one route object, so we get the
first element and add its Polyline overlay to the map.
The next step is to implement the MKMapViewDelegate protocol method to
provide the renderer for the Polyline overlays.
Because we centered the visible area at the Apple Store, the user may not
be able to see the entire route unless the map is zoomed out or scrolled.
There are several things we can do to improve our application, but it
always depends on what we want to achieve and how the application is
organized. One alternative is to define the annotations for the starting
point and destination and then call the showAnnotations() method provided by
the Map Kit View to set a visible region that includes both annotations. should see the entire route on the screen, from the Apple Store to
This way, not only will the entire route be visible, but we also make sure Grand Central Terminal.
that the user can identify the places where the trip begins and ends.
There are two more methods defined in the MKMapViewDelegate protocol
Listing 20-17: Zooming out to show the route that could be useful when working with routes and destinations.

func calculateRoute(request: MKDirections.Request) async {
let directions = MKDirections(request: request) mapView(MKMapView, didSelect: MKAnnotationView)—This
do { method is called on the delegate when an annotation view is selected.
let results = try await directions.calculate()
await MainActor.run { mapView(MKMapView, didDeselect: MKAnnotationView)—
let routes = results.routes
let route = routes.first!
This method is called on the delegate when an annotation view is
self.mapView.addOverlay(route.polyline, level: .aboveRoads) deselected.
let annotation1 = MKPointAnnotation()
annotation1.coordinate = self.origin.placemark.coordinate Using these methods, we can draw the route to the destination selected by
annotation1.title = "Apple Store"
the user. The following example finds coffee shops nearby the Apple Store
self.mapView.addAnnotation(annotation1)
in New York City and implements the mapView(MKMapView, didSelect:) method
let annotation2 = MKPointAnnotation() to get the route between the Apple Store and the selected coffee shop.
annotation2.coordinate = self.destination.placemark.coordinate
annotation2.title = "Grand Central Terminal"
self.mapView.addAnnotation(annotation2) Listing 20-18: Calculating routes to the destinations selected by the user

self.mapView.showAnnotations([annotation1, annotation2], animated: true)
} import UIKit
} catch { import MapKit
print("Error: \(error)")
} class ViewController: UIViewController, MKMapViewDelegate {
} @IBOutlet weak var mapView: MKMapView!
var appleCoord: CLLocationCoordinate2D!

var route: MKRoute?
In the method of Listing 20-17, the annotations are added to the map override func viewDidLoad() {
super.viewDidLoad()
when results are received from the server. Because of this, the annotations appleCoord = CLLocationCoordinate2D(latitude: 40.7637825011971, longitude:
and the route are displayed at the same time and the map zooms in or out -73.9731328627541)
to show the entire route on the screen. let region = MKCoordinateRegion(center: appleCoord, latitudinalMeters: 2000,
longitudinalMeters: 2000)
mapView.setRegion(region, animated: false)
Do It Yourself: Update the calculateRoute() method with the code in mapView.delegate = self
Listing 20-17. Run the application and press the Show button. You Task(priority: .high) {
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = "Coffee" func calculateRoute(request: MKDirections.Request) async {
request.region = region let directions = MKDirections(request: request)
do {
await searchCoffeePlaces(request: request) let results = try await directions.calculate()
} await MainActor.run {
} let routes = results.routes
func searchCoffeePlaces(request: MKLocalSearch.Request) async { if let oldRoute = self.route {
let search = MKLocalSearch(request: request) self.mapView.removeOverlay(oldRoute.polyline)
do { self.route = nil
let results = try await search.start() }
await MainActor.run { self.route = routes.first!
let items = results.mapItems self.mapView.addOverlay(self.route!.polyline, level: .aboveRoads)
for item in items { }
if let coordinates = item.placemark.location?.coordinate { } catch {
let annotation = MKPointAnnotation() print("Error: \(error)")
annotation.coordinate = coordinates }
annotation.title = item.name }
annotation.subtitle = item.phoneNumber func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) ->
self.mapView.addAnnotation(annotation) MKOverlayRenderer {
} let render = MKPolylineRenderer(overlay: overlay)
} render.strokeColor = UIColor.red
} return render
} catch { }
print("Error: \(error)") }
} 
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if let destinationCoord = view.annotation?.coordinate {
The code in Listing 20-18 does not introduce anything new. We search for
let placeOrigin = MKPlacemark(coordinate: appleCoord) places related to the word "Coffee" around the Apple Store, create
let origin = MKMapItem(placemark: placeOrigin) annotations for every place found, and add them to the map. When the
origin.name = "Apple Store"
user selects a pin, the delegate method is executed, and we use the pin’s
let placeDestination = MKPlacemark(coordinate: destinationCoord) location to get the route between the Apple Store and the selected place.
let destination = MKMapItem(placemark: placeDestination)
destination.name = view.annotation?.title! Because the user can select a different place after a route was added to the
map, we store a reference to the route in the route property to be able to
let request = MKDirections.Request()
remove the overlay later.
request.source = origin
request.destination = destination
request.transportType = .walking Do It Yourself: Update the ViewController class with the code in
request.requestsAlternateRoutes = false
Listing 20-18. Remember to disconnect the Show button in the
Task(priority: .high) { Storyboard if you do not implement it anymore. Run the application
await calculateRoute(request: request)
} and tap on a pin. You should see the route from the Apple Store to
}
}
the selected place.
Drag and drop is a way to visually move data from one application to Drag refers to the action of dragging an element in our app to another app
another or from two windows of the same application. It is available on (or to another part of our app). For instance, we may have an application
devices that can share the screen with two or more windows, like iPads that shows pictures to the users, and we want to let them drag a picture to
and Mac computers. In Mac computers, the process is simple. We open the Photo Library to make it available to the rest of the applications.
two or more windows at the same time and use the mouse to drag an A drag operation is defined as an interaction between the user and the
element from one window to the other. On iPads, we must split the screen scene. The Drag and Drop framework defines the UIDragInteraction class to
in two. iPads include an icon with three dots at the top that we can tap to create it.
share the screen with other applications, as shown below.
UIDragInteraction(delegate: UIDragInteractionDelegate)—This
Figure 21-1: Tool to split the screen on iPads initializer creates an interaction to manage a drag operation. The
delegate argument is a reference to the object that is going to
respond to the interaction.
 The interaction must be assigned to the view we want the user to be able
to drag. The UIView class includes the following methods to add and remove
When we tap on the three dots, the system shows a small window with interactions.
three icons (Figure 21-1, right). The first icon assigns the whole screen to
the app, the second icon splits the screen in two to show the current app addInteraction(UIInteraction)—This method adds the interaction
on the left and an additional app on the right, and the third icon moves the
to the view.
app to an overlay that is displayed on top of other apps. If we tap on the
second or third icons, the screen will be shared by two apps, so we can removeInteraction(UIInteraction)—This method removes the
drag and drop elements between them. interaction from the view.
Drag and drop is performed on elements on the screen, but the data to be
transferred is determined from code. For this purpose, the Drag and Drop
framework defines the UIDragInteractionDelegate protocol. When the user
drags an element, the system calls methods define by this protocol to get
the data and respond to changes in the state of the operation. The
following are the most frequently used.
When a drag operation is performed, the system creates a session to Among others, the NSItemProvider class includes the following methods to
manage it. Every delegate method receives a reference to this session that retrieve the object.
we can use to access the data. The session is created from a class that
conforms to two protocols: UIDragDropSession and UIDragSession. These canLoadObject(ofClass: Type)—This method returns a Boolean
protocols define the following properties and methods to process the data. value that determines if the object in the item provider can be
converted to an object of the class specified by the ofClass argument.
localContext—This property returns the additional object associated loadObject(ofClass: Type, completionHandler: Closure)—This
to the item. method loads the object in the item provider, converts it to the data
canLoadObjects(ofClass: Type)—This method returns a Boolean type specified by the ofClass argument, and calls the closure specified
value that indicates whether the elements in the session can be by the completionHandler argument when the process is over. The
converted to the data type specified by the ofClass argument. closure receives an array of NSItemProviderReading with the results.
Item providers require the objects that contain the data to conform to the dragImage.image = UIImage(named: "husky")
dragImage.isUserInteractionEnabled = true
NSItemProviderWriting protocol, which defines properties and methods to dragImage.clipsToBounds = false
specify the data and its type. But this is usually not necessary. The most
let drag = UIDragInteraction(delegate: self)
common elements users drag are images and text, and classes like UIImage dragImage.addInteraction(drag)
and NSString already conform to this protocol. }
Once we have the item provider with the object we want to transfer, we func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session:
UIDragSession) -> [UIDragItem] {
must prepare it to be used in a drag operation. The framework defines the if let image = UIImage(named: "husky") {
UIDragItem class for this purpose. let item = NSItemProvider(object: image)
let dragItem = UIDragItem(itemProvider: item)
return [dragItem]
UIDragItem(itemProvider: NSItemProvider)—This initializer }
return []
creates an item to send in a drag operation. The itemProvider }
argument is the object containing the data. }

itemProvider—This property returns the NSItemProvider object
associated with the item. This example assumes that we have an Image View in our scene connected
localObject—This property sets or returns an additional object to with an Outlet called dragImage. Using this Outlet, we assign the picture of a
husky to the view and the value true to the isUserInteractionEnabled property to
associate with the item. It is useful when we need to read the content
allow the user to interact with it. When the drag begins, the system shows
of the item from multiple methods.
an expanded image to indicate to the user that it is ready to initiate the
operation. If the image extends outside the view's boundaries, it may be
Applications that allow drag and drop must include an element on the
clipped. To avoid this, we assign the value true to the clipsToBounds property,
screen for the user to drag or an element for the user to drop other
so the image is always displayed in full.
elements. For our example, we are going to use an Image View at the top
Once the element to be dragged is ready, we configure the drag operation.
of the screen. The following view controller implements the minimum code
The first step is to create the UIDragInteraction object and add it to the Image
necessary to assign an image to the Image View and allow the user to drag
View. This object declares the view controller as the delegate, so we can
it to another application.
implement the dragInteraction(UIDragInteraction, itemsForBeginning:) method of
the UIDragInteractionDelegate protocol to provide the data. In this method, we
Listing 21-1: Allowing the user to drag an image
recreate the UIImage object with the picture of the husky, create the

NSItemProvider object with this image, and include it in a UIDragItem object.
import UIKit
Although this time we send only a single item, we can return multiple
class ViewController: UIViewController, UIDragInteractionDelegate { values and let the app on the other side decide which one to use.
@IBOutlet weak var dragImage: UIImageView!
The user can now drag the image of the husky to any other application.
override func viewDidLoad() { Figure 21-2, below, shows what we see when the app is opened in a split
super.viewDidLoad()
screen along with the Photo Library. If we drag the husky to the app on the the user is dragging. For this purpose, the UIDragInteractionDelegate protocol
right, the picture is added to the Photo Library. includes the dragInteraction(UIDragInteraction, previewForLifting:, session:) method.
The preview is defined by two objects: a UIDragPreviewTarget object to
Figure 21-2: Drag and drop operation determine for which view we are creating the preview, and a
UITargetedDragPreview object to define the preview with the new view. The
UIDragPreviewTarget class is a subclass of the UIPreviewTarget class, which
includes the following initializer.
Do It Yourself: Add the method in Listing 21-2 to the ViewController types of operations are defined by a UIDropOperation enumeration
class. Run the application, split the screen, open the Photo Library, value. The values available are cancel, forbidden, copy, and move.
and drag the picture. You should see the picture of the husky being dropInteraction(UIDropInteraction, performDrop:
dragged, with no white area around it. UIDropSession)—This method is called on the delegate when the
user performs the drop and the items are ready.
UIDropSession)—This method is called on the delegate when the let drop = UIDropInteraction(delegate: self)
user drags the element outside the area occupied by the view. dropImage.addInteraction(drop)
}
dropInteraction(UIDropInteraction, sessionDidEnd: func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -
> Bool {
UIDropSession)—This method is called on the delegate when the let valid = session.canLoadObjects(ofClass: UIImage.self)
operation is over. return valid
}
func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session:
The session for a drop operation is created from an object that conforms to UIDropSession) -> UIDropProposal {
return UIDropProposal(operation: .copy)
the UIDropSession protocol. The protocol defines the following method to get
}
the data. func dropInteraction(_ interaction: UIDropInteraction, performDrop session:
UIDropSession) {
session.loadObjects(ofClass: UIImage.self) { results in
loadObjects(ofClass: Type, completion: Closure)—This method if let list = results as? [UIImage], let image = list.first {
gets the objects in the session that can be converted to the data type self.dropImage.image = image
}
specified by the ofClass argument. The completion argument is a }
closure that is executed to process the result. The closure receives an }
}
array of NSItemProviderReading value that we must cast to the original 
data types.
As always, we first define the interaction and add it to the element on the
The drop session also conforms to the UIDragDropSession protocol, so we can interface that is going to respond to the operation. If the user drags an
implement the canLoadObjects(ofClass:) method to know if the session element over the view, the system calls the dropInteraction(UIDropInteraction,
sessionDidUpdate session: UIDropSession) method to know what kind of operation
contains a value we can process, and then call the loadObjects(ofClass: Type,
completion: Closure) method to load the data. For instance, the following
is admitted by the view. In this case, all we need is to copy the image (we
example allows the user to drag and drop an image from an external app to don't need the image to be removed at the origin), so we return a
UIDropProposal object with the value copy. If the user drops the element on
an Image View in the interface.
the view, the system first calls the dropInteraction(UIDropInteraction, canHandle:
UIDropSession) method to know if the view can handle the values being
Listing 21-3: Dropping images in an Image View

dropped. In this method, we check if the value can be casted as a UIImage
import UIKit object with the canLoadObjects(ofClass:) method and return the value
produced by the method (true if it was successful). If the value dropped by you enter the area of the Image View, the background color should
the user is valid, the system then calls the dropInteraction(UIDropInteraction, change to Teal (green). If you drag the element outside the area of
performDrop:) method to allow us to process the data. In this method, we get the view, the background should go back to normal.
the object with the loadObjects(ofClass:) method, cast it to a UIImage object,
and assign it to the Image View in the interface.
Lists Listing 21-5: Defining the model to test drag and drop

 import UIKit
enum Sections {
Any view can be dragged or receive a drop, but the process requires a few case main
more steps for Table and Collection Views. These views are containers for }
struct ItemsData: Identifiable {
one or more views, so the system needs to know which views are going to
var id: UUID = UUID()
participate in the process and where to place the view that is being var image: UIImage!
dropped. For this reason, the UIKit framework defines specific classes and }
struct ApplicationData {
protocols to control drag and drop operations performed on these var dataSource: UICollectionViewDiffableDataSource<Sections, ItemsData.ID>!
elements. There are different versions for Table Views and Collection Views var items: [ItemsData] = []
but they all work the same way. For instance, the framework defines the init() {
UICollectionViewDragDelegate protocol to control a drag operation for Collection items.append(ItemsData(image: UIImage(named: "bagels")))
Views. The following are some of the methods defined by this protocol. items.append(ItemsData(image: UIImage(named: "brownies")))
items.append(ItemsData(image: UIImage(named: "butter")))
items.append(ItemsData(image: UIImage(named: "cheese")))
collectionView(UICollectionView, itemsForBeginning: items.append(ItemsData(image: UIImage(named: "coffee")))
items.append(ItemsData(image: UIImage(named: "cookies")))
UIDragSession, at: IndexPath)—This method is called on the items.append(ItemsData(image: UIImage(named: "donuts")))
items.append(ItemsData(image: UIImage(named: "granola")))
delegate to get the objects to transfer to the destination. The method items.append(ItemsData(image: UIImage(named: "juice")))
must return an array of UIDragItem objects. items.append(ItemsData(image: UIImage(named: "lemonade")))
items.append(ItemsData(image: UIImage(named: "lettuce")))
collectionView(UICollectionView, items.append(ItemsData(image: UIImage(named: "milk")))
items.append(ItemsData(image: UIImage(named: "oatmeal")))
dragPreviewParametersForItemAt: IndexPath)—This method is items.append(ItemsData(image: UIImage(named: "potato")))
called on the delegate to know which part of the cell should be shown items.append(ItemsData(image: UIImage(named: "tomato")))
items.append(ItemsData(image: UIImage(named: "yogurt")))
in the preview. The method receives the cell's index path and must
}
return a UIDragPreviewParameters object that determines the parameters }
var AppData = ApplicationData()
of the preview. The class includes the properties backgroundColor and

visiblePath to define the background color or clip the cell.
We also need a cell to show the images in the Collection View. This cell is
The process to drag a cell from a Collection View to another application is like the one we used before in the examples of Chapter 11, with the
like the one we used before for a single Image View, but we need a difference that now we must enable user interaction for the image and the
Collection View Controller and a model that provides the images to display. cell's content view to allow the user to perform a drag and drop operation.
The following is the model for this example.
Listing 21-6: Defining the cell to test drag and drop
 super.viewDidLoad()
import UIKit collectionView.dragDelegate = self
prepareDataSource()
class FoodCell: UICollectionViewCell { prepareSnapshot()
let picture = UIImageView() }
func prepareDataSource() {
override init(frame: CGRect) { let cellRegistration = UICollectionView.CellRegistration<FoodCell, ItemsData.ID> { cell,
super.init(frame: frame) indexPath, itemID in
picture.translatesAutoresizingMaskIntoConstraints = false if let item = AppData.items.first(where: { $0.id == itemID }) {
picture.contentMode = .scaleAspectFit cell.picture.image = item.image
picture.isUserInteractionEnabled = true }
}
self.contentView.addSubview(picture) AppData.dataSource = UICollectionViewDiffableDataSource<Sections, ItemsData.ID>
self.contentView.isUserInteractionEnabled = true (collectionView: collectionView) { (collection, indexPath, itemID) in
return collection.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item:
picture.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 8).isActive = true itemID)
picture.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -8).isActive }
= true }
picture.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 8).isActive func prepareSnapshot() {
= true var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
picture.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -8).isActive snapshot.appendSections([.main])
= true snapshot.appendItems(AppData.items.map({ $0.id }))
} AppData.dataSource.apply(snapshot)
required init?(coder: NSCoder) { }
fatalError("Error") func collectionView(_ collectionView: UICollectionView, itemsForBeginning session:
} UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
} let row = indexPath.row
 if let image = AppData.items[row].image {
let item = NSItemProvider(object: image)
let dragItem = UIDragItem(itemProvider: item)
The purpose of this app is to allow the user to drag the thumbnail in a cell return [dragItem]
to another app (e.g., the Photo Library), so we must implement the two }
return []
delegate methods introduced before to provide the data to be transferred }
and define the area of the cell used to create the preview. To designate the func collectionView(_ collectionView: UICollectionView, dragPreviewParametersForItemAt
indexPath: IndexPath) -> UIDragPreviewParameters? {
view controller as the delegate, we assign self to the dragDelegate property let cell = collectionView.cellForItem(at: indexPath) as! FoodCell
let imageFrame = cell.picture.frame
provided by the UICollectionView class, as in the following example.
let preview = UIDragPreviewParameters()
preview.visiblePath = UIBezierPath(rect: imageFrame)
Listing 21-7: Defining a Collection View for drag and drop return preview
}
 }
import UIKit 
The process is the same as before, the only difference is in how we instruct application, split the screen, open the Photo Library, and drag one of
the system to create the preview. What we need to do in our example is to the cells. You should see something like Figure 21-3.
determine the path around the area occupied by the image in the cell, so
only the thumbnail is used to create the preview. The UIDragPreviewParameters Of course, we can also drop elements in the Collection View. UIKit defines
class includes the visiblePath property to specify this path, which takes a the UICollectionViewDropDelegate protocol to control the process in a Collection
value of type UIBezierPath. This is a class defined in the UIKit framework to View. The following are some of the methods define by the protocol.
create paths based on coordinate values. One of the values taken by this
class is a CGRect structure, so we can use it to create a path from the frame collectionView(UICollectionView, canHandle: UIDropSession)
of the image, as we did in this example. Once the parameters are returned, —This method is called on the delegate to know if the Collection View
the user can drag the pictures in the Collection View to other applications. can receive the elements dragged by the user. The method must
return a Boolean value to report the result.
Figure 21-3: Dragging a cell from a Collection View collectionView(UICollectionView, dropSessionDidUpdate:
UIDropSession, withDestinationIndexPath: IndexPath?)—This
method is called on the delegate when the user drags an element
inside the Collection View. The method must return a
UICollectionViewDropProposal object to determine the type of operations
allowed. The types of operations are defined by a UIDropOperation
enumeration value. The values available are cancel, forbidden, copy, and
 move.
Do It Yourself: Create a new Project. Replace the scene with a collectionView(UICollectionView, performDropWith:
Collection View Controller. Click on the Collection View and set the
UICollectionViewDropCoordinator)—This method is called on the
delegate when the user performs the drop and the items are ready.
Estimate Size to None from the Size Inspector panel (see Chapter 11,
Figure 11-7). Download the thumbnails from our website and add The method receives an object that contains the items dragged by the
them to the Assets Catalog. Create a Swift file called user and the location in the Collection View where the items were
ApplicationData.swift for the model in Listing 21-5. Create a subclass dropped.
of UICollectionViewCell with the name FoodCell. Update this class with collectionView(UICollectionView, dropSessionDidEnter:
the code in Listing 21-6. Create a subclass of UICollectionViewController UIDropSession)—This method is called on the delegate when the
called DragViewController and assign it to the Collection View user drags the element inside the area occupied by the view.
Controller. Update this class with the code in Listing 21-7. Run the collectionView(UICollectionView, dropSessionDidExit:
UIDropSession)—This method is called on the delegate when the
user drags the element outside the area occupied by the view. Listing 21-8: Dropping elements in a Collection View

import UIKit
When an item is dropped, the system calls the collectionView(UICollectionView,
performDropWith:) method with a coordinator object that provides all the class DropViewController: UICollectionViewController, UICollectionViewDropDelegate {
information required to process the item. This coordinator is created from override func viewDidLoad() {
super.viewDidLoad()
an object that conforms to the UICollectionViewDropCoordinator protocol, which collectionView.dropDelegate = self
defines the following properties. prepareDataSource()
prepareSnapshot()
}
items—This property returns an array of objects that conform to the func prepareDataSource() {
let cellRegistration = UICollectionView.CellRegistration<FoodCell, ItemsData.ID> { cell,
UICollectionViewDropItemprotocol and contain the values of the item and indexPath, itemID in
its previous location (in case it was dragged from the same Collection if let item = AppData.items.first(where: { $0.id == itemID }) {
cell.picture.image = item.image
View). }
}
destinationIndexPath—This property returns an IndexPath structure AppData.dataSource = UICollectionViewDiffableDataSource<Sections, ItemsData.ID>
with the location of the cell where the items were dropped. (collectionView: collectionView) { (collection, indexPath, itemID) in
return collection.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item:
itemID)
The items are represented by an object that conforms to the }
}
UICollectionViewDropItem protocol. The protocol defines the following
func prepareSnapshot() {
properties to return information about the items. var snapshot = NSDiffableDataSourceSnapshot<Sections, ItemsData.ID>()
snapshot.appendSections([.main])
snapshot.appendItems(AppData.items.map({ $0.id }))
dragItem—This property returns a UIDragItem object with the item AppData.dataSource.apply(snapshot)
dropped by the user. }
func collectionView(_ collectionView: UICollectionView, canHandle session: UIDropSession) -
previewSize—This property returns a CGSize structure with the size > Bool {
let valid = session.canLoadObjects(ofClass: UIImage.self)
of the preview. return valid
}
sourceIndexPath—This property returns an IndexPath structure with func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session:
the location of the cell that was dragged by the user. UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) ->
UICollectionViewDropProposal {
let proposal = UICollectionViewDropProposal(operation: .copy)
Again, the implementation is like what we would use for a normal drop return proposal
}
operation, but with the protocol methods defined specifically for Collection func collectionView(_ collectionView: UICollectionView, performDropWith coordinator:
Views. The following example allows the user to drop an image in the UICollectionViewDropCoordinator) {
Collection View and adds an item to the model with it. for item in coordinator.items {
let provider = item.dragItem.itemProvider
provider.loadObject(ofClass: UIImage.self) { result, error in
if error == nil {
if let image = result as? UIImage {
self.addImage(image: image)
}
} else {
print("Error: \(error!)")
}
}
}
}
@MainActor func addImage(image: UIImage) {
if let thumbnail = image.preparingThumbnail(of: CGSize(width: 80, height: 100)) {
let newItem = ItemsData(image: thumbnail)
AppData.items.append(newItem)
prepareSnapshot()
}
}
}

Mac Apps the Scheme called My Mac that allows us to run the application on the
computer.

Figure 22-3: My Mac option
The first step to turn our iPad app into a Mac app is to select the option
from the app's settings. The option is in the General panel, inside the

Deployment info section, as shown below.
In addition to this option, Xcode performs other changes to get the app to
work on the Mac. Something we will notice right away is a new option in 
The Storyboard can show the scenes with a Mac layout. The option is when we click on the + button next to the value we want to change (Figure
available along with the iPad configurations, as shown below. 22-7, number 1). In the example below, we assign a value of 50 points to
the constraint when the button is shown on a Mac (Figure 22-7, number 2).
Figure 22-5: Mac configuration for the Storyboard
Figure 22-7: Values for Mac
When we click on the Mac option (Figure 22-5, number 1), the popup
window is expanded to include two more options to configure the 
interface to match the iPad or to be optimized for Mac. For instance, the
figure below shows how a Filled button looks on an interface that matches
the iPad interface (left) and when it is optimized for Mac (right).
Conditional Code Mac" is shown on the console, otherwise the message "Prints in iOS" is
shown instead.

As we will see later, these conditionals are useful when some of the
implemented classes are not supported by one system or the other, but we
Although the system is responsible for converting UIKit code into AppKit
can also detect the system in code and implement an if else statement when
code, more often than not we must define specific code for one platform
the code in question is platform independent. As we have seen in Chapter
or another. For this purpose, the Swift compiler supports conditionals.
6, the UITraitCollection class includes the userInterfaceIdiom property for this
These are not normal if else statements, they are conditionals that are
purpose.
checked before the code is compiled and, therefore, we can use them to
View controllers include the traitCollection property to return the
select the code we want to compile according to the target device.
UITraitCollection object that defines the way the view is presented. From this
Conditionals are built using the keywords #if, #else, and #endif. The #if and
property, we can check the platform and execute code accordingly.
#else keywords work like the Swift conditionals if else, but because the code
is not within a block, the #endif keyword is required to indicate the end. Listing 22-2: Detecting the platform when the app is running
There are several parameters we can use to set the condition, but to detect 
whether the application is being compiled for the Mac, we can use the import UIKit
targetEnvironment() instruction and the value macCatalyst, as in the following
class ViewController: UIViewController {
example. override func viewDidLoad() {
super.viewDidLoad()
if traitCollection.userInterfaceIdiom == .mac {
Listing 22-1: Detecting the platform before compiling print("Prints in Mac")
 } else {
print("Prints in iOS")
import UIKit }
}
class ViewController: UIViewController { }
override func viewDidLoad() { 
super.viewDidLoad()
#if targetEnvironment(macCatalyst)
print("Prints in Mac")
#else
print("Prints in iOS")
#endif
}
}

This is a very simple example but illustrates how to work with these types
of conditionals. If we run this project on the Mac, the message "Prints in
Menu

Something that iPad apps don't have is a menu bar, which is a required
feature for Mac applications. To create a menu, we can add the Main Menu
option to the Storyboard.

Figure 22-8: Main Menu option in the Library
The bar is organized hierarchically. There is an element at the top
representing the main menu, elements that represent each menu of the
bar, such as File or Format, then elements inside that represent groups of

options with similar characteristics, such as New Scene or Open Recent,
and finally the elements that represent the options the user can select,
The Main Menu option adds a menu bar to the Storyboard with system
such as the New option in the menu of Figure 22-10 (number 1). To delete
predefined options that provide basic functionality to the app.
a menu or an option, we just select it and press the Delete button, but if
we want to insert a new option, we must consider whether the option is
Figure 22-9: Predefined menu
related to any of the available options, or it belongs to a separate group.
For instance, if we want to insert an option that is related to the New
option in the File menu, we can drag the Menu Command option from the
Library to the New Scene group.

Figure 22-11: Menu option
We can double-click on a menu to see the options, select a menu or an
option and press the Delete button to remove it, or edit the menus from
the Document Outline panel.

Figure 22-10: Menu options in the Document Outline panel
The New Scene group in the File menu will now contain two options, New
and Item, and the change will be reflected in the Storyboard.
 Dragging this option inside a menu in the Storyboard adds a group with
two options. The group is called Inline Menu, and the options are called
Item 1 and Item 2.
In this example, we have removed a few options from the File menu and
added a new one. The option is added with the title Item and no key
Figure 22-15: New inline section
associated to it, but we can change these values from the Storyboard or
from the Attributes Inspector panel. The panel shows the values of every
single attribute, including the combination of keys that perform the action.
In the example below, we assign the title "Show Alert" to the option and
the keys Command + S, so the user can perform the action by selecting the
option from the menu or by pressing those keys.
There is also an option called Sub Menu to add more menus to the bar.


If our new option is not related to any of the options in the menu, we can
create a new group. For this purpose, the Library includes an option called
If this option is dropped on the menu bar, it adds a new menu (Figure 22-
Inline Section Menu.
17, left), but when we drop it between the options of a menu, it adds a
submenu with more options inside (Figure 22-17, right).
Figure 22-14: Inline section option
Figure 22-17: New submenu

Document Outline panel and put it inside the New Scene group in state—This property sets or returns the option's state (indicated by a
the File menu (or create a new group with the Inline Section Menu checkmark on the left-hand side of the title). It is an enumeration of
option). You should have a new option highlighted in blue (Figure 22- type State with the values on, off, and mixed.
12). Select the option and change the title and the associated keys
from the Attributes Inspector panel (Figure 22-13). Update the Every time an option is about to be displayed on the screen the menu calls
ViewController class with the code in Listing 22-3. Control-drag a line the following method in the view controller that performs the action to
from the new option to the icon that represents the First Responder validate it.
and select the Action in your view controller (Figures 22-18 and 22-
19). Run the application on your Mac (My Mac scheme). Select the
validate(_ command: UICommand)—This method is called on the
responder (the view controller) to validate the command. It receives a
option from the menu or press the associated keys. You should see
reference to the UICommand object that represents the option.
an Alert View popping up over the window.
action—This property sets or returns the selector that performs the @IBAction func showAlertView(_ sender: Any?) {
action. let alert = UIAlertController(title: "Show Alert", message: "Open from menu", preferredStyle:
.alert)
attributes—This property sets or returns the option's attribute. It is a let action = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(action)
structure of type Attributes defined by the UIMenuElement class. The present(alert, animated: true, completion: {
structure includes three type properties to configure the option: self.alertDisplayed = true
})
destructive, disabled, and hidden. }
override func validate(_ command: UICommand) {
let action = command.action
switch action { value of type UIMenuBuilder to provide access to the menu and options
case #selector(showAlertView):
if alertDisplayed { for configuration.
command.attributes = .disabled
}
default: When the app is launched, this method is called on the AppDelegate class and
break this is our chance to customize the menu bar. The method receives a value
}
} of type UIMenuBuilder, which is a protocol with all the methods we need.
} The following are the most frequently used.

In this example, we include a Boolean property to check whether the Alert insertChild(UIMenu, atStartOfMenu: Identifier)—This method
View was already opened or not. When we open the File menu, the inserts an option or a group of options at the beginning of the menu
validate() method is called for every option. Here, we check that the option is indicated by the atStartOfMenu argument.
the one that performs the showAlertView() method and if the Alert View was insertChild(UIMenu, atEndOfMenu: Identifier)—This method
already shown, we deactivate the option assigning the value disabled to the inserts an option or a group of options at the end of the menu
attributes property. Next time we open the File menu, the option is grayed
indicated by the atEndOfMenu argument.
out.
insertSibling(UIMenu, beforeMenu: Identifier)—This method
Do It Yourself: Update the ViewController class with the code in inserts an option or a group of options before the option indicated by
Listing 22-4. Run the application, open the File menu, and click on the beforeMenu argument.
the Show Alert option. You should see the Alert View on the screen. insertSibling(UIMenu, afterMenu: Identifier)—This method
Close the Alert View and open the File menu again. The Show Alert inserts an option or a group of options after the option indicated by
option should be disabled this time. the afterMenu argument.
remove(menu: Identifier)—This method removes the option or
From the validate() method we can modify all the attributes of an option, but group of options indicated by the menu argument.
we can't remove or add options to the menu. This is a limitation we have
when the menu is added to the Storyboard. If we need to modify the
There are two types of menus, the main menu at the top of the screen,
options available while the app is running, we must create the menu from
and contextual menus that open when the user interacts with a view. To
code. The option is again provided by the UIResponder class, which defines
identify the system we are working with, the UIMenuBuilder protocol
the following method for this purpose.
includes the system property. This property returns a UIMenuSystem object
buildMenu(with: UIMenuBuilder)—This method is executed by with information about the menu. The class includes two type properties
the application before the menu bar is created. The method receives a to represent the menu system and two methods to process the menu.
main—This type property returns a reference to the main menu UICommand(title: String, image: UIImage?, action: Selector,
system (the menu bar). propertyList: Any?, alternates: [UICommandAlternate],
context—This type property returns a reference to the context menu discoverabilityTitle: String?, attributes: Attributes, state:
system. State)—This initializer creates a menu option. The title argument is
the option's title. The image argument is the image associated with
setNeedsRebuild()—This method tells the menu system to rebuild
the option. The action argument is a selector with the action we want
all the menus.
to perform when the option is selected. The propertyList argument is
setNeedsRevalidate()—This method tells the menu system that the the data we want to associate with the option. The alternates
menus need to be revalidated. It is used to update the menus when
argument is an array of alternative options. The discoverabilityTitle
the state of the app changes.
argument is a string that describes the purpose of the option. The
attributes argument is a structure of type Attributes with properties to
The system creates a standard menu, as the one we added to the
specify the style of the option (destructive, disabled, and hidden). And
Storyboard before, that we can edit from code. For this purpose, the
finally, the state argument is a State enumeration that specifies the
framework defines the UIMenu class. The class includes the following
option's initial state. The values available are on, off, and mixed.
initializer.
The framework includes a subclass of UICommand called UIKeyCommand to
UIMenu(title: String, image: UIImage?, identifier: Identifier?,
define options that can be selected with a combination of keys.
options: Options, children: [UIMenuElement])—This initializer
creates a UIMenu object to represent a menu or an option. The title
UIKeyCommand(title: String, image: UIImage?, action:
argument is the title of the element. The image argument is the
Selector, input: String, modifierFlags: UIKeyModifierFlags,
image associated with the element. The identifier argument is the
propertyList: Any?, alternates: [UICommandAlternate],
value we want to use to identify the element. The options argument
discoverabilityTitle: String?, attributes: Attributes, state:
is a structure that determines the type of element this object
State)—This initializer creates an option with a shortcut key. The title,
represents. An empty array indicates that the element is a menu (or a
image, action, propertyList, alternates, discoverabilityTitle,
submenu), and the displayInline property indicates that the element is a
attributes, and state arguments are the same defined for the
menu option. Finally, the children argument is an array with the
UICommand initializer. The input argument is the textual representation
elements we want to add.
of the key the user must press to perform the action, and the
modifierFlags argument is a property of a structure of type
The options are still defined by the UICommand class. The class includes the
UIKeyModifierFlags that determines the modifier key the user must press
following initializer to create them from code.
to perform the action. The properties available are alphaShift, shift, Listing 22-5: Adding a menu to the menu bar
control, alternate, command, and numericPad. 
import UIKit
The options are identified with a structure of type Identifier defined by the @main
UIMenu class. The structure provides the following initializer to create a class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
custom identifier. [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
UIMenu.Identifier(String)—This initializer creates an identifier for func application(_ application: UIApplication, configurationForConnecting
a menu option. The argument must be unique and therefore Apple connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) ->
UISceneConfiguration {
recommends declaring it with a reverse domain, such as return UISceneConfiguration(name: "Default Configuration", sessionRole:
com.formasterminds-myapp.MyMenuBar.myOption. connectingSceneSession.role)
}
override func buildMenu(with builder: UIMenuBuilder) {
The Identifier structure also includes type properties used to identify super.buildMenu(with: builder)
standard options (those included in the menu by default). The properties if builder.system == .main {
available are application, file, edit, view, window, help, about, preferences, services, hide, let option = UICommand(title: "Show Alert", action:
#selector(ViewController.showAlertView(_:)))
quit, newScene, close, print, undoRedo, standardEdit, find, replace, share, textStyle, let mymenu = UIMenu(title: "Alerts", identifier:
spelling, spellingPanel, spellingOptions, substitutions, substitutionsPanel, UIMenu.Identifier("com.formasterminds.test.alerts"), options: [], children: [option])
builder.insertSibling(mymenu, afterMenu: .file)
substitutionOptions, transformations, speech, lookup, learn, format, font, textSize, }
}
textColor, textStylePasteboard, text, writingDirection, alignment, toolbar, fullscreen,
}
minimizeAndZoom, bringAllToFront, and root. 
To customize the menu bar, we must override the buildMenu(builder: To create a new menu, we specify the title of the UIMenu object, declare the
UIMenuBuilder) method in the AppDelegate class. This method is called when options argument with an empty array (or ignore it), and then assign the
the application is about to create the menu, so we can customize it before options we want to include in the menu to the children argument. In this
it is shown to the user. example, the menu's title is "Alerts", and an option is defined by a
The method may be called to configure the main menu or contextual UICommand object with the title "Show Alert". The menu can be added as a
menus, so we must first check that we have received a reference to the new menu to the menu bar or as a submenu. This is determined by the
menu we want to modify and then proceed to add or remove the elements method we use to add the menu and the identifier. In this case, we use the
we want. The following example adds a menu called Selection to the main insertSibling(UIMenu, afterMenu:) method to add the menu to the main menu
menu, next to the standard File menu. next to the File menu (the file identifier represents the File menu).
Notice that the action is defined with a selector (#selector) that calls the already defined in the Storyboard). Update the AppDelegate class with
showAlertView() method in our view controller. This is possible because the the code in Listing 22-5 and the ViewController class with the code in
selector looks for the method in the responder chain, which includes all Listing 22-6. Run the application. You should see the Alerts menu
the view controllers opened by the user. In this case, the selector asks the next to the File menu, and the option should perform the same
system to search for the showAlertView() method in the responder chain, the action as before.
method is found in the ViewController object, and executed, showing an Alert
View as before. IMPORTANT: In this example, we take advantage of the responder
For the method to be found by the selector in our view controller, we need chain to execute a method in the view controller, but we could have
to turn the @IBAction into an @objc method, as shown next. defined the action inside the AppDelegate class and then post
notifications to the view controllers or modify the model. There are
Listing 22-6: Defining the action in the view controller different programming patterns available. For more information, visit
 our website and follow the links for this chapter.
import UIKit
Of course, we can also add the option to an existent menu or remove some
class ViewController: UIViewController { of the standard menus and options included by the system, as we did
@objc func showAlertView(_ sender: Any) {
let alert = UIAlertController(title: "Show Alert", message: "Open from menu", preferredStyle: before from the Storyboard. Next, we show how to add the Show Alert
.alert) option to the File menu and how to remove elements.
let action = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(action)
present(alert, animated: true, completion: nil) Listing 22-7: Modifying the main menu
} 
}
 import UIKit
@main
The main menu now includes an additional menu called Alerts with an class AppDelegate: UIResponder, UIApplicationDelegate {
option called Show Alert that opens an Alert View when pressed. func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
Figure 22-20: New menu added from code }
func application(_ application: UIApplication, configurationForConnecting
connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) ->
UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole:
 connectingSceneSession.role)
}
override func buildMenu(with builder: UIMenuBuilder) {
Do It Yourself: Remove the main menu added before to the super.buildMenu(with: builder)
Storyboard (the buildMenu(with:) method is not called if a menu was if builder.system == .main {
let option = UIKeyCommand(title: "Show Alert", image: UIImage(systemName:
"trash"), action: #selector(ViewController.showAlertView(_:)), input: "S", modifierFlags:
[.command]) Listing 22-8: Defining a model to store the state of the menu
let mymenu = UIMenu(title: "", identifier: 
UIMenu.Identifier("com.formasterminds.test.showalert"), options: .displayInline, children: [option])
import Foundation
builder.insertSibling(mymenu, beforeMenu: .close)
struct ApplicationData {
builder.remove(menu: .edit)
var includeMenu: Bool = true
builder.remove(menu: .format)
}
}
var AppData = ApplicationData()
}

}

Now we can check the value of the includeMenu property in the
In this example, the Show Alert option is created with the UIKeyCommand buildMenu(with: UIMenuBuilder) method to include an option only when the
class, so we can assign a combination of keys to the option (Command + S). value is true.
The File menu created by the system includes an option identified by the
close value. Using this reference, we insert our option at the top of the Listing 22-9: Defining the menu according to the current state
menu and then remove the Edit and Format menus. The result is shown 
@main
Figure 22-21: New option in the File menu class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?) -> Bool {
return true
}
func application(_ application: UIApplication, configurationForConnecting
 connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) ->
UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole:
The option in the last example includes an image. If we need to modify this connectingSceneSession.role)
image, or any other attribute, we can implement the validate() method on }
override func buildMenu(with builder: UIMenuBuilder) {
the view controller, as we did before (see Listing 22-4), but if we want to super.buildMenu(with: builder)
modify the menu itself, we must ask the system to rebuild it with the
if builder.system == .main {
setNeedsRebuild() method. This means storing the state of the menu in a if AppData.includeMenu {
let option = UICommand(title: "Show Alert", action:
model and rebuilding it every time the state changes. As an example, we #selector(ViewController.showAlertView(_:)))
are going to implement a simple model with just one property to let mymenu = UIMenu(title: "", identifier:
UIMenu.Identifier("com.formasterminds.test.showalert"), options: .displayInline, children: [option])
determine if an option should be included or not. builder.insertChild(mymenu, atStartOfMenu: .file)
}
} in Listing 22-9 and the ViewController class with the code in Listing 22-
}
} 10. Add a button to the interface and connect it to the activateOption()
 method in the view controller. Run the application. You should see
the Show Alert option in the File menu. Press the button on the
In the view controller, we need to add an action to toggle the value of the
interface and open the File menu again. The option should be gone.
includeMenu property when a button on the interface is pressed.
This example assumes that we have added a button to the interface and it
is connected to an action called activateOption(). When the button is pressed,
this method toggles the value of the includeMenu property and calls the
setNeedsRebuild() method on the main menu. This forces the system to
recreate the menu and the option is removed or added according to the
property's current value.
In this example, we create an instance of the MyToolbar class and assign the Listing 22-13: Assigning a custom title to the toolbar
object to the delegate property of the NSToolbar object, so the toolbar can 
call the methods in this delegate object when it needs the items to show override func viewDidAppear(_ animated: Bool) {
on the screen. After the delegate is defined, we configure the bar to only super.viewDidAppear(animated)
show icons (iconOnly), and then assign the bar to the Scene. The result is #if targetEnvironment(macCatalyst)
if let scene = view.window?.windowScene, let bar = scene.titlebar {
shown below. let toolbar = NSToolbar(identifier: "main")
toolbar.delegate = toolbarDelegate
toolbar.displayMode = .iconOnly
Figure 22-22: Mac toolbar bar.toolbar = toolbar
scene.title = "My App"
}
#endif
}
 
Do It Yourself: Create a new project. Configure the project to work When the toolbar is displayed on the screen, the system calls a method in
on Macs from the app's settings. Create a new file with a subclass of the view controller to make sure that the actions can be performed. The
NSObject called MyToolbar and complete the file with the code in following is the method defined in the UIResponder class for this purpose.
Listing 22-11. Update the ViewController class with the code in Listing
22-12. Run the application on the Mac. You should see a toolbar with canPerformAction(Selector, withSender: Any?)—This method is
two buttons, as illustrated in Figure 22-22. Press the buttons. A called in the view controller when the toolbar items are presented on
message should be printed on the console. In this example, we just
the screen. The first argument is a reference to the item's action, and disabled and the image replaced.
the withSender argument is a reference to the item itself.
The items are identified by the action they performed. For instance, we can
disable the action that executes the openFirstItem() method, as shown next.
This method is similar to the validate() method we use to modify the options
of a menu. We can not only implement it to disable an item but also to
change it. For instance, the following method disables the first item and
replaces the image of the trash can with another one to reflect a new
state.
Listing 22-16: Adding a hover gesture recognizer The implementation is simple. We create the tooltip with the message we
 want to show and add it to the view. In the following example, we add the
import UIKit tooltip to the main view.
Views
Do It Yourself: Update the ViewController class with the code in

Listing 22-17. Run the application. Move the mouse over the window
and wait for a second. You should see a message popping up with There are a series of small changes we may introduce to a scene to
the text "This is the view". improve the interface for Mac computers. For instance, the
UISplitViewController class includes the following properties to set up the
IMPORTANT: The UIControl class includes the toolTip property to minimum and maximum width of the primary column.
define a tooltip. If your view inherits from the UIControl class (e.g.,
UIButton), you do not need to create a UIToolTipInteraction object. All minimumPrimaryColumnWidth—This property sets or returns
you have to do is to assign the message to the toolTip property and the minimum width of the primary column in points.
the tool is created for you. maximumPrimaryColumnWidth—This property sets or returns
the maximum width of the primary column in points.
The UILabel class also includes a property useful for Mac applications.
Classes like UIButton and UISlider also include a property that may be helpful
when the application is running on a Mac.
preferredBehavioralStyle—This property sets or returns a value code makes sure that the switch is represented by a checkbox and assigns
that determines the style of the control. It is an enumeration of type the label "Option" to it.
UIBehavioralStyle with the values automatic (default), pad, and mac. If we
want the control to be shown with the style used for iPads, we must Figure 22-23: Switch on the Mac
assign the value pad to this property.
Another element that provides customization options for the Mac is the
Switch. By default, the switch created by the UISwitch class is represented 
by a checkbox on Mac computers, but we can define the style with the
following property.
If we decide to go with a checkbox, we can also specify a label with the title
property.
This example assumes that we have added a switch to the interface, and it
is connected to the ViewController class with an Outlet called myswitch. The
22.2 Multiple Windows In addition to the options provided by the system, we can open the app in
 a new window by creating a Scene from code. The UIApplication class offers
the following methods to manage Scenes.
iPads and Mac computers can open multiple instances of an application in
separate windows (Scenes), but this feature is not enabled by default. The requestSceneSessionActivation(UISceneSession?,
option to support multiple windows is available from the General panel in userActivity: NSUserActivity?, options:
the project's settings, as illustrate below. ActivationRequestOptions?, errorHandler: Closure?)—This
method activates an existing Scene or creates a new one. The first
Figure 22-24: Support Multiple Windows option argument is a reference to the Scene session we want to activate or
the value nil if we want to create a new one. The userActivity
argument is a reference to an object of type NSUserActivity that stores
the state we want to set when the Scene is opened. The options
argument is an object with information for the session associated
 with the Scene. And the errorHandler argument is the closure to be
executed if an error occurs.
Activating this option allows the application to work from multiple requestSceneSessionDestruction(UISceneSession, options:
windows. iPads and Mac computers provide built-in functionality for this UISceneDestructionRequestOptions?, errorHandler: Closure?)
purpose. For instance, if we compile the app to work on the Mac, an option —This method dismisses an existing Scene session. The first argument
appears on the File menu to create a new window. is a reference to the session we want to destroy, the options
argument provides information on how to remove the Scene, and the
Figure 22-25: Option to create new window
errorHandler argument is the closure to be execute if an error occurs.
requestSceneSessionRefresh(UISceneSession)—This method
requests the system to update the views in the Scene specified by the
argument.

If all we want is to open a new window, we can just call the
requestSceneSessionActivation() method with nil values, as in the following
iPads offer multiple alternatives to open the app in a new window. The
easiest way is to split the screen with the bottoms at the top and open the example.
app again (see Chapter 21, Figure 21-1). If the app allows multiple
windows, two instances of the same app will share the screen. Listing 22-19: Opening a new window

import UIKit NSUserActivity(activityType: String)—This initializer creates a
class ViewController: UIViewController {
new NSUserActivity object with the name specified by the argument.
@IBAction func openNewScene(_ sender: UIButton) { The activityType argument is a string to identify the state.
let app = UIApplication.shared
app.requestSceneSessionActivation(nil, userActivity: nil, options: nil)
} The following are some of the properties and methods provided by the
} class.

This example assumes that we have a button on the interface connected to title—This property sets or returns the activity's title.
the ViewController class with an action called openNewScene(). When the button userInfo—This property sets or returns a dictionary with the values
is pressed, we get a reference to the UIApplication object assigned to the app required to set or restore the state represented by the object.
and call the requestSceneSessionActivation() method on it to open a new window. becomeCurrent()—This method marks the activity as the one that
is currently in use.
Do It Yourself: Create a new project. Go to the app's settings, resignCurrent()—This method deactivates the activity.
configure the project to work on Macs, and check the Support
invalidate()—This method invalidates the activity.
multiple windows option to be able to open the app in multiple
windows (Figure 22-24). Add a button to the interface and connect it
How the state is restored depends on the characteristics of our application,
to the ViewController class with an action called openNewScene().
but the process to store the information that represents the state is always
Complete the class with the code in Listing 22-19 and run the
the same. We must create an NSUserActivity object, store the information we
application on your Mac. Click on the button to open the app in a
are going to need to set the session in the userInfo property, and then use
new window.
that information to configure the state of the session when a previous
session or a new one is created.
The window opened by the example in Listing 22-19 launches an instance To illustrate how this works, we are going to create a simple application
of the app with the configuration by default. This means that the app will with a Pop Up button to select a picture, an Image View to display the
display the initial screen as if it had been launched for the first time. But selected picture, and a button to open a new window.
this is not always appropriate. Users often expect the window to be in the
state it was before the app was closed. To set the initial state of the session Figure 22-26: Interface to test multiple windows on Mac
or recover an old state, the Foundation framework defines the NSUserActivity
class. From this class, we can create objects that contain the information
required to set the state of the session or restore a previous one.
import UIKit
To store the list of pictures, we need a model with at least two properties, var listOptions: [UIAction] = []
for (index, item) in AppData.picturesList.enumerated() {
one to store the array with the names and another to store the index of listOptions.append(UIAction(title: item.capitalized, identifier:
the selected picture, as shown next. UIAction.Identifier(String(index)), handler: selectOption))
}
myButton.menu = UIMenu(children: listOptions)
Listing 22-20: Defining the model to test multiple windows }
 func initializePicture() {
let id = AppData.selectedPicture
import Foundation
let action = myButton.menu?.children[id] as? UIAction
struct ApplicationData { action?.state = .on
var picturesList: [String]
var selectedPicture: Int myPicture.image = UIImage(named: AppData.picturesList[id])
}
init() { func selectOption(action: UIAction) {
picturesList = ["bagels", "brownies", "butter", "cheese", "coffee", "cookies", "donuts", "granola", if let id = Int(action.identifier.rawValue) {
"juice", "lemonade", "lettuce", "milk", "oatmeal", "potato", "tomato", "yogurt"] AppData.selectedPicture = id
selectedPicture = 0 myPicture.image = UIImage(named: AppData.picturesList[id])
} }
} }
var AppData = ApplicationData() @IBAction func openNewWindow(_ sender: UIButton) {
 let activity = NSUserActivity(activityType: "com.formasterminds.images")
activity.userInfo?["selected"] = AppData.selectedPicture
In the view controller, we must feed the Pop Up button with all the values
let app = UIApplication.shared
in the model and initialize the interface with the first value on the list. app.requestSceneSessionActivation(nil, userActivity: activity, options: nil)
}
}
Listing 22-21: Storing the state in an NSUserActivity object 

To configure the interface, we have defined two methods: configureButton() }
}
and initializePicture(). In the configureButton() method, we configure the Pop Up }
button with the pictures' names. First, we assign the value true to the }

changesSelectionAsPrimaryAction and showsMenuAsPrimaryAction properties to make
sure that the button is rendered as a Pop Up button and the name of the There are different ways to read the current activities set by the
selected picture is displayed on the title. And then we populate the button application. In this example, we get them from the userActivities property of
with the list of pictures using a for in loop. On the other hand, the the ConnectionOptions object received by the method. This property returns
initializePicture() method gets the index of the selected picture from the an array of NSUserActivity objects, so we get the first one that matches the
model and updates the Pop Up button and the Image View with it, so the type used by the view controller and then read the value from its userInfo
selected picture is always shown on the screen. property with the "selected" key. The key returns an Int value that we
At the end of the view controller of Listing 22-21, there is an action for the assign to the selectedPicture property in the model, so the currently selected
Open Window button. When the button is pressed, this method creates an picture is shown on the new window.
NSUserActivity object, sets a value in the userInfo dictionary with the key
"selected" and the index of the selected picture, and executes the Do It Yourself: Remove all the elements on the interface and the
requestSceneSessionActivation() method on the UIApplication object with this code in your view controller. Add a Pop Up button, an Image View,
object to open the window. and a button with the title Open Window to the scene, as in Figure
To set the state of the Scene and get the new window to show the right 22-26. Connect the Pop Up button to the ViewController class with an
image, we must read back the value stored in the userInfo property when Outlet called myButton and the Image View with an Outlet called
the Scene is created by the SceneDelegate object, as shown next. myPicture. Connect the Open Windows button with an Action called
openNewWindow(). Complete the ViewController class with the code in
Listing 22-22: Reading the state from the NSUserActivity object Listing 22-21. Create a Swift file called ApplicationData.swift for the

model in Listing 22-20. Update the SceneDelegate class with the code in
import UIKit
Listing 22-22. Download the thumbnails from our website and add
class SceneDelegate: UIResponder, UIWindowSceneDelegate { them to the Assets Catalog. Run the application on the Mac or an
var window: UIWindow?
iPad, select a picture, and click on the Open Window button.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: Another window should open with the same picture selected before.
UIScene.ConnectionOptions) {
guard let _ = (scene as? UIWindowScene) else { return }
IMPORTANT: In this example, we barely scratched the surface of
if let activity = connectionOptions.userActivities.first(where: { $0.activityType ==
"com.formasterminds.images" }) {
what is possible to do when working with NSUserActivity objects and
if let index = activity.userInfo?["selected"] as? Int { multiple windows. The topic is extensive and goes beyond the scope
AppData.selectedPicture = index
of this book. For more information, visit our website and follow the
links for this chapter.
CHAPTER 23 - APP STORE
23.1 Publishing Apple Developer Program
 
At the beginning of this book, we talked about Apple’s strict control over Developing and testing can be done with a free account, but publishing our
the applications to which the users have access. Applications for Mac app requires a membership to the Apple Developer Program. The option to
computers can be sold separately, but mobile applications can only be sold enroll in this program is available on the developer.apple.com website. We
in the App Store. The process is performed from Xcode, but there are a must click on the Discover/Program options at the top of the screen, press
series of requirements we need to satisfy for our app to be published and the Enroll button, and follow the instructions to register an account for an
become available to the users. Individual or an organization. At the time of writing, the membership costs
USD 99 per year.
We need an Apple Developer Program membership.
We need a Distribution Certificate.
We need a Provisioning Profile for distribution.
We need an App ID for each application.
We must register the app in the App Store Connect website.
We must create an archive with our app for each platform to send
to Apple’s servers.
We must upload the archive to App Store Connect for review.
Certificates, Provisioning Profiles, and Identifiers a list of options to select the type of values we want to work with, and the
right panel shows the list of values available and buttons to create new

ones. When a value is selected, a new panel opens with tools to edit it.
Apple wants to make sure that only authorized apps are running on its
devices, so it requests developers to add a cryptographic signature to each
application. There are three values that are necessary to authorize the app:
certificates, provisioning profiles, and identifiers. Basically, a certificate
identifies the developer that publishes the application, the provisioning
profile identifies the device that is allowed to run the application, and an
identifier, called App ID, identifies the application. These values are packed
along with the application’s files and therefore Apple always knows who
developed the app, who is authorized to run it, and in which devices.
Xcode automatically generates these values for us, so we do not have to
worry about them, but Apple offers a control panel in our developer
account in case we need to do it manually (the option is not available for
free members). Figure 23-1 shows the menu we see after we go to
developer.apple.com, click on Account, and select the option Certificates,
IDs & Profiles.
The first step to submit our app is to create a record on Apple’s servers.
Apple has designated a website for this purpose, available at
appstoreconnect.apple.com. To login, we must use the same Apple ID and
password we use to access our account at developer.apple.com. Figure 23-
2 illustrates the options available after logging in.

Figure 23-2: App Store Connect menu
To add a new app, we must select the New App option and insert the app’s
information. The first window asks for the platform we have developed the
app for (in our case, iOS and macOS), the application’s name, the primary
language, the bundle ID, and a custom ID (SKU) that can help us identify
the app later. The name and language are values we already have, and the
SKU is a custom string, but the Bundle ID is a value generate by Xcode.
Xcode creates a Bundle ID and submits it to Apple servers when we enable
services from the capabilities panel. If our app does not use any of these
services, we can register a new Bundle ID from developer.apple.com (see
Figure 23-1), as explained in the form.

Figure 23-4: Bundle ID and SKU identifier
From this panel, we can insert our financial information (Agreements, Tax,
and Banking), publish our apps (My Apps), and see how the business is
going (Sales and Trends). The first step is to create a record for the app we
want to publish from the My Apps option. When we click on this icon, a
new window shows the list of our apps and a + button at the top to add
more.
After these values are inserted, we can press the Create button and Submitting the Application
complete the rest of the information. This includes the app’s description,

screenshots, and personal information. We also must select the option
Pricing and Availability on the left panel to set the price and where the
The application and its resources must be compiled for each platform in a
application will be available. Once all the information is provided, we can
single archive and then submitted to App Store Connect. We must create
finally press the Save button and go back to Xcode to upload the files.
an archive for iOS devices and another for macOS. The option is available
on the Xcode’s Product menu, but it is only enabled when the appropriate
device is selected on the Schemes. We can select a real device connected
to the computer, or we can use the Any iOS Device option for iOS apps or
the Any Mac option for macOS.
After we click on this option, Xcode compiles the application and creates
the archive. The next window shows the archive and offers buttons to
validate and submit the app.

Figure 23-6 shows an archive created for an application called Test (number All the options are recommended. The first one tells Xcode to include code
1). The item representing the archive includes the date it was created, the that improves the app’s performance, and the second uploads the
app’s version, and the number of the build (we can send multiple builds to necessary information for Apple to be able to report errors and perform
App Store Connect and later decide which one we want to be reviewed by diagnostics.
Apple). The next window allows us to select how we want to sign the app. With
automatic signing, we let Xcode take care of everything for us
IMPORTANT: The app’s version and the number of the build (recommended).
(archive) are determined from the app’s settings editor (by default,
both values are set to 1.0). If we want to specify a different version, Figure 23-8: Option to select automatic signing
we must declare the numbers separated by one or two periods (e.g.,
1.0 or 1.2.5). The values represent different revisions of our app,
with the order of relevance from left to right. The actual meaning of
the value is arbitrary, but we are required to change it every time an
update is published to the App Store to reflect how big the update
was.
J.D Gauchat
www.jdgauchat.com

After the archive is selected, we can press the Save button to save the
app’s description. If all the required information was provided, we can
finally press the Submit for Review button at the top of the page to submit
the application. The system asks a few questions and then the application
is sent for review (the message Waiting for Review is shown below the
app’s title).
The process takes a few days to be completed. If everything is correct, and
the app is accepted, Apple sends us an email to let us know that the app
has become available in the App Store.