Swift Programming The Ultimate Beginner's Guide To Learn Swift Programming Step by Step, 3
Swift Programming The Ultimate Beginner's Guide To Learn Swift Programming Step by Step, 3
Table of contents
Introduction
About Swift
About the book
Chapter 5. Tuples
5 .1. Basic information about tuples
5 .2. Interacting with elements of a tuple
Chapter 6 . Optional data types
6 .1. Optionals
6 .2. Retrieving an optional value
Chapter 7. Assertions
Chapter 11 . Functions
11 .1. Function declaration
11 .2. Input parameters and return value
11 .3. Function body as value
11 .4. Nested functions
11 .5. Reloading functions
11 .6. Recursive function call
Chapter 12 . Closures
12 .1. Functions as closures
12 .2. Closing expressions
12 .3. Implicit return value
12 .4. Abbreviated parameter names
12 .5. Closure variables
12 .6. Capturing variables
12 .7. Array sorting method
Chapter 14 . Enumerations
14 .1. Enumeration syntax
14 .2. Associated parameters
14 .3. Switch statement for enumerations
14 .4. The associated member values of the enumeration
14 .5. Properties in enumerations
14 .6. Methods in enumerations
14 .7. Operator self
14 .8. Recursive enums
Chapter 15. Structures
15 .1. Syntax for declaring structures
15 .2. Properties in structures
15 .3. Structure as a namespace
15 .4. Custom initializers
15 .5. Methods in structures
Chapter 17 . Properties
17 .1. Property types.
17 .2. Control of receiving and setting values.
17 .3. Type properties.
Chapter 18 . Subscripts
18 .1. Appointment of subscripts.
18 .2. Subscript syntax
Chapter 19 . Inheritance
19 .1. Inheritance syntax
19 .2. Overriding inherited elements
19 .3. The preemptive final modifier
19 .4. Substitution of class instances
19 .5. Cast
Chapter 24 . Extensions
24 .1. Computed properties in extensions.
24 .2. Extension initializers.
24 .3. Methods in extensions.
24 .4. Subscripts in extensions.
Chapter 25 . Protocols
25 .1. Required properties
25 .2. Required methods
25 .3. Required initializers
25 .4. Protocol as data type
25 .5. Extension and protocols
25 .6. Protocol inheritance
25 .7. Class protocols
25 .8. Composition of protocols
Swift, many developers went exactly along this path, realizing that in the
future Apple's greatest attention will devote to the development of a new
language. Moreover, Swift became the first open source development by
Apple, which means that
implementation of its support and other operating systems (and not
iOS and OS X only) . 2
1
Swift differs significantly from Objective-C upwards
convenience of programming. However, on rare occasions when developing
programs you may need to use inserts written in
Objective-C.
2
Currently, Swift applications can be developed not only for
operating systems iOS and OS X, but also for watchOS (operating system
Smart watch Apple Watch) and tvOS (television operating system
4th generation Apple TV boxes). However, learning development techniques
applications for different operating systems are out of scope
of this book.
About Swift
Swift is fast, modern, secure and user-friendly.
gramming. With its help, the process of creating programs becomes very
flexible and productive, as Swift has incorporated the best from languages
like C, Objective-C and Java. Swift is extremely convenient to study,
perceive and read the code. It has extremely promising
a vivid future.
Learning this wonderful language, you will be surprised how connected it is
with Xcode (development environment, we will dwell on it later) excel- gives
other programming languages in which you wrote programs we are earlier.
Its simplicity, brevity and incredible possibilities are simply amazing!
The Swift language was created completely "from scratch", therefore it has a
number of features bennities:
❑ Object oriented .
Swift is an object-oriented programming language designed for
adhering to the paradigm "everything is an object". If the present
moment this statement seemed incomprehensible to you, did not
live: a little later we will return to it.
❑ Readability , economy and conciseness of the code .
Swift is simply designed to be easy to use and easy to use.
easy to understand. It has a simple and transparent syntax,
allowing you to shorten the multi-line code that you can
but, wrote in the past, to one-lines (and in some cases -
single character!) expressions.
❑ Security .
Within Swift, the developers tried to create a modern
a language that is free from vulnerabilities and does not require excessive
knowledge straining the programmer when creating applications. Swift has
strongly typed: at any given time, you know for sure
those with what type of object you are working with. Moreover, when
creating
applications, you practically do not need to think about consumable
RAM, Swift does everything for you automatically
mode.
❑ Performance .
Swift is a very young language, however, in terms of performance
of the developed programs, it is approaching (and in some
cases already overtakes) the well-known "old man" - the language of pro
grammar C ++ . These features make Swift a truly amazing language.
1
programming. Now is the time for you to dive into the world Swift: he is still
very, very young, people with significant luggage knowledge and experience
behind shoulders simply does not exist due to age
language, so in the future you can become one of them.
Legend
NOTE This block contains notes and notes.
CAUTION This is what important material notes look like.
Listing
And these are code examples (listings)
SYNTAX
In such blocks, syntactic constructions are given with an explanation of the options
their use.
The task
You will find tasks to complete in separate sections.
Part I
Introducing Xcode
In the first part, you will learn what you need to start developing
Swift applications. You will get your own custom account
employee, get acquainted with the application development environment called
Xcode, and learn the first and most important interaction skills
with her .
NOTE If you are unable to meet all the requirements of this
parts, do not despair. To get started learning Swift, you can use
use the special service http: // swiftstub .com /, which will allow you to write
Swift code directly in the browser and see the results of its execution there.
If you've never worked with Xcode before, you'll be amazed
the breadth of its capabilities and an unusual approach to the development of applications
ny (unlike many other programming environments). Naturally,
not all of these possibilities are covered in the book, so I advise
you in the future independently continue to study it
In case you already have experience with Xcode, you can skip
this chapter and go directly to learning the Swift language, although this
and not recommended.
✓ Chapter 1. The first steps
✓ Chapter 2. Xcode and Playground projects
1 First steps
eleven . You need a Mac computer
Before you start developing programs in Swift, you
it will take a few things. First you need a computer
iMac, MacBook, Mac mini, or Mac Pro with an operating
Mavericks (OS X 10.9), Yosemite (OS X 10.10), or El Capitan system
(OS X 10.11). This first and basic requirement is due to the fact that the
environment
Xcode application development is created by Apple exclusively
but with a focus on its own platform. Although due to the fact that
Swift is an open language, there may already be projects that
allowing the development of applications for other operating rooms
systems.
NOTE If you have a Windows computer and do not have a separate
computer with OS X, you should choose one of three ways to solve this
Problems:
- purchase a Mac with OS X installed;
- install OS X (or rather, Hackintosh) on your computer;
- install a virtual machine in Windows and deploy the finished image on it
OS X.
I note that the method with installing a virtual machine is the simplest
and acceptable, although in this case you will not achieve the same level of comfort
work as it did on the original Mac.
After clicking on the View in the Mac App Store link, an automatic
Cycle to the Mac App Store to a page
Xcode. Here you can see a summary of all the main development
environment features, an overview of the latest update and user calls. To
download Xcode just click on the Download button and, if necessary, enter your
account details
Apple ID records.
Once the installation process is complete, you should be able to find Xcode
in Launchpad or in the Applications folder in the Dock.
2 Xcode environment
and playground projects
2 .1. Introduction to Xcode
We will start learning programming in Swift with a development
environment.
Xcode work.
NOTE Integrated Development Environment
Environment, IDE) - a software system used by programmers
for the development of software (SW).
The development environment usually includes:
- text editor;
- compiler and / or interpreter;
- assembly automation tools;
- debugger.
Xcode is an IDE, that is, an environment for creating apps for iOS and OS X.
Xcode is the most important tool used to develop
worker. Xcode environment is amazing! It provides broad
possibilities, and they should be studied gradually, based on the
and emerging tasks. The external view of the working environment is shown
on
fig. 2.1.
It is with the use of this interface that any
iOS and OS X apps. As you learn Swift, you will be
operate with a different workspace - the play interface
ground projects. We will talk about it a little later.
Xcode is distributed free of charge. It is multifunctional
new application without any restrictions in its work. In Xcode
integrated iOS SDK, code editor, interface editor
sa, debugger, and more. It also has built-in simulators.
iPhone, iPad, Apple Watch and Apple TV. This means that all created
The Xcode environment, together with playground projects, gives you a truly
fun
tastic opportunities to realize your ideas!
It's time to move on to learning Swift. For this we will write Swift-
the code together, detailing all its capabilities and mechanisms.
Part II
Basic features
Swift
Swift is a very interesting language. If you have previously worked with other languages
programming, you will soon notice their resemblance to the brainchild
Apple. This chapter will introduce you to the basic concepts that
walk on successful programming, and teach the basics of syntax
and work with various fundamental mechanisms that formed
the basis of the entire system for developing programs in the Swift language.
Like many other languages, Swift makes extensive use of variables and con-
stants for storing values, and to access them are used to identify
cator (names). Moreover, Swift has erected the functionality of variables
and constants to a degree. And soon you will be convinced of this. In the course of reading a book
you will learn about many amazing innovations that have not been met
you in other languages, but which you may have complained about.
✓ Chapter 3. A starting point
✓ Chapter 4. Data types and operations with them
3 Starting point
Swift, like any other programming language, has its own
functions with the help of commands that give (or rather, write)
worker. A completed Swift command is called an expression .
A code file usually consists of a collection of expressions written
on a set of lines. You could see examples in the images,
demonstrating playground projects in Xcode.
Expressions, in turn, consist of operators, modifiers
and other expressions. The operator is the minimum autonomous unit
nice (word or group of words) that performs a specific command.
Modifiers are functional units that expand the scope of
operator capabilities. All reserved by programming language
These character sets are called keywords .
There are two important concepts in Swift: declaration and initialization.
zation.
❑ Declaration is the creation of a new object with which it is planned
interaction.
❑ Initialization is the assignment of a declared object to a specific
lone value.
For example, imagine that you have created (declared) some storage
nil with the name "Dragon Chest" (Fig. 3.1). At the moment this
the chest is empty, and no one knows what will be stored in it, not even the
dragon.
But it is known for sure that you can access this repository by
name, that is, among thousands of different storages, you can easily find
baby this chest.
Now the dragon has decided to transfer all his potions to the Dragon Chest.
stolen gold, or, in other words, decided to initialize
"Dragon Chest" with a certain value (Fig. 3.2). To then
to gain access to the stolen gold, the dragon does not need to use
to pump and collect it throughout the cave - it will be enough just to take
Dragon Chest and refer to its contents.
A large number of actions that you will program
in the future, will either define some object, or initialize
its value, or request this value. For example, for
requesting what is stored in the "Dragon Chest", you will be returned
meaning "Stolen gold".
Assignment operator
The assignment operator ( = ) is a special binary operator. They With-
uses in the generic expression a = b , initializing the value of the
EKTA a object value b . Referring to the previous example
with the "Dragon Chest", it was assigned the value "Stolen
gold ”(it was initialized with this value).
Remember that both the left and right sides of the assignment operator must
us to be of the same type (that is, to have the same type). Imagine
that in addition to the dragon's chest there is a "Troll Cauldron", in which
there is a yellow soup (fig. 3.3).
Based on the contents of both repositories, we can conclude that sun-
the duc is of the "Solid objects" type, as it is intended for storage
solid objects (for example, gold), and the cauldron, in its
queue has the type "Liquid", as it is intended exclusively
for storing liquids (for example, chowder).
The var statement is followed by the name of the variable to be created, with which
rogo will refer to the value written in the variable (or
with which this value will change). Further, after the name and opera-
the assignment torus, the value to be initialized to the variable follows.
Listing 3.4
var x = 0.1, y = 5
let a = 2, b = 7.1
Listing 3.6
Console :
Hello Dragon!
The above code produces the output of information passed to the function
the print () clause .
Note that the output to the console is duplicated is added in the output area,
but at the end it is added line feed character ( \ n ).
NOTE In Xcode 6 there were two functions for displaying information
to the console: println () and print (). The first added to the end of the displayed expression a sym-
a line break, and the second is not. In Xcode 7, the print () function has been deprecated.
and println () has been renamed to print (). From now on, any information output to the console
terminated with a newline character (\ n).
Listing 3.9
Console :
Knight swords
The created variable gragonsBox is passed to the print () function as
as an input argument (or input parameter), and its value
is also output to the console.
In addition, it is possible to combine the output of the text
information with the value of some parameter (or parameters).
To do this, in the required place in the text, you must use the symbol
backslash (slash) followed by parentheses
specify the name of the displayed parameter. An example is shown in Listing
3.10.
Listing 3.10
Console :
3 .5. Comments
3 .6. Semicolon
If you have any programming experience behind you, then,
perhaps accustomed to ending each line of code with a dot
with a comma ( ; ). If so, you probably noticed that in none of the
from the previous listings, this symbol is not used. In contrast
from many other programming languages, Swift does not require
semicolon at the end of the completed expression. The only use
the key is when you write on one line at once
several teams. An example is shown in Listing 3.12.
Listing 3.12
1 // one expression per line - no semicolon needed
2 var number = 18
3 // multiple expressions per line - semicolon needed
4 number = 55; print (number)
We have already met this code earlier. Let me remind you that it can be sold
read aloud as follows:
Declare a variable named gragonBox and assign a value to it
"Swords of the Knights" .
In this case, it is not additionally required to indicate the type of the created
variable: Swift parses the value being initialized and accepts
Decides on the type of a variable on its own. Now, knowing that
that there are data types, we can read the code in Listing 4.1
in the following way:
Declare a variable of string type named gragonBox and when-
assign it the string value "Swords of the Knights" .
Or another option:
Declare a variable of type String named gragonBox and prisvo-
give it the string value "Swords of the Knights" .
String is a keyword used in Swift to indicate
to a string data type. It allows you to store in variables
and constants text. Detailed techniques for working with this type of opening
branes further.
An operation in which Swift independently determines the type of the created
This parameter is called an implicit type inference .
When explicitly (direct) defining the type of a variable or
constant after its name separated by a colon, specify the required
a type. In this case, the value can be omitted, then it will be declared
an empty variable, that is, a variable containing an empty value.
Examples are shown in Listing 4.2.
Listing 4.2
NOTE Swift is a strongly typed language. Once having determined the data type
variable or constant, you cannot change it. At every moment
time, you should have a clear understanding of the types of values with which
your code works.
Listing 4.4
and UInt64 . They define the range of stored values: 8-, 16-, 32-
and 64-bit numbers.
NOTE All listed integer data types are different types
data (like the types "Liquid" and "Solid"), and the values in these types are not
can interact with each other directly. All operations must take place
dit between values of the same data type!
At the very beginning of the book, I talked about one of the features of Swift:
everything
in this programming language are objects. Object is
some abstract entity, implemented using software
we. For example, the object is the number 2 or the grocery machine.
Each entity can have a set of characteristics (called
properties) and programmed actions (called
methods). Each property and each method has a name that allows
access it. So, for example, at the object "grocery car
tomato "property can exist" capacity "and the method" self-
be destroyed. "
The properties and methods of objects in Swift are accessed by
by the power of their names, written with a dot after the name of the object,
example:
NOTE About why and for what brackets are added after the method name
ki, we'll talk later.
The task
The second kind of numbers that Swift can work with is pure
la floating point, that is, numbers that have a fractional
part. Examples are the numbers 3.14 and -192.884022.
In this case, a variety of data types capable of storing fractional
numbers, not as large as in the case of integer types.
To declare parameters that can store floating-point numbers
point, two keywords are used: Float and Double . Both
data types are signed.
Float is a 32-bit floating point number containing up to
6 numbers in fractional part.
Double is a 64-bit floating point number containing up to
15 numbers in fractional part.
An example of declaring parameters containing such numbers is given
in Listing 4.6.
Listing 4.6
The task
Arithmetic operators
Earlier we learned about data types that allow you to store numeric values.
values in variables and constants. With the numbers we store, you can but
carry out various arithmetic operations. Swift supports the same set of
operations as other programming languages.
Each arithmetic operation is performed using special operator. Here is a list
of the operations and operators available in Swift:
+ Binary addition operator adds the first and second operand-
d and returns the result of the operation ( a + b ). Result type
the values correspond to the type of the operands.
+ The unary plus operator is used as a prefix, i.e.
is placed before the operand ( + a ). Returns the value of the operand without
any changes. In practice, this operator is usually not
used.
- The binary subtraction operator subtracts the second operand from the first
th and returns the result of the operation ( a - b ). Result type
the values correspond to the type of the operands.
- The unary minus operator is used as a prefix, then
is is placed before the operand ( -a ). Inverts the operand and
rotates its new value.
NOTE The minus and plus symbols are used as names for two
operators each. This practice should be familiar to you even from classes in
mathematics.
1 // integer variables
2 var numOne = 19
19
3 // operation of increment postfix
4 numOne ++
19
5 numOne
20
6 // operation of increment by prefix
7 ++ numOne
21
8 numOne
21
Swift allows you to minimize the amount of code. And the deeper
you will comprehend this wonderful language, the more techniques,
able to make your life easier, you will find out. One of these techniques
is the combination of the arithmetic operator ( + , - , * , / ,
% ) and the assignment operator ( = ). Consider the example in Listing 4.13,
in which we create an integer variable and using the composition
new assignment operator, we will change its value using
minimum code.
Listing 4.13
Listing 4.14
The task
1 // 17 decimal
2 let deciamlInteger = 17
17
3 // 17 in binary
4 let binaryInteger = 0b10001
17
5 // 17 in octal
6 let octalInteger = 0o21
17
7 // 17 in hex
8 let hexadecimalInteger = 0x11
17
In the results area, you can see that each of the given numbers -
it's 17.
Floating point numbers can be decimal (no prefix)
or hexadecimal (prefixed with 0x ). Such numbers should
have the same form of entry (number system) on both sides
rones from the point.
Listing 4.16
1 // decimal
2 let deciamlDouble = 12.1875
12.1875
3 // decimal number with exponent
/ * matches expression
exponentDouble = 1.21875 * 101 * /
4 let exponentDouble = 1.21875e1
12.1875
5 // hexadecimal number with exponent
/ * matches expression
hexadecimalDouble = 0xC.3 * 20 * /
6 let hexadecimalDouble = 0xC.3p0
12.1875
Listing 4.17
Let's look at an example of working with the String data type (Listing 4.19).
Listing 4.19
Concatenating strings
If necessary, you can concatenate multiple lines into one,
longer. There are two mechanisms for this: interpolation
and concatenation.
When interpolation takes place association string literals
variables, constants and expressions in a single string literal
(Listing 4.24).
Listing 4.24
1 // variable of type String
2 var name = "Dragon"
"The Dragon"
The task
1. Declare a variable of type String and write a string to it.
free string literal.
2. Declare a constant of type Character containing the
free symbol of the Latin alphabet.
3. Declare two variables of type Int and write in them the
free values.
4. Concatenate into a string literal in one expression.
a String variable , a Character constant, and a sum
variables of type Int , and write the result into a new con-
stanta.
5. Print this constant to the console.
The study of fundamental data types does not end with number-
out and string types. There is a special boolean in Swift
a data type called Bool and capable of storing one of two
values: "true" or "false". The value "true" is indicated by
as true and false as false . Let's declare a variable and a constant
a boolean data type (Listing 4.28).
Listing 4.28
1 // boolean variable with implicitly specified type
2 var isDragon = true
true
3 // boolean constant with an explicit type
4 let isTroll: Bool = false
false
As with other data types in Swift, it is possible for Bool to be explicit and not
an explicit type definition, as seen in the example above.
NOTE Swift's strong typing prevents replacement of other datatypes.
in Bool, as you might have seen in other languages, where, for example, the lines i = 1
and i = true meant the same thing. In Xcode, this approach will throw an error.
Data type Bool commonly used when working with the operator if-
else ,
which depending on the value of the parameter passed to it
allows you to run the program on different branches
(Listing 4.29).
Listing 4.29
1 // boolean variable
2 var isDragon = true
true
3 // condition construction
4 if isDragon {
five
print ("Hello Dragon!")
"Hello Dragon! \ N"
6 } else {
7
print ("Hello, Troll!")
8}
Console :
Hello Dragon!
As you can see, the message "Hello Dragon!" Is displayed on the console.
while the second phrase is ignored by the program. Operator us-
Logical operators
Logical operators test the truth of any statement.
denia and return the corresponding boolean value. Swift
supports three standard logical operators:
❑ logical NOT ( ! A );
❑ logical AND ( a && b );
❑ logical OR ( a || b ).
The unary logical NOT operator is prefixed and written
This is the exclamation symbol. It returns an inverted log-
the physical value of the operand, that is, if the operand was true , then
will return false and vice versa. For the expression ! A, this operator can
be read as "not a " (Listing 4.30).
Listing 4.30
1 var someBool = true
true
2 // invert the value
3 ! SomeBool
false
The someBool variable is initially boolean true .
The boolean NOT operator returns the inverted
the value of the someBool variable . Moreover, the meaning in the change itself
noah does not change.
The binary operator of logical AND is written as doubled
ampersand character and is infix. It returns true when
yes both operands are true . If the value of at least one of
operands equals false , the return value is false (listing 4.31).
Listing 4.31
1 let firstBool = true, secondBool = true, thirdBool = false
2 // group different conditions
3 var one = firstBool && secondBool
true
4 var two = firstBool && thirdBool
false
5 var three firstBool && secondBool && thirdBool
False
The logical AND operator allows you to determine whether among the re-
the operands given to it are false values.
You can group an arbitrary number of operands by using
using the required number of operators.
Binary logical OR operator looks like a doubled character
straight line and is infix. It returns true when though
one of the operands is true . If the values of both oper-
rand equal to false , the return value to false (Listing 4.32).
Listing 4.32
Listing 4.33
When calculating the result of an expression, Swift evaluates the value of the
sub-
expressions sequentially, that is, first the first, then the second
etc.
In some cases, it is necessary to indicate in what order the
to practice logical operators. In Swift, you use
parentheses. Expressions enclosed in parentheses
are executed first (Listing 4.34).
Listing 4.34
The task
1. Declare two boolean variables. The meaning of the first
should be true, the second should be false.
2. Write down in two constants the result of using them as
as operands for logical AND and OR operators.
3. Output to the console the values of both resulting con-
stunt.
4. Evaluate the result of the following Boolean expressions.
That being said, try not to use Xcode:
((true && false) || true)
true && false && true || (true || false)
false || (false || true) && (true && false)
Listing 4.35
Listing 4.36
1 // define a pseudonym for the String type
2 typealias textType = String
3 typealias wordType = String
4 typealias charType = String
5 // create variables of each type
6 var someText: textType = "This is text"
"This is text"
7 var someWord: wordType = "Word"
"Word"
8 var someChar: charType = "B"
"B"
Listing 4.37
1 // declare an alias
2 typealias ageType = UInt8
3 / * use type property
4
UInt8 via its alias * /
5 var maxAge = ageType.max
255
For Swift, referring to an alias is equivalent to referring to itself
data type. An alias is a reference to a type. In this example,
uses the maxAge alias to access the UInt8 data type .
NOTE Remember that aliases can be used perfectly.
but for any type. And if these examples do not fully disclose
the need to use the typealias operator, then when studying tuples
(in the following sections) you will come across composite types containing from
two or more subtypes. Such composite types can also be accessed via
aliases.
The task
Listing 4.38
1 // Assertion "1 is greater than 2"
2 1> 2
false
3 // will return false as it is false
4 // Statement "2 is not equal to 2"
5 2! = 2
false
6 // will return false as it is false
7 // Statement "1 plus 1 is less than three"
8 (1 + 1) <3
true
9 // will return true as it is true
10 // Statement "5 is greater than or equal to 1"
11 5> = 1
true
12 // will return true as it is true
We will turn to the question of the scope of use of comparison operators
further.
This concludes the study of basic Swift features! The whole
the studied material will be actively used in the future,
starting from the next listing.
Part III
The main
Swift facilities
Of course, Swift's capabilities don't end there.
in the previous part with material. This was just the beginning. All material,
that has been learned prepared you for the next step: learning the basics
means of this programming language.
In this chapter, you will re-acquaint yourself with the already known to you in other languages
cams of programming constructs, and also learn completely new,
sometimes just amazing Swift tools that will wake you up
more interest!
✓ Chapter 5. Tuples
✓ Chapter 6. Optional data types
✓ Chapter 7. Assertions
✓ Chapter 8. Flow control. Branching
✓ Chapter 9. Collection types
✓ Chapter 10. Flow control. Repetitions
✓ Chapter 11. Functions
✓ Chapter 12. Closures
5 Tuples
You may have never seen such a thing in programming.
nii as tuples, however this is one of the very interesting
functionality available in Swift.
5 .1. Understanding tuples
Tuple declaration
Listing 5.1
1 / * declare a tuple consisting of
2
from three different parameters,
3
each of which has its own type
4
data * /
5 let myProgramStatus = (200, "In Work", true) (.0 200, .1 "In Work",
.2 true)
6 myProgramStatus
(.0 200, .1 "In Work",
.2 true)
Listing 5.4
1 // declare a tuple
2 let myProgramStatus = (200, "In Work",
true)
(.0 200, .1 "In Work",
.2 true)
3 // write the values of the tuple
into variables
Console :
Response code - 200
Response text - In Work
Server connection - true
If you do not pass as the right side of the assignment operator
the name of the parameter storing the tuple, and the tuple as a set of values,
then you can assign arbitrary variables or constants to a set
values (Listing 5.5).
Listing 5.5
1 / * declare several at once
2
variables and set
3
values for them * /
4 var (myName, myAge) = ("Troll", 140)
5 // display their values
6 print ("My name is \ (myName), and I
"My name is Troll, and I
\ (myAge) years ")
140 years \ n "
Console :
Console :
Listing 5.7
1 // declare a tuple
2 let myProgramStatus: (Int, String, Bool) =
(200, "In Work", true)
(.0 200, .1 "In Work",
.2 true)
3 // display information using
indices
4 print ("Response code - \ (myProgramStatus.0)") "Response code - 200 \ n"
5 print ("Response text -
\ (myProgramStatus.1) ")
"Response text - In
Work \ n "
6 print ("Communication with the server -
\ (myProgramStatus.2) ")
"Communication with the server -
true \ n "
Console :
separated by a colon. At the same time, you cannot specify names for
individual elements.
possible: you must provide names for either all elements, or
don't use them at all (Listing 5.8).
Listing 5.8
1 let myProgramStatus = (statusCode: 200,
statusText: "In Work", statusConnect: true)
(.0 200, .1 "In Work",
.2 true)
Tuples allow you to do what you might have lacked in the other
in other programming languages, - return a set of values in a convenient
the new interface to access them. This use of tuples can
find, for example, in functions.
NOTE Tuples do not allow the creation of complex data structures,
their only purpose is to group a set of different types
or parameters of the same type and transfer to the required place. To create complex
structures, it is necessary to use the means of object-oriented software
ming (OOP), or rather, classes or structures. We will get acquainted with them in the cha-
of the IV book.
The task
6 .1. Optionals
Listing 6.1
Listing 6.2
1 / * variable with optional Int type
2
and with the set value * /
3 var dragonAge: Int? = 230
230
4 // destroy the value of the variable
5 dragonAge = nil
Nil
NOTE In Swift, the nil keyword can only be used in conjunction with
variables of an optional data type. Moreover, if you declare such a re-
variable, but don't initialize its value, Swift considers it equal by default
noah nil.
Listing 6.3
basic data types, the rest of the properties of these types are optional
nalam does not apply. For example, the Int? cannot be used as an operand
when performing arithmetic operations
(Listing 6.4).
Listing 6.4
1 / * optional variable
2
with set value * /
3 var trollAge: Int? = 95
95
5 trollAge = trollAge + 10 // ERROR
1 / * optional variable
2
with set value * /
3 var trollAge: Int? = 95
95
4 // perform arithmetic operation
5 trollAge = trollAge! + 10
105
6 trollAge
105
Listing 6.6
As the main type for the optional, you can use any
bout data type. Since a tuple is a separate type
data corresponding to the type of elements included in it, then its
the type can be taken as the basis for the optional, as demonstrated
in the previous example.
7 Assertions
Swift allows you to interrupt the execution of the program in the case when
not-
which condition is not met - for this purpose there is a special
assertions mechanism . Assertions are used on
debugging stage of the program.
Listing 7.1
1 / * integer variable
2
with set value * /
3 var dragonAge = 230
230
4 // check if the condition is correct
5 assert (dragonAge> = 225, "Your dragon
too young to continue ")
6 // display the value of the variable
7 dragonAge
230
The condition dragonAge> = 225 is passed to the statement . Since re-
meline dragonAge equal to 230, the given condition is true and the interrupt
the program will not happen.
If we change the condition passed in the previous listing to
dragonAge> = 235 then assertion will stop program execution
and a corresponding message will appear on the console indicating the line,
where execution stopped (fig. 7.1).
Figure: 7.1. An error occurred when the transferred
in the statement of the condition
You can choose not to pass the message as an input parameter
into the statement, limiting ourselves to only a condition (Listing 7.2).
Listing 7.2
1 / * integer variable
2
with set value * /
3 var dragonAge = 230
230
4 // check if the condition is correct
5 assert (dragonAge> = 225)
6 // display the value of the variable
7 dragonAge
230
Assertions should be used when the value of the condition is the same.
value should be true , but chances are it will return
false . So with the assert () function you can easily
check if some optional has any value. For
it just needs to be compared (using the compare operator
ness) optional and nil (Listing 7.3).
Listing 7.3
8 Flow control.
Branching
Imagine a river: its unbridled seething flow, flowing
turns, sharp drops and backwaters. Humanity possesses techno
logias, thanks to which the flow of the river can be controlled:
the strength of its flow, create new channels, etc.
A thread in a programming context is a process of execution
programs. There are special mechanisms in Swift that allow
who control this process, for example, perform or, conversely,
ignore the code depending on the conditions set, and
to repeat certain blocks of code many times.
The ability to manage the course of the program is a very important aspect
programming in Swift language, thanks to which your programs
we will be able to execute different blocks of code depending on the
conditions. This chapter discusses the mechanisms that
supported in almost all programming languages and without
which is impossible to execute any program.
If statement syntax
Listing 8.1
Console :
LogicVar is true
As a condition for the if statement , the logical
variable logicVar . Since its value is true, the code located
in the body of the statement, was executed, as evidenced by the message on
console.
If you change the value of logicVar to false , then the check will fail
and the print () function will fail.
Don't forget that Swift is a strongly typed language. Any
the condition must return either true or false , and nothing else.
Therefore, if the check returns a value of a different type, then Xcode
will report an error (Listing 8.2).
Listing 8.2
1 var intVar = 1
2 if intVar {// ERROR
3
// ...
4}
Listing 8.3
Console :
LogicVar is false
Using logical negation, the value of the variable logicVar
is inverted, as a result, the if statement finds that
its condition is true.
The syntax used to write the if statement , allowing you to specify
block of executable code only for the true test result,
called short . Swift allows enclosing one operator
condition generator blocks of code for both true and false results
checks. To do this, you need to use the standard syntax
cis operator condition if .
SYNTAX
if test_condition {
// the true body of the operator
} else {
// false operator body
}
If the passed condition is true, the code from the "true" block is executed. In
in all other cases, a "false" block of code is executed that follows
the else keyword.
Listing 8.4 shows an example of using the standard syntax
system of the logical operator if .
Listing 8.4
If the first checked condition is true, then the code block of the first branch is executed
condition operator. Otherwise, there is a transition to checking the second
lovia. If it is true, then the code from the second branch is executed. Otherwise
the code of the else block is executed.
The number of else if statements within one condition statement can be
arbitrary, and the else clause is optional.
Listing 8.6 shows an example of using the extended
different syntax of the logical operator of the condition.
Listing 8.6
1 // variables of type Bool
2 var firstLogicVar = true
3 var secondLogicVar = true
4 // check the value of variables
5 if firstLogicVar && secondLogicVar {
6
print ("Both variables are true")
7 } else if firstLogicVar || secondLogicVar {
8
print ("One of the variables is true")
9 } else {
ten
print ("Both variables are false")
11 }
Console :
Both variables are true
Note that the operator finds the first match, after
which executes the corresponding block of code and exits
from the operator. If the condition with logical OR ( || ) is placed
first, and it also returns true , then the output to the console is
the result (one of the variables is true) will only partially reflect
real situation.
In all examples, variables were used as conditions
like Bool . But Swift doesn't limit us to that. Only one thing is important to
him:
so that the condition in the if statement will return a boolean value. Yes-
let us use as a condition an expression that returns
boolean value.
Imagine you have an apartment building that you rent out.
The rent that each tenant pays depends on the total
number of residents:
❑ If the number of tenants is less than 5, then the cost of rent
per month is equal to 1000 rubles per person.
❑ If the number of tenants is from 5 to 7, then the rent is
800 rubles per person.
Listing 8.7
Since the total number of residents falls into the second block of the
construction
tion the if-the else , variable rentPrice takes the value 800. Summary
the result is 4800.
Ternary condition operator
For your convenience, Swift allows you to shorten the standard form
writing an if statement to just a few characters. This form
is called a ternary condition operator .
SYNTAX
This operator can execute one of two blocks of code, depending on the use
the uncertainty of the checked condition. Condition and executable code are separated from each other.
friend with a question mark. The code blocks themselves are separated from each other by a colon.
If the checked condition is true, the first block of code is executed. In all
in other cases, the second block is executed.
An example of using the ternary operator of the condition is given in Lis-
tinge 8.8.
Listing 8.8
1 // constants
2 let a = 1
3 let b = 2
4 // comparing constant values
5 a <= b? print ("A is less than or equal to B"): print ("A is greater than B")
Console :
Listing 8.10
1 / * variable of optional type
2 with preset value * /
3 var trollsCount: Int? = 8
8
4 // determine if a value exists
5 if trollsCount == nil {
6
print ("There are no trolls")
7 } else {
8
// number of boilers for one troll
nine
var potCountForTroll = 2
2
ten
// total number of required boilers
eleven
var allPotsCount = potCountForTroll * trollsCount!
ten
12 }
As with the assert () function , to define
the fact of the presence of a value in the optional, you just need to compare
it with nil .
Note that when calculating the value of allPotsCount non-
it is necessary to use forced extraction of the optional
the values of the trollsCount variable .
Within the framework of using the if-else construction to check the existence
setting the value in the optional you can try to extract it
and assign it to the new parameter. In this case, if the value exists
em, then a new parameter will be created and will receive the extracted
optional
value. If the value is nil , then an attempt to initialize
will fail.
This technique is called optional binding (optional
binding).
SYNTAX
if var generated_variable_name = optional_name {
code1
} else {
code2
}
Here, a let statement can be specified instead of the var statement if necessary.
The variable being created has limited visibility (scope).
You can use it only within this if-else construction.
If the considered optional is nil, then the condition is considered unfulfilled
and the code block in the if statement is omitted. If in this case the else block is present, then
the code placed in it is executed.
If the considered optional is not nil, then the optional value is automatically
is extracted, a new parameter (variable or constant) is created, it
assigned the newly retrieved value and block of code in the if condition statement
executed. In this case, the created parameter can only be used in this
block, that is, the scope of the parameter is limited to this block.
NOTE Scope defines where in the code some
an object . If this object is global, then it is available at any point in the
grams. If the object is local, then it is available only in that block of code
(and in all nested blocks) for which it is local. Outside
of this block, the object is simply not visible.
Listing 8.11
The result is a waste of CPU time, since the code is doing the same function
twice. Can be avoided this by creating a variable in advance to which the
value will be retrieved optional. This variable must be used in both cases
instead of calling the Int () function . An example is shown in Listing 8.13.
Listing 8.13
Despite the fact that the above code fully solves the delivered
problem, it has one drawback: the created variable coins
will exist (and therefore occupy RAM) even
after the completion of the conditional statement, although then the given
the variable is no longer needed.
When programming, you must in all possible ways
avoid wasting computer resources to which
both CPU time and memory are included.
In order to avoid wasting memory, you can use
optional binding, since after executing the statement, the
vii the variable created during binding will be automatically deleted
(Listing 8.14).
Listing 8.14
The task
The guard statement is called the early exit statement . Like opera-
to the if statement , it checks the truth of the condition passed to it. Excellent
what it does is that it executes a block of code located in curly
brackets, only if the condition returned false .
SYNTAX
Let's start our examination of the switch statement with an example of use.
structure if-else for a large number of options
(Listing 8.15). This code outputs certain text to the console
depending on the grade received by the student.
Listing 8.15
Console :
Listing 8.16
1 / * variable of type Int
2
contains the estimate obtained
3
user * /
4 var userMark = 4
5 / * use the if-else construction
6
to determine the value
7
userMark variable and output
8
required text * /
9 switch userMark {
ten
case 1:
eleven
print ("One on the exam! It's awful!")
12
case 2:
thirteen
print ("You will stay with a 2 for the second year!")
fourteen
case 3:
fifteen
print ("You did not study the material well this year!")
sixteen
case 4:
17
print ("Not bad, but could be better")
eighteen
case 5:
19
print ("You are guaranteed a free place at the university!")
20
default:
21
break
22 }
Console :
Console :
Exam passed!
The line "Exam not passed!" output to the console, provided that the
the trusted value in the userMark variable is 1 or 2. In the rest
In some cases, the line "Exam passed!" ...
The default block allows you to avoid an error if suddenly the estimate turns
out
out of range 1 ... 5 . It interrupts the execution of the program, informing
that the value of the userMark variable is wrong.
Fallthrough keyword
Listing 8.18
Console :
Where keyword
Console :
Exam passed!
Here we do not specify the value after the case keyword , but instead
we put the underscore skip character on it, so the
only the condition passed after where is valid .
Tuples in a switch statement
The switch statement's capabilities don't end with
operators and keywords considered. You have previously known
komilis with tuples, thanks to which the
efficiency of working with values of various types. Swift can
use tuples as passed to the construct
switch-else parameter. In this case, the desired value in the case block is not
must be specified in the same way as the value of the shortcut itself is written
other, that is, in parentheses, where items are separated by commas. Each
an element of a tuple can be checked with an arbitrary
value or range of values. Additionally for the pass
element can use the underscore character on
place of any element.
Let's go back to the dragon example. In it the values of the variables
dragonColor and dragonWeight were separated, so we had to
use the where keyword to display the required text.
However, these variables can be combined into a tuple and checked for
consistency.
locally (Listing 8.21).
Listing 8.21
Console :
Console :
The task
1. Define an Operation alias for the tuple type containing which has three
elements with the following names: operandOne , operandTwo , operation .
The first two are floating point numbers. They will co- hold operands for the
operation being performed. The third element - a string type Character . It
will contain a pointer to the operation being performed. Total there can be
four types of operations: + , - , * , / .
9 .1. Arrays
Array declaration
and the same. Even if you declared an empty array, it doesn't care
the type of elements not yet existing must be determined.
The array value is specified using an array literal , in which
the values included in the elements are listed
.
SYNTAX
The array literal is indicated in square brackets, and the values of individual elements
tod in it are separated by commas. The array can contain any number of
number of elements of the same type.
Value indices are assigned automatically depending on the order of the next
elements.
1 // immutable array
2 let alphabetArray = ["a", "b", "c"]
3 // mutable array
4 var mutableArray = [2, 4, 8]
Listing 9.2
1 // immutable array
2 let unmutableArray = ["one", "two", "three"]
["one", "two",
"three"]
3 // copy the array from constant to variable
4 var newArray = unmutableArray
["one", "two",
"three"]
5 newArray
["one", "two",
"three"]
6 // change the value of the new array
7 newArray [1] = "four"
"four"
8 // display the value of the original array
9 unmutableArray
["one", "two",
"three"]
When passing the value of the original immutable array in the new
a full copy of the array is created. When changing this copy
the value of the original array remains the same.
NOTE While this approach is logical, it can sometimes
cause some difficulties in application development when without
necessary, multiple copies of the array are created, which leads to an increase in
total used memory.
Comparing arrays
Arrays, just like the values of fundamental data types, can
compare with each other. Two arrays are equivalent if:
❑ The number of elements in the compared arrays is the same.
❑ Each matching pair of elements is equivalent.
Let's look at an example of comparing two arrays (Listing 9.3).
Listing 9.3
1 / * three constants that
2
will become array elements * /
3 let a1 = 1
4 let a2 = 2
5 let a3 = 3
6 if [1, 2, 3] == [a1, a2, a3] {
7
print ("Arrays are equivalent")
8 } else {
nine
print ("Arrays are not equivalent")
10 }
Console :
Arrays are equivalent
Despite the fact that the array [a1 , a2 , a3] contains not values, but con-
stants containing these values, array equivalence conditions
are still executed.
1 // immutable array
2 let alphabetArray = ["a", "b", "c"]
["a", "b", "c"]
3 // mutable array
4 var mutableArray = [2, 4, 8]
[2, 4, 8]
5 // access to array elements
6 alphabetArray [1]
"b"
7 mutableArray [2]
8
NOTE The way to access items using key names
innovations, in this case indices, is called a subscript. In the next section
lah we will get acquainted with all the possibilities of subscripts.
Using indices, you can access array elements not
read-only, but also changeable (Listing 9.5).
Listing 9.5
1 // mutable array
2 var mutableArray = [2, 4, 8]
[2, 4, 8]
3 // change an array element
4 mutableArray [1] = 16
sixteen
5 // output a new array
6 mutableArray
[2, 16, 8]
An attempt to modify an array stored in a constant will cause
error.
When using the range operator, you can access the
to the set of elements in the array, that is, to its subarray.
This operator must point to the indices of the outermost elements
the set to be allocated (Listing 9.6).
Listing 9.6
1 // mutable array
2 var mutableArray = ["one", "two", "three", "four"] ["one", "two",
"three", "four"]
3 // copy the subarray into a separate variable
4 var subArray = mutableArray [1 ... 2]
["two", "three"]
5 / * replace multiple elements
6
new array * /
7 mutableArray [1 ... 2] = ["five"]
["five"]
8 mutableArray
["one", "five",
"four"]
Listing 9.9
Merging arrays
Listing 9.10
1 // create two arrays
2 let charsOne = ["a", "b", "c"]
["a", "b", "c"]
3 let charsTwo = ["d", "e", "f"]
["d", "e", "f"]
4 let charsThree = ["g", "h", "i"]
["g", "h", "i"]
5 // create a new one by merging two
6 var alphabet = charsOne + charsTwo
["a", "b", "c", "d", "e",
"f"]
7 // merge the new array with the old one
8 alphabet + = charsThree
["a", "b", "c", "d", "e",
"f", "g", "h", "i"]
10 alphabet [8]
"i"
Multidimensional arrays
Array elements can be not only the values of the fundamental
types but also other arrays. Arrays containing other masses
sivas are called multidimensional. Ensure unity
type of all nested arrays.
Consider the example in Listing 9-11.
Listing 9.11
Listing 9.12
Arrays are very functional elements of the language. This is taken care of
The Swift developers were trying to provide us with a set of properties and
methods,
allowing to significantly expand their capabilities in comparison
with other languages.
The count property returns the number of elements in the array (Lis-
Thing 9.13).
Listing 9.13
1 var someArray = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5 ]
2 // number of elements in the array
3 someArray.count
five
If the value of the count is zero, and the property isEmpty WHO-
rotates true (Listing 9.14).
Listing 9.14
1 var someArray: [Int] = []
[]
2 someArray.count
0
3 someArray.isEmpty
true
You can use the count property to get
the required array elements (Listing 9.15).
Listing 9.15
1 var someArray = [1, 2, 3, 4, 5]
[ 1, 2, 3, 4, 5]
2 // number of elements in the array
3 var newArray = someArray [someArray.count-3 ... [3, 4, 5]
someArray.count-1]
Another means of getting a set of array elements is
The suffix () method is passed as an input parameter
the number of items to receive. Elements from-
read from the last element in the array (Listing 9.16).
Listing 9.16
1 var someArray = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
2 // get the last three elements of the array
3 let subArray = someArray.suffix (3)
[3, 4, 5]
First and last properties return the first and last elements
array (Listing 9.17).
Listing 9.17
1 var someArray = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
2 // returns the first element of the array
3 someArray.first
1
4 // returns the last element of the array
5 someArray.last
five
Using the append () method , you can add a new element to the end
array (Listing 9.18).
Listing 9.18
1 var someArray = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
2 someArray.append (6)
[1, 2, 3, 4, 5, 6]
If the array is stored in a variable (i.e. is mutable),
then the insert () method inserts a new single element into the array with the
the associated index (Listing 9.19).
Listing 9.19
1 var someArray = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
2 // insert a new element in the middle of the array
3 someArray.insert (100, atIndex: 2)
[1, 2, 100, 3,
4, 5]
In this case, the array indices are recalculated to provide them
sequence.
As in the case of changing the array, the removeAtIndex () methods ,
removeFirst () and removeLast () allow you to remove required items
cops. At the same time, they return the value of the removed element.
(Listing 9.20).
Listing 9.20
Listing 9.22
The indexOf () method reports the index of the first occurrence of the item you
are looking for.
cop in the considered array. Since the required element can be
present, the method returns an optional value (Listing 9.23).
If no element is present, nil is returned .
Listing 9.23
1 let intArray = [1, 2, 3, 4, 5, 6, 4, 5, 6]
[1, 2, 3, 4, 5, 6,
4, 5, 6]
2 // find the first occurrence of an element
3 let result = intArray.indexOf (4)
3
4 let resultNIL = intArray.indexOf (99)
Nil
NOTE You can compare values of those data types that are
are hashable, that is, they must have a calculation functional
hash for the value. Most fundamental data types support
hashing. The hash provides the ability to compare (collate) different
values of the same type. When calculating it, the eigenvalue
parameter by a special algorithm is converted into a numerical value and
is stored in the hashValue property of the parameter. To access the hash, the parameter is sufficient
call the specified property:
var a: Float = 3.5
a.hashValue // 1 080 033 280
This functionality is provided by the Hashable protocol, standard for Swift.
About what protocols are and how to use them for your own development, you
you will find out later.
To change the order of all elements of the array to pro
conversely, use the reverse () method as shown in listing
ge 9.25.
Listing 9.25
1 var someArray = [1, 3, 5, 7, 9]
[1, 3, 5, 7, 9]
2 someArray.reverse ()
ReverseRandomAccess
Collection <Array <Int>>
Surprisingly, reverse () does not return an array of type
[Int] , and some unknown value of type Reverse Random AccessCol-
lec tion . This is one of the most interesting features of the Swift language.
About,
what is this data type and how to get the usual array from it, we
we'll talk in the next sections.
9 .2. Sets
Set declaration
SYNTAX
[value_1, value_2, ..., value_N]
The set literal is indicated in square brackets, and the values of the individual elements
tod in it are separated by commas. A literal can contain any number of
number of unique elements of the same type.
When creating a set, you must explicitly indicate what is being created.
set. If you pass a set literal to a variable, then Swift recognizes
in it an array literal and an array will be created instead of a set. Due
with this it is necessary:
❑ either explicitly specify the data type of the set using the construct
tions Set <DataType> , where DataType is the type of elements of the created
set;
❑ either use the Set <DataType> function , which as
the input parameter is arrayLiteral containing a list
set items.
Listing 9.26
1 / * set created by
2 explicit type statements * /
3 var dishes: Set <String> = ["bread", "vegetables", {"vegetables", "stew",
"stew", "water"]
"water", "bread"}
4 / * set created without explicit
5 specifying data type * /
6 var dishesTwo: Set = ["bread", "vegetables",
{"vegetables", "stewed meat",
"stew", "water"]
"water", "bread"}
7 / * set created with
8 functions with explicit
9 specifying the data type * /
10 var members = Set <String> (arrayLiteral:
{"Anakin", "Obi Wan",
"Anakin", "Obi Wan", "Yoda")
"Yoda"}
11 / * set created with
12 non-explicit functions
13 data types * /
14 var membersTwo = Set (arrayLiteral: "Anakin", {"Anakin", "Obi Wan",
"Obi Wan", "Yoda")
"Yoda"}
The variables members , membersTwo , dishes , dishesTwo store sets
unique values.
Listing 9.29
1 // create a set with values
2 var musicStyleSet: Set <String> =
{"Jazz", "Hip-Hop", "Rock"}
["Jazz", "Hip-Hop", "Rock"]
3 // remove one of the elements
4 musicStyleSet.remove ("Hip-Hop")
"Hip-Hop"
5 musicStyleSet
{"Jazz", "Rock"}
6 // remove non-existent element
7 musicStyleSet.remove ("Classic")
nil
8 // remove all elements of the set
9 musicStyleSet.removeAll ()
[]
For any two sets, you can perform the following operations
(fig.9.2):
❑ Get all elements common to both sets ( intersect ).
❑ Get all disjoint (not common) for both sets
elements ( exclusiveOr ).
❑ Get all the elements of both sets ( union ).
❑ Get the difference of elements, that is, the elements that are included
in the first set, but not in the second ( subtract ).
Figure: 9.2. Operations carried out with sets
When using the intersect () method , a new set is created with
holding values common to the two sets (Listing 9.32).
Listing 9.32
1 var inter = differentDigits.intersect (oddDigits) .sort ()
[3, 7]
Note that in this example, for the first time in one expression
The method uses a chain of calls to the intersect () and sort () methods .
Calling the sort () method returns a sorted
the array in which the smallest values are listed first.
This method returns exactly an array, since a set is an inappropriate
row collection, where the concept of the order of the elements
absent.
from bSet . At the same time, the bSet is a subset (or subset) of
aSet , since all bSet elements exist in aSet . Sets cSet and bSet
are non-overlapping since they have no common elements,
while aSet and cSet are overlapping because they have common elements.
Two sets are considered equivalent if they have the same
set of elements. The equivalence of sets is checked using
the equivalence operator ( == ), as shown in Listing 9.36.
Listing 9.36
1 // create a set and a copy of it
2 var bSet: Set = [1, 3]
3 var copyOfBSet = bSet
4 / * bSet and copyOfBSet have the same composition
5 elements. Let's check their equivalence * /
6 if bSet == copyOfBSet {
7
print ("Sets are equivalent")
8}
Console :
Console :
aSet is a superset for bSet
The isDisjointWith () method
determines if two sets exist
common elements, and if they are absent, returns true (Lis-
Thing 9.39).
Listing 9.39
1 var bSet: Set = [1, 3]
2 var cSet: Set = [6, 7, 8, 9]
3 if bSet.isDisjointWith (cSet) {
4
print ("bSet and cSet do not overlap")
5}
Console :
the bSet and cSet do not overlap
Methods isStrictSubsetOf () and isStrictSupersetOf ()
is determined yav-
a set is a subset or a superset that is not equal to the specified
set (Listing 9.40).
Listing 9.40
1 var aSet: Set = [1, 2, 3, 4, 5]
2 var bSet: Set = [1, 3]
3 if bSet.isStrictSubsetOf (aSet) {
4
print ("bSet is a subset of aSet")
5}
6 if aSet.isStrictSupersetOf (bSet) {
7
print ("aSet is a superset for bSet")
8}
Console :
bSet - a subset of aSet
aSet - superset for bSet
9 .3. Dictionaries
Dictionary declaration
SYNTAX
[key_1: value_1, key_2: value_2, ...,
key_N: value_N]
A dictionary literal describes the elements of a dictionary. It is written in square
brackets, and the items specified in it are separated by commas. Each element is
it is a key-value pair, where the key is separated from the value by a colon.
NOTE To create an immutable dictionary, use the let statement,
otherwise, the var statement.
An example of creating a dictionary is shown in Listing 9.41.
Listing 9.41
1 var dictionary = ["one": "one", "two": "two", "three": "three"]
The dictionary contains three elements. Here one , twoand three are
these are keys that are used to access the values of the dictionary. By type
key data, like the data type of dictionary values, is
String .
When trying to create a dictionary with two same Xcode keys
will report an error.
Interacting with dictionary elements
As noted earlier, access to the elements of the dictionary occurs with the help
of
by the power of unique keys. As with arrays, the keys
Listing 9.44
Listing 9.45
The data type of the dictionary elements contains two data types: the type
key and value type. As with arrays and set-
mi, you can explicitly specify the value of the collection data type, but when
This requires you to specify the data types for both the key and the value.
SYNTAX
var dictionary_name: Dictionary <KeyType: ValueType>
var dictionary_name: [KeyType: ValueType]
The dictionary type in this case is [KeyType: ValueType] (with square brackets-
mi) or Dictionary <KeyType: ValueType>. Both options for specifying a data type
are equivalent.
The declared dictionary before you can interact with it -
vat must be initialized.
NOTE The values in the dictionary are not stored in the order in which you
they were placed there. Dictionaries are not arrays, they are not ordered
collections. You cannot add an element to the end of the dictionary, you just add-
you put a new element, and Swift decides on its own what position in the given
put it in the dictionary.
When calling the keys or values property, Swift does not return an array,
a collection or dictionary, and a value of some type LazyMapCollection . From to
we have seen similar behavior earlier when we used the function
tion reverse () for reordering elements of an array
on the opposite. We will talk about what it is in the next chapter.
The task
10 Flow control.
Repetitions
Earlier, we started to explore the topic of flow control, in which
looked at the operators that allow you to change the code execution
depending on
from the conditions that have arisen during the program. In this
the chapter describes the mechanisms that allow you to cyclically execute
various blocks of code and manage these loops.
Cyclic code execution is provided by repeat operators
niya . Swift offers just three such operators: for , while and repeat.
while . Each of them has different capabilities.
The code can be repeated either until a certain condition is met, or
a certain number of times. Each next block execution
code is called iteration .
10 .1. Repeat operator for
For loop
SYNTAX
All three constructs following the for statement are separated by a dot character
with a comma, and the body of the loop is enclosed in curly braces.
The operator works as follows:
1. Running startovoe_vyrazhenie wherein declared nekoto-
paradise variable with a specific value.
Listing 10.1
You can declare in a separate expression (even before the for statement )
and initialize a variable that will be checked in the condition
the loop, and pass only its name to the operator. Moreover, all changes
the values of this parameter inside the loop will be global, that is
visible outside the for statement (Listing 10.2).
Listing 10.2
1 var i = 0
0
2 for i; i <5; ++ i {
3
i ++
(3 times)
4
print (i)
(3 times)
5}
6i
6
Console :
1
3
five
The variable i is global, so if you change it to c-
glue for , then these changes will be visible outside the loop.
The for statement can be used to iterate over all the elements of an array
or a set (Listing 10.3).
Listing 10.3
RUS
UK
USA
The i variable is used to access array elements. Since the index
the first element of the array is always 0, then in the start expression
variable i , which will act as the index of the read
element must be initialized to 0.
To limit the number of loop iterations, use the property
count containing the number of elements in the array.
Retrieving all elements of a dictionary one at a time using a given
the cycle is much more difficult to implement. First you need
get an array of all keys and, by traversing it with a for loop ,
get values from a dictionary (Listing 10.4).
Listing 10.4
1 var mySports = ["Football": "Football",
["Football":
"Polo": "Polo", "Golf": "Golf"]
"Football", "Golf":
"Golf", "Polo":
"Polo"]
2 // get an array of all keys
3 let mySportsArray = Array (mySports.keys)
["Football", "Golf",
"Polo"]
4 / * traverse the array of keys
five
to access dictionary values * /
6 for var i = 0; i <mySportsArray.count; i ++ {
7
print (mySports [mySportsArray [i]]!)
(3 times)
8}
Console :
Football
Golf
Polo
Do not forget that any reference to a dictionary element returns
optional, so to get the original data type, no
it is necessary to perform a forced extraction of the optional
values.
Listing 10.5
1 var totalSum = 0
0
2 for i in 1 ... 10 {
3
totalSum + = i
(10 times)
4}
5 totalNum
55
1 var totalNum = 0
0
2 var i = 0
0
3 for var i in 1 ... 10 {
4
totalNum + = i
(10 times)
5}
6 totalNum
55
7i
0
Despite the fact that in the loop the value of the local variable i changes
it means that the value of the global variable i remains the same.
Using a for-in loop is very convenient to iterate over the values of a collection.
tions. This requires passing the collection name after the key
the words in (Listing 10.7).
Listing 10.7
1 var myMusicStyles = ["Rock", "Jazz", "Pop"]
2 for musicName in myMusicStyles {
3
print ("I love \ (musicName)")
4}
Console :
I love Rock
I love Jazz
I love Pop
As a result, the musicName variable will receive each of the
values written to the myMusicStyles array .
Console :
Russia joined the EAEU
France joined the EU
USA joined NATO
As you can see, this method is much more convenient than using complex
constructs in the for loop , and working with collections in the loop is now up
to
is a pleasure!
Console :
country Russia
country - France
Country: USA
You got acquainted with this technique while studying the corte-
zhey.
The Swift has a special method enumirate () , which pozvolya-
transforms the array in such a way that using a for-
in get each separate index-value pair as a tuple
(Listing 10.10).
Listing 10.10
1 var myMusicStyles = ["Rock", "Jazz", "Pop"]
2 for (index, musicName) in myMusicStyles.enumerate () {
3
print ("\ (index + 1). I love \ (musicName)")
4}
Console :
1. I love Rock
2. I love Jazz
3. I love Pop
A string (that is, a value of type String ) is a set of characters that
can be represented as a collection. This is done using the property
characters . As such, a collection of symbols can be processed
character by character using a for-in loop . Listing 10.11 shows a value of type
String is represented as a collection of characters and the elements of this
collections are printed to the console one at a time.
Listing 10.11
1 let myName = "Troll"
2 for oneChar in myName.characters {
3
print (oneChar)
4}
Console :
T
r
o
l
l
To handle multidimensional constructs like nested collections
tion, you can nest one cycle within another. In Listing 10.12
we will create a dictionary that contains the results of the hockey games
teams in the championship. The key of each element is the name of the
command
rival, and the value of each element is an array of game results
with the command specified in the key. The results of all
games with the indication of teams and the final score.
Listing 10.12
1 // dictionary with game results
2 var resultsOfGames = ["Red Wings": ["2: 1", "2: 3"],
"Capitals": ["3: 6", "5: 5"], "Penguins": ["3: 3", "1: 2"]]
3 // processing the dictionary
Console :
Game with Capitals - 3: 6
Game with Capitals - 5: 5
Game with Red Wings - 2: 1
Game with Red Wings - 2: 3
Playing with the Penguins - 3: 3
Playing with the Penguins - 1: 2
The array type resultsOfGames is a "dictionary of dictionaries" Dictio-
nary <String: Dictionary <String>> . The teamName variable is local,
but the nested for-in loop falls within its scope , so
in this loop, you can use this variable to display it
values.
While loop
SYNTAX
while condition {
block_code
}
The loop begins with a while statement, followed by the tested condition.
catch.
Each iteration starts by checking the condition. If it returns true, then you-
block_code is executed. Next, the next iteration starts. Etc .
ATTENTION Be careful when setting the condition, because inadvertently
You can specify a condition that will never return false. In this case, the cycle
will execute indefinitely, which will likely freeze the program.
Listing 10.13
1 // initial value
2 var i = 1
1
3 // storage of the result of addition
4 var resultSum = 0
0
5 // loop for calculating the sum
6 while i <= 10 {
7
resultSum + = i
(10 times)
8
i ++
(10 times)
9}
10 resultSum
55
The variable i is the counter in this loop. It is for her the value determines
whether the loop body needs to be executed. On each iteration, the value of i
is increased by one, and as soon as it reaches 10, then the condition checked
by the operator returns false , after which the loop is exited. The while statement
is a pre-conditional loop, that is, the condition is checked first, and only then
the code is executed.
SYNTAX
repeat {
block_code
} while condition
The loop begins with a repeat statement, followed by the body of the loop. At the end of the cycle
the while statement and the loop execution condition are written.
An example implementation of the repeat while loop is shown in Listing 10.14.
which adds up all the numbers from 1 to 10 and displays the result.
Listing 10.14
1 // initial value
2 var i = 1
1
3 // storage of the result of addition
4 var resultSum = 0
0
5 // loop for calculating the sum
6 repeat {
7
resultSum + = i
(10 times)
8
i ++
(10 times)
9 } while i <= 10
10 resultSum
55
1 import Foundation
2 for i in 1 ... 10 {
3
var randNum = Int (arc4random_uniform (10))
4
if randNum == 5 {
five
print ("Iteration number \ (i)")
6
break
7
}
8}
Console :
Iteration number 7
The output to the console in your case may differ from what is
included in the example, since a random number generator is used.
The first line imports the Foundation library into which includes the
arc4random_uniform () function designed to getting a random number. If you
remove the import line then Xcode will report that a function named
arc4random_uniform () does not exist is. This function takes as input a number of
type Uint32 and rotates a random number in the range from 0 to the passed
value
of type UInt32 . The returned random number also has a data type
UInt32 , so you need to cast it to Int using the appropriate
corresponding function.
NOTE All variables and constants created inside the loop are
local to the current iteration, that is, in the next iteration, this re-
The menu will be unavailable.
Exercise 1
Imagine that you are a teacher of courses in chess
mats. There are three students attending your classes.
1. Create a dictionary that will contain information
about your students and their success. The dictionary key must
be the last name, and the value is another dictionary containing
the date of the lesson and the grade you received in that lesson.
The dictionary data type must be [String: [String: UInt]] .
Your ezine should contain two
grades for each of the three students. Surnames, dates of registration
come up with concepts and assessments yourself.
2. Calculate the average score of each student and the average score
the whole group as a whole and print all the information received
command to the console.
Assignment 2
Let's go back to the task from Chapter 9, in which you described the check
matte figure and create an if-else construction , check-
the presence of a piece on the playing field.
You need to modify this program in such a way,
so that it automatically analyzes more than one transmitted
her shape, and all the shapes stored in the Chessmans variable .
11 Functions
Earlier in the process of parsing programs from listings, we repeatedly
met with functions. All functions used were re-
the result of the work of the developers of the Swift language. In this chapter,
you will
learn to create functions yourself according to your
needs.
A function is a named piece of program code to which
rum can be accessed multiple times. Functions avoid
duplicating code by grouping it. This is a very useful tool.
Swift tool. I'm sure you've already familiarized yourself with functions in
others.
programming languages. Any major differences in Swift
no, but there are a number of nuances to be aware of.
SYNTAX
A function declaration begins with the keyword func, followed by the name of the created
given function. Further in parentheses are input parameters, or arguments,
then after the arrow - the return type and finally - in curly braces-
the body of a function, that is, a block of code that contains all the logic of its operation.
The function name is used every time you call it in your code.
The function name should be written in the lower camelcase style. On-
example:
func myFirstFunc
The list of input parameters is enclosed in parentheses and consists of
from comma separated items. Every single element
describes one input parameter and consists of the name and type of this
parameter, separated by colons. Input parameters, or arguments
cops, allow you to pass in the function the values that it needs
are. The specified parameters are local to the function body,
that is, they are only available in the body of the function. Number of input
parameters can be arbitrary (they can also be absent
vat). For instance:
func myFirstFunc
(someValue: Int, anotherValue: String)
The parameter list is followed by an arrow ( -> ) followed by
the data type of the function's return value. As a data type-
you can specify both the fundamental type and the type of the array
or a tuple. For instance:
func myFirstFunc
(someValue: Int, anotherValue: String)
-> String
Or:
func myFirstFunc
(someValue: Int, anotherValue: String)
-> [(String, Int)?]
The function body is enclosed in curly braces and contains the entire
the logic of her work. If the function returns any value, then
its body must have a return statement followed by
return value. After the program has executed the operator
return the function terminates. For instance:
func myFirstFunc
(someValue: Int, anotherValue: String)
-> String {
return String (someValue) + anotherValue
}
Let's declare the simplest function that has no input and output-
values. Imagine that when some event occurs
you need to print a message to the console. Let's implement this
mechanism using a function (Listing 11.1).
Listing 11.1
1 func printMessage () -> Void {
2
print ("Message received")
3}
4 // call the function by its name
5 printMessage ()
Console :
Message received
To output text to the console, you call the printMessage () function ,
just by writing her name with parentheses. PrintMessage () function
does not have any input parameters or return value.
It just prints a text message to the console.
If the function does not return any value, then the type
return value data must be of type Void . By-
A similar C-shaped approach is used in many languages.
There is an alternative notation to the Void keyword -
empty parentheses, which also indicate that the function is not possible
rotates the value. For instance:
func printMessage () {
print ("Message received")
}
Let's write a function that has one input parameter using in her body (Listing
11.2). As an argument, the function takes flashes the response code from the
server and prints to the console a line containing supporting information.
Listing 11.2
Console :
Listing 11.3
Amount – 153
When calling the function, the name of the argument a is not specified, but is
specified
names are given for all the remaining input parameters. Wherein
all parameters are passed exactly in the order in which they were
described.
NOTE About the names of which parameters you need to specify, you should
Xcode will say, or rather, the code completion mechanism. We met him
in the first part of the book.
All input parameters of the function are constants. When trying to change
When changing their values inside the function body, an error occurs. When
you can use var keyword before
parameter name to create a variable copy of the parameter, before-
editable (Listing 11.5).
Listing 11.5
1 func generateString (code: Int, var _ text: String)
2
-> String {
3
text + = String (code)
4
return text
5}
6 generateString (200, "Code:")
"Code: 200"
The generateString () function takes a text parameteras input . So
as before it is the var statement , in the body of the function automatically
a copy of it is created and available for editing.
NOTE All changes made to the copy of the input parameter are destroyed after
shutting down the function. Variable copies only exist while you are
completing the function code.
Pass-through parameters
As we found out, parameter copy variables can exist
only in the body of the function, and their values disappear as soon as the
function
completes its work. For input arguments to be saved
its values even after the function call completes, you must
use pass-through parameters .
To convert an input parameter to a pass-through, before specifying it,
With this you must specify the inout modifier . Pass-through parameter
passed to a function, changed in it, and returned from the function,
replacing the original value of the input argument. When you call
the function before the passed argument value must
use an ampersand ( & ) to indicate that the parameter
passed by link. The function in Listing 11.6 receives two
parameter and swaps their values.
Listing 11.6
1 func changeValues (inout a: Int, inout _ b: Int) -> () {
2
let tmp = a
3
a=b
4
b = tmp
5}
6 var a = 150, b = 45
7 changeValues (& a, & b)
8a
45
9b
150
Console :
Listing 11.8
Console :
Console :
(150, "Correct")
As the return type of the getCodeDescrip function
tion () specifies the type of a tuple containing two values: the code and its description
sanitation.
The getCodeDescription () function can be improved by not specifying
just the type of the returned tuple, and the names of its elements (Lis-
Thing 11.10).
Listing 11.10
1 func getCodeDescription (code: Int)
2
-> (code: Int, description: String) {
3
let description: String
4
switch code {
five
case 1 ... 100:
6
description = "Error"
"Error"
7
case 101 ... 200:
8
description = "Correct"
nine
default:
ten
description = "Unknown"
eleven
}
12
return (code, description)
(.0 45, .1 "Error")
13 }
14 let request = getCodeDescription (48)
(.0 45, .1 "Error")
15 request.description
"Error"
16 request.code
45
The value obtained during the work of the getCodeDescription () function
is written to the constant request , which has properties
description and code , which correspond to the names of the elements returned
my tuple.
Consider a real-world example that might require
writing a function. Suppose you have a virtual co-
silk. At any given time, it contains bills of various
advantages: from 50 to 5000 rubles. You need to write a function
ttion that will calculate the total amount of money in the wallet
(Listing 11.11).
Listing 11.11
1 func sumWallet (wallet: [Int]) -> Int {
(2 times)
2
var sum = 0
3
for oneBanknote in wallet {
4
sum + = oneBanknote
(19 times)
five
}
6
return sum
(2 times)
7}
8 // wallet with bills
9 var wallet = [50, 100, 100, 500, 50, 1000, [50, 100, 100, 500, 50,
5000, 50, 100]
1,000, 5,000, 50, 100]
10 // count the sum of all bills
11 sumWallet (wallet)
6950
12 // add a new bill
13 wallet.append (1000)
[50, 100, 100, 500,
50, 1,000, 5,000, 50,
100, 1,000]
14 // calculate the amount again
15 sumWallet (wallet)
7 950
At any time, you can call the sumWallet () function and pass
into it a wallet array. This will return a value of type
Int , reporting the amount of money in the wallet.
Default values for arguments
For any input parameter, you can specify its external name, that is
the name that is specified when calling the function. It is written before
the internal name of the argument and is separated from it by a space (lis-
Thing 11.13).
Listing 11.13
1 func sumWallet (banknotsArray wallet: [Int]? = Nil) ->
Int? {
2
var sum = 0
3
if wallet == nil {
4
return nil
five
}
6
for oneBanknote in wallet! {
7
sum + = oneBanknote
8
}
nine
return sum
10 }
11 // count the sum of all bills
12 sumWallet (banknotsArray: [50, 100, 100, 500, 50, 1000, 6 950
5000, 50, 100])
The wallet input parameter now has the external name banknotsArray ,
therefore, when calling the sumWallet () function, you must specify not
only the parameter value, but also its external name.
External names of input parameters are used to hide
their internal names. For example, as external you can use
use long names that are logical for the developer,
and as internal - abbreviated.
Function as argument
NOTE A function in Swift has its own functional data type! Surprised
us? The data type of a function is denoted by a construct pointing to
type of input and output values.
If the function accepts and returns nothing, then its type is indicated as follows:
() -> ()
If the function accepts an array of integer values as input, and returns
an optional two-string tuple, then its data type looks like
in the following way:
([Int]) -> (String, String)?
On the left side of this construction, the types of input parameters are indicated, in the right
howl - types of output values.
Using input parameters does not end with transmission
them of values of fundamental types, arrays and tuples. Previously
we have already passed the value returned by the function as
values for the argument. Now we will learn to convey not meaning
return, but the function itself.
The passed function can be used in the body of the function
to which the transfer takes place.
To pass a function as a value, you must specify a function
the functional type of the accepted function as the type of the accepted
the same argument. Let's write a new function generateWallet () , which
randomly generates an array of banknotes. She must accept
mother at the entrance the required number of banknotes in the wallet. Also
we
Let's rewrite the sumWallet () function so that it takes
la at the input is not an array of values, but the generateWallet () function , and
itself
Generated an array of random banknotes (Listing 11.14).
Listing 11.14
1 import Foundation
2 // function for generating a random array of banknotes
3 func generateWallet (walletLength: Int)
4
-> [Int] {
five
// existing types of banknotes
6
let typesOfBanknotes = [50, 100, 500, 1000, 5000]
7
// array of banknotes
8
var wallet: [Int] = []
nine
// cycle for generating an array of random banknotes
ten
for _ in 1 ... walletLength {
eleven
let randomIndex = Int (arc4random_uniform (
UInt32 (typesOfBanknotes.count-1)))
12
wallet.append (typesOfBanknotes [randomIndex])
thirteen
}
fourteen
return wallet
15 }
16 // function of counting money in the wallet
Listing 11.15
1 // text output function
2 func printText ()
3
-> (String) {
4
return "A very good book"
5}
6 // function that returns a function
7 func returnPrintTextFunction ()
8
-> () -> (String) {
nine
return printText
10 }
eleven
12 let newFunctionInLet = returnPrintTextFunction () () -> String
13 newFunctionInLet ()
"Very good
book"
function, you don't have to return it from another function. For this
you can create an unnamed function and pass it as a value
into a variable or constant. Unnamed functions have no names.
They consist only of the function body (Listing 11.16).
Listing 11.16
1 // unnamed function as value
2 let functionInLet = {return true}
3 functionInLet ()
true
The created constant has the functional type () -> Bool and stores
in itself a body of function.
Unnamed functions are called closure expressions , or
closures . Closing expressions may not just do
any action, but also take input parameters as well
return arbitrary values. We will talk about this in the next
the next chapter.
NOTE A function is a reference type, that is, it is passed by reference.
For instance:
let trueFunc = {return true}
let anotherTrueFunc = trueFunc
Here the constants trueFunc and anotherTrueFunc point to the same function.
Listing 11.17
Exercise 1
Assignment 2
12 Closures
We have already met with the concept of closures while studying the function
tions. There they were presented as unnamed functions. how
Apple explained in the documentation for the Swift language, closures
(closures) -
these are organized blocks with specific functionality that
can be passed around and used in your code.
Agree, not a very accessible explanation. Let's try it differently.
Closures are containerized code that can
be passed as an argument and reused.
NOTE If you have previously programmed in other languages, then analog
closures for you can be blocks (in C and Objective-C), lambda expressions, and ano
small functions.
2. Create one function with a common code for all selection functions
and two functions that check the conditions. As an argument in os-
transfer the required function of checking the condition
viya, that is, pass a function as an argument.
If we take the first path, then with an increase in the number of times
personal conditions of selection, a single function will grow and at the end
ends will become unreadable and too complex. Therefore, use
We use the second option (Listing 12.3).
Listing 12.3
SYNTAX
{(input_arguments) -> ReturnValueType in
body_closing_expression
}
The closing expression is written in curly braces. After specifying the list
input arguments and return type are set with the in keyword,
followed by the body of the closure.
In accordance with the third property of closures, the closing leads
Parameters can be passed as arguments. Let's call
the previously written handle () function , passing it a closure
expression as an input parameter (Listing 12.4).
Listing 12.4
1 // selection of banknotes with denominations over 1000 rubles
2 handle (wallet, closure: {(banknot: Int) -> Bool in
3
return banknot> = 1000
4 })
5 // selection of banknotes worth 100 rubles
6 handle (wallet, closure: {(banknot: Int) -> Bool in
7
return banknot == 100
8 })
As a result, the need for the functions compare100 ()
and compareMore1000 () disappears, since the code of the checking function
passed directly as the argument to the closure .
There is an increase in flexibility and a decrease in the amount of code.
Listing 12.8
Listing 12.9
Console :
Closing expression
Since this closing expression has no input parameters
and the return value, then its functional type is () -> () .
To call a closed expression written in a constant, you need to
you need to write the name of the constant with parentheses, that is, exactly
just like we call functions.
In order to pass some values to the closure (as
input parameters), it is necessary to describe them in the functional
the type of this constant. When describing, you can use all the
previously available features: internal and external names, data type,
and so on. To access the values of the input arguments inside a closure
the expression must use abbreviated names
access ( $ 0 , $ 1 , etc.), as shown in Listing 12-11.
Listing 12.11
1 var sum: (numOne: Int, numTwo: Int) -> Int = {
2
return $ 0 + $ 1
3}
4 sum (numOne: 10, numTwo: 34)
Here the closure expression stored in the constant sum takes-
takes two input arguments of type Int and returns their sum.
1 var a = 1
2 var b = 2
3 let closureSum: () -> Int = {
4
return a + b
5}
6 closureSum ()
7a=3
8b=4
9 closureSum ()
Listing 12.13
1 var a = 1
2 var b = 2
3 let closureSum: () -> Int = {
4
[a, b] in
five
return a + b
6}
7 closureSum ()
8a=3
9b=4
10 closureSum ()
The closure stored in the constant closureSum adds values
variables a and b . When these values change, the return value
by clicking, the meaning also changes.
13 .1. Instances
Properties are data stores, that is, they are the same
most variables and constants, but with limited access: they
only available through an instance.
In addition to properties, methods can be defined on an instance. Me-
todes, and we have already met with them many times, are functions
which are defined within object types. Automobile class could
would have the following methods: beep, start, accelerate:
a.startEngine ()
a.accelerate ()
a.beep ()
13 .2. Namespaces
14 Enumerations
Let's move on to studying the mechanisms for creating object data types.
Let's start with the simplest of them - enumeration. This is very important
and an interesting topic in Swift.
enum EnumerationName {
case Value1
case Value2
...
}
SYNTAX
ATTENTION Members of the enumeration are not values of any data type.
data such as String or Int. Therefore, the values in the following variables currency1
and currency2 are not equivalent:
var currency1 = CurrencyUnit.Rouble
var currency2 = "Rouble"
NOTE In early versions of Swift in the results area, instead of the defined
the enumeration value was displayed.
1 enum CurrencyUnit {
2
case Rouble (сountrys: [String], shortName: String)
3
case Dollar (сountrys: [String], shortName: String)
4
case Euro (сountrys: [String], shortName: String)
5}
Listing 14.7
nine
case Rouble (сountrys: [String], shortName: String)
ten
case Dollar (сountrys: [String], shortName: String, national:
DollarCountrys)
eleven
case Euro (сountrys: [String], shortName: String)
12 }
Console :
enumeration members
As an alternative to the associated parameters for enumerated members
they can be assigned associated values of some data type
(such as String , Character, or Int ). As a result, you get a member
an enumeration and its associated value.
NOTE Linked values are also referred to as raw or raw values.
But in this case, the term “connected” reflects their intentions much better.
value.
For the first member of the enumeration, as the initial value, specify
given an integer 1. For each next term, the value is increased
per unit, since nothing else is indicated: for Venus it is 2, for
Earth - 3, etc.
Listing 14.14
1 var iAmHappy = Smile.Joy
Joy
2 iAmHappy.rawValue
":)"
As a result of using the rawValue property , we get the original
the value of the Joy member of type String .
The rawValue property can be used for more than just getting
associated value, but also to instantiate the enumeration. For
this needs to call the enumeration initializer and pass
it needs an initial value (Listing 14.15). In this example
an instance is created containing a pointer to the third planet
from the sun. This is done by the previously created enumeration Planet .
Listing 14.15
members and other enumerations, but also methods. Methods are functions
inside enumerations, structures and classes, so their syntax and
the capabilities are identical to the syntax and capabilities of the functions.
Let's go back to the example with the Smile enumeration and create a method
that
ry displays help information about the destination to the console
enumerations (Listing 14.17).
Listing 14.17
Console :
Listing 14.19
1 enum ArithmeticExpression {
2
// addition operation
3
case Addition (Int, Int)
4
// subtraction operation
five
case Substraction (Int, Int)
6}
7 var expr = ArithmeticExpression.Addition (10, 14)
Addition (10, 14)
1 enum ArithmeticExpression {
2
case Addition (Int, Int)
3
case Substraction (Int, Int)
4
// counting method
five
func evaluate () -> Int {
6
switch self {
7
case .Addition (let left, let right):
8
return left + right
nine
case .Substraction (let left, let right):
ten
return left - right
eleven
}
12
}
13 }
14 var expr = ArithmeticExpression.Addition (10, 14) Addition (10, 14)
15 expr.evaluate ()
24
1 enum ArithmeticExpression {
2
case Number (Int)
3
indirect case Addition (ArithmeticExpression,
ArithmeticExpression)
4
indirect case Subtraction (ArithmeticExpression,
ArithmeticExpression)
five
func evaluate (var expression: ArithmeticExpression? =
nil) -> Int {
6
expression = (expression == nil? self: expression)
7
switch expression! {
8
case .Number (let value):
nine
return value
ten
case .Addition (let valueLeft, let valueRight):
eleven
return self.evaluate (valueLeft) +
self.evaluate (valueRight)
12
case .Subtraction (let valueLeft, let valueRight):
thirteen
return self.evaluate (valueLeft) -
self.evaluate (valueRight)
fourteen
}
fifteen
}
16 }
17 var expr = ArithmeticExpression.Addition (.Number (20),
.Subtraction (.Number (10), .Number (34)))
18 expr.evaluate ()
-4
Here, the enumeration has a new member, Number . He defines-
is an integer. For members of arithmetic operations, use
but the indirect keyword , allowing you to pass a value like
ArithmeticExpression as an associated parameter.
The evaluate method accepts an optional value of type as input
ArithmeticExpression? ... The optional in this case allows you to call
method without passing it the instance from which this method was extracted
called. Otherwise, the last line of the listing would look like
in the following way:
expr.evaluate (expr)
Agree that the existing option is much more convenient.
The switch statement , using forced retrieval, determines
which member of the enumeration is passed, and returns the corresponding
value.
As a result, this enumeration allows you to simulate any
an operation in which the addition and subtraction operators are present.
Swift enums are more powerful than similar mechanisms in others.
programming languages. You can create properties and methods,
apply extensions and protocols to them, and do a lot
other. We will talk about all this in the near future.
The task
Structures are declared using the struct statement followed by the name of the created-
given structure. The requirements for a name are exactly the same as for a name.
no enumeration: it must be written in the top camelcase style.
The body of the structure is enclosed in curly braces and can contain methods and properties.
state.
Listing 15.2
1 struct PlayerInChess {}
2 var oleg: PlayerInChess
Since the structure is a data type, using data
new storage type can be declared.
Declaring properties
struct StructureName {
var property1: DataType
let property2: DataType
// ...
}
Listing 15.3
1 struct PlayerInChess {
2
var name: String
3
var wins: UInt
4}
The name and wins properties characterize the player's name and number of
wins
play chess.
Structures, like enumerations, have a built-in initial
An alizer that does not need to be declared. This initializer
takes the values of all properties of the structure as input, produces them
initialize and instantiate the structure (Listing 15.4).
Listing 15.4
Listing 15.5
1 struct PlayerInChess {
2
var name = ""
3
var wins: UInt = 0
4}
5 var oleg = PlayerInChess (name: "Oleg", wins: 32)
PlayerInChess
6 var maks = PlayerInChess ()
PlayerInChess
Listing 15.6
1 struct PlayerInChess {
2
var name: String
3
var wins: UInt = 0
4}
5 var oleg = PlayerInChess (name: "Oleg")
6 // access to property
7 oleg.name
8 oleg.wins = 20
This access method provides not only reading, but also modification.
property values of the structure instance.
When creating a new instance, you will only have access to development
the initializer you specified. The parameter value is passed to it
name , so you don't need to set a default value for it.
Instance properties are accessed to change them
using self .
Method declaration
Console :
Modifying methods
1 struct PlayerInChess {
2
var name: String
3
var wins: UInt
4 // method that changes the value of the wins property
5 mutating func addWins (countOfWins: Int) {
6 self.wins + = countOfWins
7}
8}
9 var oleg = PlayerInChess (name: "Oleg", wins: 15)
10 oleg.wins
11 oleg.addWins (3)
12 oleg.wins
Listing 16.1
1 class Chessman {
2
let type: String
3
let color: String
4
var coordinates: (String, Int)? = nil
five
let figureSymbol: Character
6
init (type: String, color: String, figure: Character) {
7
self.type = type
8
self.color = color
nine
self.figureSymbol = figure
ten
}
11 }
12 var kingWhite = Chessman (type: "king", color: "white", figure: " ♚ ")
Listing 16.4
1 class Chessman {
2
let type: ChessmanType
3
let color: CheesmanColor
4
var coordinates: (String, Int)? = nil
five
let figureSymbol: Character
6
init (type: ChessmanType, color: CheesmanColor, figure:
Character) {
7
self.type = type
8
self.color = color
nine
self.figureSymbol = figure
ten
}
eleven
init (type: ChessmanType, color: CheesmanColor, figure:
Character, coordinates: (String, Int)) {
12
self.type = type
thirteen
self.color = color
fourteen
self.figureSymbol = figure
fifteen
self.setCoordinates (char: coordinates.0, num:
coordinates.1)
sixteen
}
17
func setCoordinates (char c: String, num n: Int) {
eighteen
self.coordinates = (c, n)
19
}
20
func kill () {
21
self.coordinates = nil
22
}
23 }
24 var QueenBlack = Chessman (type: .Queen, color: .Black, figure:
"♛ ", coordinates: ("A", 6))
Since the code for setting coordinates has already been written in the setCoordi
na-
tes () ,
then in order to avoid duplication in the initializer this method
will just be called.
When declaring a new instance, the autocomplete window will show
offered a choice of two initializers declared in the class
Chessman .
We will consider all the subtleties of working with initializers in a separate
section of the book, and now I advise you to save the developed designs
in a separate file, since we will return to them in the future.
1 class Chessman {
2
enum ChessmanType {
3
case King
4
case Castle
five
case Bishop
6
case pawn
7
case Knight
8
case Queen
nine
}
ten
enum CheesmanColor {
eleven
case Black
12
case White
thirteen
}
fourteen
let type: ChessmanType
fifteen
let color: CheesmanColor
sixteen
var coordinates: (String, Int)? = nil
17
let figureSymbol: Character
eighteen
init (type: ChessmanType, color: CheesmanColor, figure:
Character) {
19
self.type = type
20
self.color = color
21
self.figureSymbol = figure
22
}
23
init (type: ChessmanType, color: CheesmanColor, figure:
Character, coordinates: (String, Int)) {
24
self.type = type
25
self.color = color
26
self.figureSymbol = figure
27
self.setCoordinates (char: coordinates.0, num:
coordinates.1)
28
}
29
func setCoordinates (char c: String, num n: Int) {
thirty
self.coordinates = (c, n)
31
}
32
func kill () {
33
self.coordinates = nil
34
}
35 }
CheesmanColor and ChessmanType structures are now nested to the Chessman class .
Stored properties
A stored property is a constant or a variable declared in an object type and
storing a specific value. Stored property state has the following capabilities:
❑ Gets the default value if, when creating
instance, no value is passed to it.
❑ Gets the value in the initializer passed on creation
instance as an input argument. This value is
called the original.
❑ Changes the value while using the instance.
We have already declared stored properties many times.
SYNTAX
Computed properties
We met with computable properties in the study of re-
numbers, for which this is the only available
kind of properties.
Computed properties do not actually store a value, but compute
it with a closure expression.
SYNTAX
1 class AboutMan {
2
let firstName = "Egor"
3
let secondName = "Petrov"
4
var wholeName: String {return self.firstName + "" +
self.secondName}
5}
6 var Me = AboutMan ()
7 Me.wholeName
The value of the wholeName property is identical to the corresponding value
from the previous example.
SYNTAX
Getter and setter are defined inside the body of a computed property. In this case,
use the get and set keywords, respectively, followed by the curly
the parentheses follow the body of each function.
The getter is triggered when a property value is requested. For correct-
no work, it should return a value using the operator
return .
The setter is triggered when trying to set a new value to a property.
Therefore, you must specify the name of the parameter, which will be loaded
the set value is written. This parameter is local
for the body of the set () function .
If the computed property does not have a setter, that is, it is implemented
only getter, you can use the simplified notation syntax.
In this case, the set keyword is omitted and only
the body of the enclosing expression. We met this format in the pre-
in the previous examples.
Let's look at an example. A circle on a plane can have three axes
main characteristics: center coordinates, radius and length of the circle
nosti. The radius and length have a rigid relationship. For storage
one of them we use a computed property with implemented
a getter and a setter (Listing 17.3).
Listing 17.3
1 struct Circle {
2
var coordinates: (x: Int, y: Int)
3
var radius: Float
4
var perimeter: Float {
five
get {
6
return 2.0 * 3.14 * self.radius
7
}
8
set (newPerimeter) {
nine
self.radius = newPerimeter / (2 * 3.14)
ten
}
eleven
}
12 }
Listing 17.5
1 struct Circle {
2
var coordinates: (x: Int, y: Int)
3
var radius: Float {
4
willSet (newValueOfRadius) {
five
print ("Instead of \ (self.radius), the
value \ (newValueOfRadius) ")
6
}
7
didSet (oldValueOfRadius) {
8
print ("Instead of \ (oldValueOfRadius),
value \ (self.radius) ")
nine
}
ten
}
eleven
var perimeter: Float {
12
get {
thirteen
return 2.0 * 3.14 * self.radius
fourteen
}
fifteen
set {
sixteen
self.radius = newValue / (2 * 3.14)
17
}
eighteen
}
19 }
20 var myNewCircle = Circle (coordinates: (0,0), radius: 10)
21 myNewCircle.perimeter // prints 62.8
22 myNewCircle.perimeter = 31.4
23 myNewCircle.radius // prints 5
Console :
The value is set to 5.0 instead of 10.0
Set to 5.0 instead of 10.0
Observers are called not only when directly measuring
setting the value of a property outside the instance. Since the property setter
perimeter also changes the value of the radius property , then observers
print the corresponding result to the console.
Type properties are declared using the static keyword for all three
object types. The only exceptions are those marked with the word class
computed class properties that should be able to be overridden
in the subclass. We'll talk about what a subclass is later.
Let's create a structure to demonstrate how type properties work (see
Thing 17.6). The AudioChannel class models an audio channel that has
there are two parameters:
❑ The maximum possible volume is limited for all channels
generally.
❑ The current volume is limited by the maximum volume.
Listing 17.6
1 struct AudioChannel {
2
static var maxVolume = 5
3
var volume: Int {
4
didSet {
five
if volume> AudioChannel.maxVolume {
6
volume = AudioChannel.maxVolume
7
}
8
}
nine
}
10 }
11 var LeftChannel = AudioChannel (volume: 2)
12 var RightChannel = AudioChannel (volume: 3)
13 RightChannel.volume = 6
14 RightChannel.volume // prints 5
15 AudioChannel.maxVolume // outputs 5
16 AudioChannel.maxVolume = 10
17 AudioChannel.maxVolume // prints 10
18 RightChannel.volume = 8
19 RightChannel.volume // prints 8
We used the AudioChannel type to create two channels: the left
and right. Volume property cannot be set to 6 because
it exceeds the values of the property of type maxVolume .
Note that when accessing a type property, the
not the name of an instance of a given type, but the name of the type itself.
18 Subscripts
We met with the concept of a subscript when we studied the topic of access
to the elements of the array. There, the subscript was indicated for
accessing a specific value index. However, the subscripts
They also make it easier to work with structures and classes.
With the help of subscripts, structures and classes can be turned into non-
some semblance of collections, that is, we can access properties
instance using keys.
Suppose we have developed a Chessboard class that models
the essence of the "chessboard". An instance of the Chessboard class is kept
in the variable desk :
var desk: Chessboard
One of the properties of this instance contains information
about which square of the field is occupied by which chess piece. For pre-
a stupa for information on a specific cell we can develop a special method,
which as input pairs meters will be transmitted coordinates:
desk.getCellInfo ("A", 5) With the help of subscripts, you can organize access to the
cells of the cell
ki, passing coordinates like an array key directly
class instance:
desk ["A", 5]
NOTE Subscripts are available for structures and classes.
SYNTAX
Subscripts are declared in the body of a class or structure using the keyword
subscript. Next are the input arguments (exactly the same as for methods)
and the value type. Input arguments are values that are passed as
keys. The value type indicates the data type of the set (in the case of a set
pa) or return (in case of a getter) value.
The body of the subscript is enclosed in curly braces and consists of a getter and a setter by
analogy with calculated values. Getter executes code when requesting value
using subscript, setter - when trying to set a value.
The setter also makes it possible to additionally specify the parameter name,
which will be assigned a new value. If this parameter is not
will be specified, then the new value is automatically initialized by the lo-
the local variable newValue . In this case, the data type of the parameter will be
match the return type.
The setter is optional and, if not present, can
the abbreviated syntax can be used:
subscript (input_parameters) -> return_value {
// getter body
}
Subscripts support overloading, that is, within one
object type, an arbitrary number of
subscripts that differ only in the set of input arguments.
To study subscripts, let's go back to the topic of chess and create a class,
describing the entity "checkerboard" (Listing 18.1). When the
the work of the chessboard model, one of the most
important characteristic: a collection of game cells with an indication of
formations about the chess pieces located on them. Do not forget,
that the playing field is a matrix consisting of individual cells.
In this example, the previously created Chess type will be used .
man describing a chess piece, including nested
transfers.
When developing a class, we implement a method that sets the
the piece given to him on the playing field. At the same time, it is worth remembering two
points:
❑ The piece may have already been on the field, which means it is required
delete from the old position.
❑ The figure has the coordinates property , which is also necessary
change.
Listing 18.1
1 class gameDesk {
2
var desk: [Int: [String: Chessman]] = [:]
3
init () {
4
for i in 1 ... 8 {
five
desk [i] = [:]
6
}
7
}
8
func setChessman (chess: Chessman, coordinates: (String, Int)) {
nine
if let oldCoordinates = chess.coordinates {
ten
desk [oldCoordinates.1]! [oldCoordinates.0] = nil
eleven
}
12
self.desk [coordinates.1]! [coordinates.0] = chess
thirteen
chess.setCoordinates (char: coordinates.0, num:
coordinates.1)
fourteen
}
15 }
16 var game = gameDesk ()
17 game.setChessman (QueenBlack, coordinates: ("B", 2))
18 game.setChessman (QueenBlack, coordinates: ("A", 3))
The gameDesk class describes the game board. Its main and only
ny property is a collection of cells on which they can
lag chess pieces (instances of the Chessman class ).
When instantiated, the desk property is set to
the default is "empty dictionary". While the initializer is running
The task
As you may have noticed, the subscript setter in object
type gameDesk will not be able to correctly handle the situation when
it is passed nil as its value . Although the figure of good
will be removed from the corresponding desk property node ,
The coordinates property of this shape will remain unchanged.
1. Change the subscript so that it is correct
handled the removal of a piece from the chessboard. Not for-
Be it that the Chessman class has a kill () method.
2. Implement the method printDesk () in the class gameDesk , outputting
to the console, the text image of the chessboard is
measured in the following form:
1____
♔
___
2________
3________
4________
five _ _ _ _ _ _ _ _
6________
7________
8____ ♚ ___
ABCDEFGH
In this case, images should be displayed (property symbol
Chessman class ) corresponding to each chess piece
noah board.
19 Inheritance
One of the main goals of the object-oriented approach is
Xia code reuse. Combine code for it many- multiple uses allow closures and
object data types.
them. In OOP methodology, in addition to instantiating various
enumerations, structures and classes, it is possible to create a new class based
on an existing one with automatic in it all the properties, methods and
subscripts of the parent class.
This approach is called inheritance . In terms of inheritance, the "old" class is
called the superclass (or base class), and "new" is a subclass (or a subclass, or
derived class).
To inherit one class from another, you must specify the name of the su-
perclass, separated by a colon after the name of the declared class.
SYNTAX
Listing 19.1
1 // superclass
2 class Quadruped {
3
var type = ""
4
var name = ""
five
func walk () {
6
print ("walk")
7
}
8}
9 // subclass
10 class Dog: Quadruped {
eleven
func bark () {
12
print ("woof")
thirteen
}
14 }
15 var dog = Dog ()
16 dog.type = "dog"
17 dog.walk () // prints walk
18 dog.bark () // prints woof
The myDog instance allows you to access properties and methods
parent class Quadruped . Additionally, the Dog class extends
own capabilities, realizing an additional
bark () method .
NOTE A class can be a superclass for any number of
subclasses.
Listing 19.2
1 // subclass
2 class Dog: Quadruped {
3
func bark () {
4
print ("woof")
five
}
6
func printName () {
7
print (self.name)
8
}
9}
10 var dog = Dog ()
11 dog.name = "Dragon Wan Helsing"
12 dog.printName () // prints Dragon Wan Helsing
It makes no difference to the class what characteristics it interacts with.
it belongs, own or inherited. This statement is true
sly until the implementation of the inherited characteristics changes.
theristic.
Overriding Methods
Listing 19.4
Overriding initializers
19 .5. Cast
Earlier we have created three classes: Quadruped , Dog and NoisyDog , as well as
the animalsArray array is defined , containing elements of all three
data types. This set of types is a hierarchy
classes, since between all classes you can specify clear dependencies
simosti (who inherits whom). To analyze classes in a single hierarchy
there is a special mechanism called typecasting .
Type checking
Listing 19.7
This code goes through all the elements of the animalsArray array and
trusts them against the Dog class . As a result, it turns out that
only two array elements correspond to it: an instance of the Dog class
and an instance of the NoisyDog class .
Type conversion
As stated repeatedly before, announced and filled
the animalsArray has elements of different data types from one
hierarchical structure. Despite this, upon receipt of the next
th element, you will work exclusively using the me-
The todes of the class specified in the array type (in this case, Quadruped ).
That is, having received an element of type Dog , you will not see it defined in
it
bark () method as Swift assumes you are working exactly
with an instance of type Quadruped .
In order to convert the type and tell Swift that the given
element is an instance of a particular type, used
operator as , more precisely, its two variations: as? and as! ... This operator
is placed after the instance name, followed by the class name,
to which the instance is converted.
There is a difference between both forms of the operator:
❑ as? ClassName returns either an instance of the ClassName type ? (optional-
cash), or nil in case of unsuccessful conversion.
❑ Variant as! ClassName is forced to retrieve
values and returns an instance of type ClassName or calls
error in case of failure.
ATTENTION The data type can only be converted within its own
class hierarchy.
Let's go through the animalsArray again . This time we will
call execute method bark () which doesn't exist in super-
class Quadruped , but is present in the Dog and NoiseDog subclasses ( fox-
Thing 19.8).
Listing 19.8
1 for item in animalsArray {
2
if var animal = item as? NoisyDog {
3
animal.bark ()
4
} else if var animal = item as? Dog {
five
animal.bark ()
6
} else {
7
item.walk ()
8
}
9}
Each element in the animalArray is written to the item parameter .
Further in the body of the loop, this parameter using the as operator ?
tries to convert to each of the data types of our structure
ry classes. If item is converted to type NoisyDog or Dog , then it has
the bark () method becomes available .
20 Aliases Any
and AnyObject
Swift offers two special aliases to make it work
with undefined types:
❑ AnyObject matches an arbitrary instance of any class;
❑ Any corresponds to an arbitrary data type.
These aliases allow you to correctly handle situations,
when the specific name of the type or class is unknown, or
The range of possible types can be heterogeneous.
NOTE The Any alias is incompatible with the Hashable protocol, so the
it is not possible to use type Any where matching is required. It is related
It links, for example, to the keys of dictionaries.
Any casting
Listing 20.2
Console :
an integer value of 0
something else
an integer value of 42
a string value of "hello"
an (x, y) point at 3.0, 5.0
Hello, Troll
Each of the array elements is converted to a specific type when
using the as operator . Moreover, in the switch-case construction, this
the operator does not require you to specify any postfix (exclamation mark-
or question).
AnyObject cast
Sometimes you know for sure that all elements of the AnyObject type are
actually
actually have a certain type. In this case, for analysis
elements of type AnyObject must be cast
(Listing 20.4).
Listing 20.4
1 for object in someObjects {
2
let animal = object as! Dog
3
print ("This is bad Dog")
4}
To keep things short, you can typecast for
transforming the entire array (Listing 20.5).
Listing 20.5
1 for object in someObjects as! [Dog] {
2
print ("This is bad Dog")
3}
21 Initializers
and deinitializers
An initializer is a special method that prepares specific actions when creating
an instance of an object data type.
That is, the initializer is triggered when an instance is created, and when
removing it, the deinitializer is called.
21 .1. Initializers
The initializer sets the initial values stored properties and various settings that
need to be used instance.
Designated initializers
When implementing your own data types, in many cases
you need to create your own initializer, since the classes
and structs have built-in initializers:
❑ classes have an empty built-in init () {} initializer ;
❑ Structures have a built-in initializer that accepts
as input arguments, the values of all properties.
NOTE An empty initializer works without errors only if
especially if the class has no properties or each property has a value
default .
For optional data types, no default value is required,
it matches nil .
Class initializers and structs that set value
property assignments are called designated. You can
design an arbitrary number of designated initializers
Convenient initializers
Initializer inheritance
Failing initializers
In some situations it is necessary to define an object
a type that may fail to instantiate is
called an incorrect set of external parameters, the absence
any external resource or other circumstance. For this
goals are falling through (failable) initializers. They do
can return nil when trying to create an instance. And this is their
main destination.
SYNTAX
init? (options) {
// initializer body
}
To create a failing initializer, use the init?
(with a question mark), which indicates that the returned instance will be
an option or it will not be available at all.
The expression return nil must be present in the body of the initializer.
Let's look at an example of implementing a failing initializer.
Let's create a class describing the "Rectangle" entity. When
creating an instance of this class must be controlled
the values of the passed parameters (height and width) so that they
were necessarily greater than zero. Moreover, in the case of incorrect
parameter values, the program should not terminate with an error.
To solve this problem, we use the failing initial
a lyser (Listing 21.2).
Listing 21.2
1 class Rectangle {
2
var height: Int
3
var weight: Int
4
init? (height h: Int, weight w: Int) {
five
self.height = h
6
self.weight = w
7
if! (h> 0 && w> 0) {
8
return nil
9
}
10
}
11 }
12 var rectangle = Rectangle (height: 56, weight: -32) // returns nil
SYNTAX
21 .2. Deinitializers
Deinitializers are a distinguishing feature of classes.
Deinitializer is automatically called during destruction
an instance of the class. You cannot call the deinitializer by itself
really. One class can have at most one deinitializer.
With a deinitializer, you can, for example, free the is-
resources used by the instance, print a log to the console, or
perform any other actions.
SYNTAX
deinit {
// deinitializer body
}
1 class SuperClass {
2
init? (isNil: Bool) {
3
if isNil == true {
4
return nil
five
} else {
6
print ("Instance created")
7
}
8
}
nine
deinit {
ten
print ("Superclass deinitializer")
eleven
}
12 }
13 class SubClass: SuperClass {
fourteen
deinit {
fifteen
print ("Subclass deinitializer")
sixteen
}
17 }
eighteen
19 var obj = SubClass (isNil: false)
20 obj = nil
Console :
Instance created
Subclass deinitializer
Superclass deinitializer
When creating an instance of the SubClass class, the corresponding
the corresponding message, since this functionality is in the
the failing initializer that follows from the superclass. At the end
program, we delete the created instance, passing it as
nil values . In this case, the output to the console shows that the first
the deinitializer of the subclass is executed, then the deinitializer of the
superclass.
22 Delete
copies and ARC
Any instantiated object data type, as in general
any storage of your program, takes up a certain amount of opera-
active memory. If timely destruction is not carried out
copies and the release of the resources they occupy, then in the program
memory leak may occur.
NOTE A memory leak is a software error that causes unnecessary
it wasting RAM.
One of the means of solving the problem of eliminating memory leaks
in Swift is the use of deinitializers, but the possibilities
Swift doesn't stop there.
22 .1. Destroying instances
Area of visibility
Listing 22.1
1 class myClass {
2
var description: String
3
init (description: String) {
4
print ("Instance \ (description) created")
five
self.description = description
6
}
7
deinit {
8
print ("Instance \ (self.description) destroyed")
nine
}
10 }
11 var myVar1 = myClass (description: "ONE")
12 if true {
thirteen
var myVar2 = myClass (description: "TWO")
14 }
Console :
Listing 22.2
1 class myClass {
2
var description: String
3
init (description: String) {
4
print ("Instance \ (description) created")
five
self.description = description
6
}
7
deinit {
8
print ("Instance \ (self.description) destroyed")
nine
}
10 }
11 var myVar1 = myClass (description: "ONE")
12 var myVar2 = myVar1
13 myVar1 = myClass (description: "TWO")
14 myVar2 = myVar1
Console :
Listing 22.3
1 class Human {
2
let name: String
3
var child = [Human?] ()
4
var father: Human?
five
var mother: Human?
6
init (name: String) {
7
self.name = name
8
}
nine
deinit {
ten
print (self.name + "- deleted")
eleven
}
12 }
13 if 1 == 1 {
fourteen
var Kirill = Human (name: "Kirill")
fifteen
var Olga = Human (name: "Olga")
sixteen
var Aleks = Human (name: "Alexey")
17
Kirill.father = Aleks
eighteen
Kirill.mother = Olga
19
Aleks.child.append (Kirill)
20
Olga.child.append (Kirill)
21 }
Nothing is output to the console, although all operations are performed
in the body of the conditional operator, that is, in the limited area, it is visible
sti. We have created three copies, cross-referenced each
to a friend, but these instances are not deleted in time.
The instances are not deleted because by the time
their scope ends, object references are still
exist, and Swift cannot figure out which links can be removed,
and which are not.
This is a typical example of a memory leak in applications.
ARC concept
ARC in Swift automatically manages the memory it occupies by removing
unused objects. With this mechanism, you can
“Just do programming” without switching to tasks,
which the system automatically solves for you.
As has already been repeated many times, when creating a new repository,
as a value, which is passed an instance of the class, given
the instance is placed in RAM, and records are stored in the storage
a link to it is given. The same instance may be indicated by
an arbitrary number of links, and ARC at any given time
knows how many such references are stored in variables, constants
and properties. Once the last reference to the instance is removed
or its scope ends, ARC will immediately call
deinitializer and destroy the object.
Thus, ARC makes working with Swift even more convenient.
Strong References in Closures
Earlier, we found that using strong links can cause
lead to memory leaks. We also learned that to solve the arisen
weak links can help.
Strong links can also become a source of problems when rewriting them.
giving as input parameters to the closure. Captured cap-
class instances are passed by a strong link and not
released when the closure is no longer in use. Consider
example. Listing 22.5 creates an empty optional closure,
to which, in the limited scope, the value is passed
(the body of the closure).
Listing 22.5
1 class Human {
2
var name = "Human"
3
deinit {
4
print ("Object deleted")
five
}
6}
7 var closure: (() -> ())?
8 if true {
nine
var human = Human ()
ten
closure = {
eleven
print (human.name)
12
}
thirteen
closure! ()
14 }
15 print ("Program finished")
Console :
Man
Program completed
Since the conditional operator limits the scope of the variables
noah human containing an instance of the Human class , then, it would seem, given
the object must be removed along with the end of the conditional statement.
However, the console output shows that the instance is being created, but before
when the program ends, its deinitializer is not called.
The created optional closure uses a strong reference to
an instance of the class created inside the conditional statement. Because
the closure is external to the conditional operator,
and the link is strong, Swift cannot decide on its own
about the possibility of removing the link and the subsequent destruction of the
specimen.
To solve the problem in the closure, you need to capture
variable, indicating that the variable contains a weak
link (Listing 22.6).
Listing 22.6
1 class Human {
2
var name = "Human"
3
deinit {
4
print ("Object deleted")
five
}
6}
7 var closure: (() -> ())?
8 if true {
nine
var human = Human ()
ten
// modified closure
eleven
closure = {
12
[unowned human] in
thirteen
print (human.name)
fourteen
}
fifteen
closure! ()
16 }
Console :
Man
Object deleted
Program completed
Captured parameter human is local to the closure
and a conditional operator, so Swift can self-
It is advisable to make a decision to destroy the link stored in it.
This example uses the unowned modifier because
the object type is not optional.
23 Optional
chains
As you know, optionals can contain some value,
or may not contain it at all. To access optional values
niyam we perform their forced extraction, indicating the sign
exclamations, which in the case of a non-existent value causes
error.
optional chains
Listing 23.4
1 var man = Person ()
2 var residence = Residence ()
3 var room = Room ()
4 man.residence = residence
5 residence.room = room
6 if let square = man.residence? .Room? .Square {
7
print ("Area \ (square) sq.m.")
8 } else {
nine
print ("There are no rooms")
10 }
11 // display the total area
If any of the nodes in the optional sequence man.
residence? .room? .square is missing a value (it is nil ), then op-
The optional bind radio will fail and will
jump to an alternative branch of the conditional statement.
In the case where the optional chain cannot access
to an element, the expression evaluates to nil . To check before-
you can just compare the leading to it
optional chaining with nil .
NOTE The optional sequence could be used
and in the first example with two-level nesting. It would also provide more
a more convenient way to access an instance property.
You can use optional chaining to call properties,
methods and subscripts for any level of type nesting are different
into a friend. This allows you to "wade" through the sub-properties inside the
complex
nested models and check the accessibility
to properties, methods and subscripts of these subproperties.
optional chains
Listing 23.5
1 man.residence? .Room? .Square = 36
2 man.residence? .Room? .Square // prints 36
To solve the problem, an optional chain
ka man.residence? .room? .square , pointing to the desired item.
If at some stage the instance is absent, the program
will not raise an error, but will just not execute the given expression.
optional chains
24 Extensions
Extensions allow you to add new functionality to an existing
the underlying object type (class, structure, or enumeration),
and also to the protocol. Moreover, you can extend data types,
access to the source code of which you do not have (for example, types,
provided by frameworks, or types fundamental to Swift
data).
Listing 24.1
1 extension Double {
2
var km: Double {return self * 1000.0}
3
var m: Double {return self}
4
var cm: Double {return self / 100.0}
five
var mm: Double {return self / 1000.0}
6
var ft: Double {return self / 3.28084}
7}
8 let oneInch = 25.4.mm
9 print ("One foot is \ (oneInch) meters")
10 // displays "One foot is 0.0254 meters"
11 let threeFeet = 3.ft
12 print ("Three feet is \ (threeFeet) meters")
13 // displays "Three feet is 0.914399970739201 meters"
Created computed properties allow you to use fractional
numbers as specific units of measure for length. Added new
properties can be applied to parameters and type literals
Double .
This example assumes that 1.0 is a Double from
expresses the value of one meter. This is why property m returns
the value of the self .
Other properties require some transformation before returning
values. One kilometer is the same as 1000 meters
so requesting the km property returns the result of the expression
self * 1000 .
To, after defining new computed properties, use
all their power, you need to create both getters and setters.
The Line structure describes the "Line" entity, in the properties of which
the coordinates of the points of its beginning and end are indicated. Created in
extension, the initializer takes an instance of the Line class as input
and calculates the required value based on the values of its properties.
When developing a new initializer in an extension, be extremely
be careful to ensure that each of the
properties had a certain value.
Listing 24.3
1 extension Int {
2
func repetitions (task: () -> ()) {
3
for _ in 0 .. <self {
4
task ()
five
}
6
}
7}
8 3.repetitions ({
nine
print ("Swift")
10 })
11 / * outputs
12
Swift
thirteen
Swift
fourteen
Swift * /
To change the properties of enumerations and structures implemented by
expanding methods, you must not forget to use modifi-
cator mutating . The following example implements the square () method ,
which squares the eigenvalue of the instance. So
as the Int type is a structure, then to change the eigenvalue
the instance must use the mutating keyword
(Listing 24.4).
Listing 24.4
1 extension Int {
2
mutating func square () {
3
self = self * self
4
}
5}
6 var someInt = 3
7 someInt.square ()
25 Protocols
In the process of learning Swift, we have already repeatedly met with
tokols, but each time we touched them indirectly, without detailed
studying the mechanisms of interaction with them.
Protocols contain a list of properties, methods and subscripts, which
rye must be implemented in an object type. The protocol itself is not
does not implement any functional, it only is
a kind of set of rules and requirements for the type. Any object
the data type can accept the protocol. The most important function
protocol is to ensure the integrity of object types by
specifying the requirements for their implementation.
Protocols are announced independently of any elements of the
grams, as well as object data types.
SYNTAX
protocol ProtocolName {
// protocol body
}
To declare a protocol, use the protocol keyword, after which
the name of the created protocol is indicated.
In order to accept a protocol for execution by any object type,
it is necessary to write its name separated by a colon immediately after the name of the implemented
type:
struct HostStructureName: ProtocolName {
// structure body
}
After specifying the name of the protocol when declaring an object type, this type is obligatory
must fulfill all the protocol requirements. You can specify an arbitrary number of
quality of the accepted protocols.
If the class not only accepts protocols, but also inherits some class, then
the superclass name must be specified first, followed by a comma separated list
protocols:
Protocol SomeProtocol requires that the host type have been rea-
two mutable ( var ) properties of the Int type have been modified . Moreover,
the property
mustBeSettable must be both accessible and changeable, and the property
doesNotNeedToBeSettable is at least mutable.
The protocol defines the minimum requirements for the type, i.e. the type
data is obliged to implement everything that is described in the protocol, but
it can
It may not be limited to this set of elements. So, for the property
doesNotNeedToBeSettable from the previous listing can be re-
not only the getter, but also the setter is lized.
To indicate a type property in the protocol, use
the static modifier before the var keyword . This requirement
is executed even if the protocol is adopted structured
swarm or enumeration (Listing 25.2).
Listing 25.2
1 protocol AnotherProtocol {
2
static var someTypeProperty: Int {get set}
3}
In this example, a property of type someTypeProperty must be required
necessarily implemented in the receiving data type.
In the following example, we will create a protocol and receive it
requirements class (Listing 25.3).
Listing 25.3
1 protocol FullyNamed {
2
var fullName: String {get}
3}
4 struct Person: FullyNamed {
five
var fullName: String
6}
7 let john = Person (fullName: "John Wick")
This example defines the FullyNamed protocol , which requires
Sets the Person structure to have an accessible fullName property of type String .
Extending protocols
With the help of extensions, we can not only indicate the necessary -
compliance with new protocols, but also to extend the
colas, since protocols are full-fledged data types.
This functionality only appeared in Swift 2.0.
When declaring an extension, you must use the name pro
tokol, and in its body specify a set of requirements with their
implementations.
After extending the protocol, the implementations described in it become
available in instances of all classes that have adopted this pro
execution protocol.
Let's write an extension for the previously implemented TextRepre protocol-
sen table (Listing 25.7).
Listing 25.7
1 extension TextRepresentable {
2
func description () -> String {
3
return "This type supports the TextRepresentable protocol"
4
}
5}
6 5.description ()
The extension adds a new method to the TextRepresentable protocol .
At the same time, we indicated earlier that the Int type corresponds to this
proto-
cola. In this regard, it becomes possible to refer to the specified
method for any value of type Int .
25 .6. Protocol inheritance
1 protocol Named {
2
var name: String {get}
3}
4 protocol Aged {
five
var age: Int {get}
6}
7 struct Person: Named, Aged {
8
var name: String
9
var age: Int
10 }
11 func wishHappyBirthday (celebrator: protocol <Named, Aged>) {
12
print ("Happy Birthday \ (celebrator.name)! You're already \
(celebrator.age)! ")
13 }
14 let birthdayPerson = Person (name: "John Wick", age: 46)
15 wishHappyBirthday (birthdayPerson)
16 // displays "Happy Birthday John Wick! You're 46!"
In this example, two protocols are declared: Named and Aged . Created by
the structure accepts both protocols and fully complies
their requirements.
The input argument to the wishHappyBirthday () function must be
a value that satisfies both protocols. This value
is an instance of the Person structure , which we pass.
NOTE Earlier we met with the Any type. This type allows us to
Send values of arbitrary data type. Any is an empty alias
composition protocol <>, so it supports passing any value.
26 Non-standard types
data and receiving
reference
information
I think that you have seen more than once that Swift is significantly
more interesting than you might have expected when you first met. At times
when developing programs, we find ourselves in such situations, for correct-
whose permission you have to refer to the documentation,
and the effect of solving the problem is delivered to the programmer
a real pleasure.
Let me tell you a secret: Swift has a wide variety of types that are used
depending on the situation.
This chapter is dedicated to understanding what these types are and how to
walk the answer to the question "What to do?", meeting the meaning a data
type completely unknown to you. It has been repeatedly noted in Swift that
"everything is an object", and every This was confirmed on several
occasions: for example, when the usual integer, properties and methods
appeared that allowed to deal with him is no longer just like a number, but
with a rather functional object.
Swift has a very interesting approach to organizing data types.
them. But what exactly is a datatype in essence? A data type is a kind of
functional design that defines the way information is stored
options and operations that can be performed with this information.
In addition to the types themselves, there are constructions that expand their
capabilities opportunities. For example, the Hashable protocol we mentioned
earlier - it is nothing more than an extension of the capabilities of data types.
If
any data type implements this protocol, then it guarantees
is that the function of calculating the hash and matching the elements of the
given
type will execute correctly.
Swift has a CollectionType protocol . He requires con-
structure so that it can store multiple items
in one value, each of which must have a unique
the address. As a result, the requirements of this protocol are fulfilled by the
following
constructs like Array , Set and Dictionary .
Let's go back to the Xcode development environment. In the code area, write
the expression
from listing 26.1.
Listing 26.1
Now hold down the Option key and hover over the name
of data type String . When the pointer icon changes to an arrow view
on the question mark, left-click. Before you will appear
a window with help information about the data type String .
While holding down the Option key , hover over the name of the translation
change str and left-click again. Before you
the familiar window with help information about this
variable (Figure 26.1).
In the Declaration line, click on the word String and Xcode will open
the documentation window for the String data type (Figure 26.2).
NOTE Internet access is required to download documentation. Besides
addition, in Xcode preferences menu you can download documentation for standalone
access to it.
When accessing the keys and values properties, Swift does not return an array,
a set or a dictionary, but some construction that has a data type:
LazyMapCollection <Dictionary <String, String>, String>
Hold down the Option key and click on the variable name
keysOfDict , you can open a window with help information and from
this window go to the documentation related to the construction
LazyMapCollection (Figure 26.3).
Let's figure out what this data type is. Collection word in type name
indicates that this type is a collection of elements.
The full name of the data type is given at the very top of the page:
struct LazyMapCollection <Base: CollectionType, Element>
If you read the description, which is located below on the page
the documentation page, it becomes clear that the value of this type is
is nothing more than a collection that consists of the elements of the base
collection designated as Base . Elements of this collection
are converted by a special function and returned as some
Element element . That is, this function takes in turn at the input
each element of the underlying collection and returns an Element . Probably,
the Element element must describe the return value, or rather -
its data type.
Let's take a look at the Conforms To section . It lists only one protocol
LazyCollectionType . Click on this name to go to the corresponding
the corresponding documentation page (fig. 26.4).
Pay attention to the section Inherits From . It specifies the protocol
CollectionType , which is inherited from LazyCollectionType .
CollectionType is nothing more than a protocol that implements
no requirements for collections of elements already known to us, for example
dictionary, array, or set. We will verify this by going to the page
CollectionType protocol documentation (Figure 26.5).
The Adopted By section presents constructs that do
the requirements of this protocol. Among them are Array ,
and Dictionary and Set .
Figure: 26.5. CollectionType Protocol Documentation
The result is an uncomplicated diagram of dependencies
col and data types shown in fig. 26.6.
protocol
CollectionType
protocol
LazyCollectionType
structs
Array,
Dictionary,
Set
struct
LazyMapCollection
Figure: 26.6. Protocol and data type dependency diagram
It turns out that the constructions Array , Dictionary
and Set , as well as con-
struction LazyMapCollection , implement the same protocol
CollectionType ,which means that they are collections, moreover, quite
possibly compatible.
Let's go back to the description page of the LazyMapCollection type and some
the returned Element . Now we need to find where
the type of this element is specified. It is logical to assume that it depends
from the base collection, that is, in the example in Listing 26.2 from the
dictionary
countryDict . Let's go back to the Xcode window and call the help again.
information about the keysofDict variable . You will see the following
data:
var keys: LazyMapCollection <Dictionary <String, String>, String>
It turns out that the data type of this collection looks like this:
LazyMapCollection <Dictionary <String, String>, String>
The first parameter ( Dictionary <String, String> ) describes
the underlying
a collection, that is, the parsed object, and the second ( String ) is the type
objects in the resulting collection. This is the very element
Element .
It turns out that as a result of calculating the keys and arrays properties,
a collection is rotated whose elements are of the String data type .
Think about other collections that can store elements
this type, you know? These are arrays and sets, of course.
This means that the value obtained in the course of accessing properties can
be
can be converted to an array or set using the appropriate
functions (Listing 26.3).
Listing 26.3
Listing 27.2
Listing 27.3
1 struct IntStack {
2
var items = [Int] ()
3
mutating func push (item: Int) {
4
items.append (item)
five
}
6
mutating func pop () -> Int {
7
return items.removeLast ()
8
}
9}
This type provides work exclusively with values of the type Int . The array [Int]
is used as a storage of elements . Himself the type for interacting with
elements of the collection provides us both methods described earlier.
The disadvantage of the created type is its limitation in relation to the type of
value to use. We implement a universal version type that allows you to work
with any elements of the same type
(Listing 27.4).
Listing 27.4
Listing 27.7
1 func findIndex <T: Equatable> (array: [T], valueToFind: T) -> Int? {
2
for (index, value) in array.enumerate () {
3
if value == valueToFind {
4
return index
five
}
6
}
7
return nil
8}
9 var myArray = [3.14159, 0.1, 0.25]
10 let firstIndex = findIndex (myArray, valueToFind: 0.1) // 1
11 let secondIndex = findIndex (myArray, valueToFind: 31) // nil
The type parameter is written as <T: Equatable> . This means "any
fight a type that supports the Equatable protocol . " As a result, the search
in the passed array is executed without errors, since the data type
Int supports the Equatable protocol , which means values of this type
can be accepted for processing.
1 extension Stack {
2
var topItem: T? {
3
return items.isEmpty? nil: items [items.count - 1]
4
}
5}
The topItem property uses a T type name placeholder to indicate
property type. This property is optional, since the
the stack may not be available. In this case, nil is returned .
Throwing an error on its own does not produce any results. You-
the thrown error must be intercepted and processed correctly.
There are four ways to handle errors in Swift:
❑ Transfer of error.
❑ Error handling by the do-catch statement .
❑ Convert error to optional.
❑ Prohibition of error throwing.
If, when calling any function or method, you know that it
may throw an error, then you must specify before calling
the try keyword .
Now let's look at each of the error handling methods.
Error transmission
When passing an error, a block of code (function, method, or initializer),
that has become the source of the error, does not handle it on its own,
but transfers the above to the code that called the given block of code.
In order to indicate to the block of code that it should transmit air
errors in it, in the implementation of this block after the list
parameters, the keyword throws is specified .
Listing 28.3 shows an example of declaring two functions that
pass the errors that occur in them above.
Listing 28.3
1 func anotherFunc () throws {
2
// function body
3
var value = try someFunc ()
4
// ...
5}
6 func someFunc () throws -> String {
7
// function body
8
try anotherFunc ()
nine
// ...
10 }
11 try someFunc ()
The someFunc () function returns a String value , so the key
chevoe word throws indicated before the return type.
The anotherFunc () function in its body does not itself throw
errors, it can only catch the error thrown by the function
her anotherFunc () . In order to catch the error thrown
inside a block of code, you need to make a call using the
the previously mentioned try statement . Thanks to him, the anotherFunc () function
will be able to react to the error that has arisen as if it itself is
is its source. And since this function is also marked with a key
with throws , it will simply pass the error to the calling code.
If the function is not marked with the throw keyword , then all arises -
It must handle errors inside it on its own.
Consider the example from Listing 28.4.
Listing 28.4
1 struct Item {
2
var price: Int
3
var count: Int
4}
5 class VendingMachine {
6
var inventory = [
7
"Candy Bar": Item (price: 12, count: 7),
8
"Chips": Item (price: 10, count: 4),
nine
"Pretzels": Item (price: 7, count: 11)
ten
]
eleven
var coinsDeposited = 0
12
func dispenseSnack (snack: String) {
thirteen
print ("Dispensing \ (snack)")
fourteen
}
fifteen
func vend (itemNamed name: String) throws {
sixteen
guard var item = inventory [name] else {
17
throw VendingMachineError.InvalidSelection
}
19
guard item.count> 0 else {
20
throw VendingMachineError.OutOfStock
21
}
22
guard item.price <= coinsDeposited else {
23
throw VendingMachineError.InsufficientFunds (coinsNeeded:
item.price - coinsDeposited)
24
}
25
coinsDeposited - = item.price
26
--item.count
27
inventory [name] = item
28
dispenseSnack (name)
29
}
30 }
The Item structure describes one product name from the machine
for the sale of food. The VendingMachine class directly describes
but the apparatus itself. Its inventory property is a dictionary containing
providing information on the availability of certain products. Property
coinsDeposited indicates the number of coins deposited into the machine.
The dispenseSnack () method reports that the device is issuing a certain
product. The vend () method directly serves the purchase of an item
through the device.
Under certain conditions (the requested item is not available, it is not
there are not enough coins in stock or the number of deposited coins
ki) the vend ( ) method may throw an error corresponding to re-
counting VendingMachineError . The method implementation itself uses
a guard statement to implement an early exit with
operator throw . The throw statement instantly changes the flow of work.
grams, as a result, the selected product can only be purchased
in the event that all conditions of purchase are met.
Since the vend () method passes all errors that occur in it,
code, then it is necessary to perform further processing
errors using a try or do-catch statement .
We implement a function that automatically tries to
find a product (Listing 28.5). In this example, the dictionary
favoriteSnacks contains a pointer to each of the favorite snacks
three people.
Listing 28.5
1 let favoriteSnacks = [
2
"Alice": "Chips",
3
"Bob": "Licorice",
4
"Eve": "Pretzels",
5]
6 func buyFavoriteSnack (person: String, vendingMachine:
VendingMachine) throws {
7
let snackName = favoriteSnacks [person] ?? "Candy Bar"
8
try vendingMachine.vend (itemNamed: snackName)
9}
The buyFavoriteSnack () function itself cannot throw an error, but
since it calls the vend () method , the above is not
you must use the throw and try statements .
Do-catch statement
Throwing and passing errors up in the end should lead to them
processing in such a way that it brings a certain benefit
user and developer. To do this, you can use
Operator do-catch statement .
SYNTAX
do {
try CallableBlockName
} catch pattern1 {
// the code...
} catch pattern2 {
// the code...
}
The statement contains a do block and an arbitrary number of catch blocks. In the do block
must contain a function or method call that might throw an error.
The call is made using the try statement.
If an error was thrown as a result of the call, then this error is compared
with templates in catch blocks. If a match is found in one of them, then execute-
code from this block is retrieved.
You can use the where keyword in condition templates.
A catch block can be used without specifying a template. In this case, this
the block matches any error, and the error itself will be in the local
variable error.
We use the do-catch operator to intercept and handle the
Possible errors (Listing 28.6).
Listing 28.6
1 var vendingMachine = VendingMachine ()
2 vendingMachine.coinsDeposited = 8
3 do {
4
try buyFavoriteSnack ("Alice", vendingMachine: vendingMachine)
5 } catch VendingMachineError.InvalidSelection {
6
print ("Invalid Selection.")
7 } catch VendingMachineError.OutOfStock {
8
print ("Out of Stock.")
9 } catch VendingMachineError.InsufficientFunds (let coinsNeeded) {
ten
print ("Not enough funds. Please add more \
(coinsNeeded) coin (s). ")
11 }
12 // displays "Insufficient funds. Please deposit more
2 coin (s). "
In the above example, the buyFavoriteSnack () function iscalled
in the do block . Since the deposited amount of coins is not enough for the
purchase
the customer's favorite candy Alice , the error is returned and
a message corresponding to this error is displayed.
Swift allows you to define a block of code that will only be executed
at the end of the execution of the current part of the program. For this
is the defer statement , which contains a set of deferred expressions
zheniy. With it you can perform the required cleaning
no matter how you exit this part of the program.
Deferred actions are performed in reverse order, i.e.
the block of the last defer statement is executed first , then the
the last, etc.
Let's consider an example of using a block of deferred actions
(Listing 28.9).
Listing 28.9
29 Non-trivial
Using operators
You have already met a large number of operators earlier,
which Swift provides. However, a situation is possible in which
for your own object data types, these operators will
seem useless. In this case, you will need yourself
create your own implementations of standard operators or completely
new operators.
Listing 29.2
1 func + (left: Vector2D, right: Vector2D) -> Vector2D {
2
return Vector2D (x: left.x + right.x, y: left.y + right.y)
3}
4 let vector = Vector2D (x: 3.0, y: 1.0)
5 let anotherVector = Vector2D (x: 2.0, y: 4.0)
6 let combinedVector = vector + anotherVector