Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
48 views

Swift Programming The Ultimate Beginner's Guide To Learn Swift Programming Step by Step, 3

Uploaded by

sreyashgemini
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
48 views

Swift Programming The Ultimate Beginner's Guide To Learn Swift Programming Step by Step, 3

Uploaded by

sreyashgemini
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 344

NLN lnc

Table of contents
Introduction
About Swift
About the book

Part I. Introducing Xcode

Chapter 1. The first steps


eleven . You need a Mac computer
12 . Sign up as an Apple developer
thirteen . Install Xcode

Chapter 2 . Xcode environment and playground projects


2 .1. Introduction to Xcode
2 .2. Playground project interface
2 .3. Playground project capabilities

Part II. Basic Swift Features

Chapter 3 . Starting point


3 .1. Setting and changing the value
3 .2. Variables and constants
3 .3. The rules for declaring variables and constants
3 .4. Display of text information
3 .5. Comments
3 .6. Semicolon
Chapter 4 . Data types and operations with them
4 .1. Types of data type definition
4 .2. Numeric data types
4 .3. Text data types
4 .4. Boolean values
4 .5. Type aliases
4 .6. Comparison operators

Part III. Fixed assets Swift

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 8 . Flow control. Branching


8 .1. Condition statement if
8 .2. Guard statement
8 .3. Range operators
8 .4. The branching operator switch

Chapter 9. Types of collections


9 .1. Arrays
9 .2. Sets
9 .3. Dictionaries

Chapter 10. Flow control. Repetitions


10 .1. The repetition operator for
10 .2. Repeat while and repeat while statements
10 .3. Cycle management

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

Part IV. Non-trivial features of Swift

Chapter 13 . OOP as a foundation


13 .1. Instances
13 .2. Namespaces

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 16. Classes


16 .1. Class syntax
16 .2. Class properties
16 .3. Class methods.
16 .4. Class initializers
16 .5. Nested types.

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 20 . The aliases Any and AnyObject


20 .1. The alias is Any
20 .2. The alias is AnyObject.

Chapter 21 . Initializers and Deinitializers.


21 .1. Initializers.
21 .2. Deinitializers.

Chapter 22 . Removing instances and ARC


22 .1. Destruction of instances
22 .2. Memory leaks
22 .3. Automatic reference counting.

Chapter 23 . Optional Chains


23 .1. Accessing properties through optional chaining
23 .2. Setting values through optional chains
23 .3. Accessing methods through optional chaining.

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

Chapter 26 . Non-standard data types and retrieval


reference information
Chapter 27 . Generic templates
27 .1. Versatile functions
27 .2. Generic types
27 .3. Type constraints
27 .4. Generic extensions
27 .5. Related types

Chapter 28 . Error processing


28 .1. Throwing errors
28 .2. Error processing
28 .3. Delayed cleanup actions.

Chapter 29 . Non-trivial use


operators
29 .1. Operator functions
29 .2. Custom operators
At the annual global platform developer conference
Apple (Worldwide Developers Conference, WWDC) June 2, 2014
The "apple" company pleasantly surprised the iOS-public, installing a new
programming language called Swift.
This came as a big surprise: the maximum that was expected developers
accustomed to a language that is now a thing of the past Objective-C is an
overview of the new features in iOS 8 and new features.
useful programming interfaces for working with them. Recovered
shocked, the developers approached Swift, studying and, of course,
criticizing him. A year later, having released several intermediate
new language versions, on June 8, 2015 Apple announced the release
version with index 2.0, which became available with the final assembly of the
mobile operating system iOS 9. Later, in the middle October of the same
year, version 2.1 was released, bringing development process a number of
significant innovations.
If you've ever written an Objective-C application, then After learning Swift
with its many possibilities, you are probably want to rewrite your
applications in a new programming language- tions . After the release of
1

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.

About the book

Using modern smartphones to address emerging


tasks became the norm. In this regard, many companies pay all
more focus on providing functional access to the services they offer via
mobile applications
(whether it is an optimized internet site that opens in a browser, or special
program). iOS is one of the most popular mobile operating systems in the
world, and in such a situation, the demand for mobile programming is
growing at an unprecedented rate. This book contains comprehensive
information for everyone learning to program in the wonderful Swift
language in order to creating your own iOS applications (as well as OS X-,
watchOS- and tvOS apps). While reading the book, you will come across not
only the theoretical information, but also a large number of practical
examples and tasks, completing which you will deepen your knowledge in
the studied material. Despite the fact that you have a long way to go, this it
will be a rewarding and very important experience. The book doesn't show
you how to write
iOS application, it is designed to learn the language itself grammar Swift. I
believe that it will give you the opportunity to master new language and soon
start writing your own apps for the App Store or Mac App Store. Having
studied the language, into the distance Then you can independently choose
for which platform
create programs - for iOS, OS X, tvOS, or watchOS.
The code examples in this book correspond to Swift version 2.1 or higher,
iOS version at least 9.1 and Xcode version at least 7.1. If you have more
new versions, do not worry, all described material with great
most likely it will work for you without any errors.
But the small possibility that Apple will change slightly
1
The corresponding tests are periodically conducted and posted on its
portal, Primate Tabs is the developer of the popular product tester
Geekbench performance.

Swift syntax does exist. If you meet with such a


tuation, I ask you to treat with understanding and inform me about it
to the email address alkal @ ya .ru .

For whom the book was written

If you answer yes to the following questions

❑ You have at least minimal knowledge of programming in


any high level language?
❑ You want to learn how to create programs for the operating room
iOS systems (for your iPhone and iPad gadget), OS X, watchOS
or tvOS?
❑ Do you prefer practical training to boring and tedious
notonic theoretical lectures?
Then this book is for you.
The material studied in the book is supported by practical homework
tasks. Together we will go from the simplest concepts to
solving the most interesting problems.
Do not be afraid, Swift will not scare you off at all (how could
Objective-C), and the process of creating applications will be very exciting
nude. And if you have an idea for an awesome app, then co-
everyone will soon be able to develop it for modern mobile
iOS system or stationary OS X.
It is very important that you do not let your hands stand idle. Test-
study all the proposed code and complete all tasks, as to learn
Programming just by reading text is not the best way. If
in the course of studying new material, you will have a desire to "play"
with the code from the listings - do it without delay. Comprehend Swift!
Do not be afraid to make mistakes, as while you are learning, mistakes are
yours.
friends. And to correct mistakes and avoid them in the future will help you
this book and the Xcode development environment (we'll talk about it later).
Remember: Becoming a Great Programmer requires
time! Be patient and study the material carefully.
Book structure
The book consists of four large parts
❑ Part I . Familiarity with Xcode . In the first part, you will start your
journey
Journey into the world of Swift, complete the most important and must

steps prior to starting to develop your own applications


niy. Learn how to create your own Apple account
ID, how to connect to the apple developers program, where to get
development environment for Swift applications, how to work with it.

❑ Part II . Basic features of Swift . After getting to know the environment


development of Xcode, allowing you to start learning the language
programming, you will learn the basic features of Swift. You
learn what syntax Swift has, what variables and con-
stants, what types of data exist and how all these
to be involved in developing programs.
❑ Part III . Fixed assets of Swift . The third part focuses on
consideration and study of the most simple, but very interesting
Swift tools. About some of them (for example, about tuples) you,
may have never heard of others (like arrays) you,
probably used in other languages as well.
❑ Part IV . Non-trivial features of Swift . At the fourth hour
methods of working with the most powerful
and Swift functionality. In this chapter, you
you will use it with enviable regularity when creating your own
applications in the future.

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.

12 . Sign up as an Apple developer


The next step is to get an Apple ID account
and registration with the Apple Developer Center. This requires
follow the link https: // developer .apple .com / register / in your browser
(fig. 1.1).
Figure: 1.1. Developer Center login page
NOTE Apple ID is an account that allows you to receive
access to services provided by Apple. You may already have a personal
Apple ID account. It is used, for example, when purchasing mobile devices.
in the AppStore or when working with the iCloud .com portal.
To create a new Apple ID, follow the link
Create Apple ID . To register, you will need to enter the required
data and click on the button at the bottom of the form.
If you already have an Apple ID account, then use the data
your account to sign in to the Developer Center ( Sign In button ).
On the start page of the Developer Center, go to
follow the link SDKs , after which you will have access to all the variety of
options
features of the Apple Developer Center (Figure 1.2).
In the Developer Center, you can access a huge amount of
various documentation, videos, code examples - to everything,
which will help you create great apps.
Registration as a developer is free. That is, everyone
can start developing applications without paying any
drinks (if you do not take into account the cost of the computer) Nevertheless
for $ 99 per year you can participate in a paid program
iOS developers (iOS Developer Program), which offers you
Apple is burning. This is optional, but if you want to test your
applications on real devices (and not only in software
simulator) and use the App Store (mobile app store
zheniy) to distribute their programs, then participation in the program
becomes mandatory.
Personally, I advise you not to think about it for now, since everyone is
iOS development ki can be obtained with a free account
entries and Xcode.

thirteen . Install Xcode


Now all you need to do is download Xcode. To do this go
by the corresponding link in the Developer Center to the page,
sacred to this wonderful development environment (fig. 1.3).
NOTE As you can see, Apple is giving you beta access
versions of Xcode. I strongly discourage using them when learning a language
programming as they are intended for experienced Apple developers.

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

you can test applications directly in Xcode (without the need-


download programs on real devices).
I hope you already have the latest version on your computer.
Xcode, which means we can move on to learning this wonderful
Wednesday. First, you need to launch Xcode. When you first start
ska, you may need to install some additional
packages (everything will pass automatically when you click on the
install ).
After downloading and fully installing Xcode, you can proceed
to using the application development environment. A little later, you co-
create your first project, and for now just take a look at the
There is a start window when Xcode starts (Fig. 2.2).
The start window serves two purposes: creating new projects
and organizing access to those created earlier. In the Xcode start window
two areas can be distinguished. The lower left area represents
a menu consisting of the following items:
❑ Get started with a playground - creating a new playground project.
We will talk about what it is a little later.
❑ Create a new Xcode project - create a new iOS application
or OS X.

❑ Check out an existing project -


connecting an external repository
to search for projects placed in it.
The right part of the window contains a list of previously created projects. In
va-
In this case, if this is your first time launching Xcode, this list will be
empty. But do not worry, soon it will be filled with many
various projects.
NOTE In the names of all projects I create while reading this book, I
Please indicate chapter and / or listing numbers. In the future, this will allow
order in the list of projects and save you from unnecessary headaches.
One of the awesome new features in Xcode 7, in addition to Swift support,
is the emergence of playground projects. A playground project is an in-
terrorist development environment, a kind of "sandbox" or "game
platform ", where you can comfortably test the code you create
and see the result of its execution in real time.
Imagine you need to quickly test a small program.
For this purpose, there is nothing better than a playground project! Example
for-
shown in Fig. 2.3.
As you can see, the appearance of the playground project interface
significantly different from the Xcode workspace you saw
earlier in the book. I repeat that the playground project allows you to write
code
and immediately see the result of its execution, although not accidentally
lives to create full-fledged independent projects. Each
the playground project is stored in the file system as a special file
with the extension of the same name.

2 .2. Playground project interface

There is no better way to learn a programming language than by


writing code. The Playground project is designed to do just that.
Select the Get started with a playground option in the start window to
creating a new playground project. Next Xcode will ask you to enter
the name of the playground project to be created, and also select the platform
for
which you will write the code. There are two platforms to choose from:
iOS and OS X. The only difference is the frameworks available in the
playground.
Change the name to Part 1 Basics, select the iOS platform and click
on the Next button .
Next, you need to select a folder to save the created pro
ect; after selecting a folder, you will see a working interface
playground project (Fig. 2.4).
The working window consists of two parts:
❑ On the left side of the screen is the code editor , in which
you can write and edit your swift code. In just
the file we created has one comment and two lines
code.
❑ Once the code is written, Xcode will immediately process it,
will display errors and display the result on the right side of the screen, in
general
of the results .
Figure: 2.4. Working window of a new playground project
You can see that the result of the generated str variable is displayed
in the results area. In the future, we will write together
code and discuss the results of its execution. Remember that the main
the goal is to improve your Swift proficiency.
If you hover over the line "Hello, playground" in the area
results, then two buttons will appear side by side, as shown in Fig. 2.5.
Figure: 2.5. Additional buttons in the results area
The left button allows you to display the result in a separate pop-up
the first window, the right one - right in the code area. Try clicking on
each of them.
2 .3. Playground project capabilities
Playground projects are an amazing platform for developing
code and writing training materials. Since version 6.3
Xcode now supports markdown syntax for comments
e. In fig. 2.6 is an example of changing the appearance of a comment
After choosing Editor > Show Rendered Markup from the menu .
You will soon see that the results can be
not only text, but also graphic data is found (Fig. 2.7).
Figure: 2.6. Formatted comment
N times format strings in the result area, where N is an integer,
indicate that the given line of code is printed N times. An example is
lines you can see in fig. 2.7. Similar conclusions of the results
can be displayed in the form of graphs and tables. With all the possible
options for displaying the results of swift code execution you will
Get familiar with playground projects in Xcode.
Xcode also has such a useful mechanism in its arsenal as
autocomplete (known as autocomplete in Xcode). For example
in the working part of the newly created playground project on a new
line write the Latin character "a" - you will see that pops up
autocomplete window (fig. 2.8).
All you need is to select the desired option and press the key
input and it will appear in the code editor. List in the autocomplete window
changes depending on the characters you entered. Also, everything is created
elements (variables, constants, types, instances, etc.)
are automatically added to the autocomplete list.
One of the features of Xcode, which greatly simplifies the work
bot is an indication of errors in the program code. For each
errors, detailed auxiliary information is displayed, allowing
to clarify and correct the defect. The error is shown
using the red circle icon to the left of the line, where
she was discovered. Clicking this icon displays a description
errors
Additionally, information about the error is displayed on the console in the
Debug Area. You can display it on the screen by selecting
menu item View > Debug Area > Show Debug Area or by clicking on
the down arrow button in the lower left corner of the area
code. You will regularly interact with the console in the process
development of programs.
NOTE In versions of Xcode below 7, all console information was output
in the Assistant Editor area.
It is possible that the console will be empty when opening the debug area.
The data will appear in it after the first error appears in your code.
or the first display of information on demand.
Swift also allows you to get comprehensive information about
objects used in the code. If you press the Alt key and click
on any object in the code area (for example, on str ), then
an auxiliary window that allows you to find out the type of object, as well as
the name

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".

3 .1. Setting and changing a value


Basic Operators
As stated earlier, operators are minimal autonomous
functional units that execute a command. Opera-
Swift operators allow you to perform various operations, for example
operations on store values. The values that operators
affect in their work, are called operands .
Let's go back to the Dragon Chest example. To increase the co-
the amount of gold in the storage, the command “Add
three coins to the stolen gold. " This command has one statement:
"Add" and two operands: "Stolen gold" and "Three coins".
"Three coins" is also a meaning, but it is not assigned to any
storage, it is transmitted directly.
In total, there are two main types of operators:
❑ Simple operators perform operations on various operands.
ladies. They include unary and binary operators. Unary
operators perform an operation on one operand (for example,
–A ). They can appear before the operand, that is, they can be prefixed
ny (for example ,! b ), or after it, that is, be postfix
(like i ++ ). Binary operators perform an operation with two-
operands (for example, 1 + 6 ). Such an operator always has
is between operands and is called infix.

❑ Structured statements affect the program flow.


Separately, we can single out the ternary operator , which is
performs operations with three operands. Swift has only one
ternary operator - condition operator a? b: c .

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).

NOTE In Swift, the assignment operator does not return an assignable


value, it only performs initialization (setting the value).
In many programming languages, this operator returns an assignable
value and you can, for example, immediately output it to the console, or
use as a condition in a condition statement. In Swift, a similar approach
will throw an error.

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).

NOTE The type defines the contents of the repository.

Since the storage types do not match, their contents cannot be


swap. Imagine what would happen if using the operator
assignments, state the following:
Dragon Chest = Troll Cauldron
Thus, the contents of the boiler will be poured (or appropriated)
the chest, from where it, of course, will instantly flow out and will not
suitable for reception.
Storages work in the same way in the Swift environment, which
has a number of predefined types of these same storages. Each
of types is intended to store a certain kind of values
(numbers, text, boolean and other values).

3 .2. Variables and constants


The most important basic concept in any programming language is
niya is a variable. A variable is some named general
an area (storage) in which some value can be stored.
The value of a variable can change over time. Process
the change in value does not occur by itself, but is initiated from the outside
when initializing a new value.
In the previously considered example with the "Dragon Chest" and "Cauldron
troll "chest and cauldron are variables (or storage), with
holding the meanings "Stolen Gold" and "Yellow Stew"

respectively. If necessary, you can remove gold from the chest


and put other solid objects there, for example, swords defeated-
knights. Thus, the value of the variable will change, but its
the name will remain the same: all swords of the defeated knights will
returned when accessing a storage named "Dragon Chest".
It's time to stretch your fingers a little. The first thing you will learn
when learning Swift, declaring variables. Variables in Swift
are declared using the var statement followed by the name
and the value of this variable.
SYNTAX

var variable_name = variable_value

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.

An example of declaring a variable and initializing its value when


listed in Listing 3.1.
Listing 3.1

1 // variables are declared using the var keyword


2 var dragonsBox = "Stolen Gold"

This example creates a variable named dragonsBox that contains


meaning "Stolen gold" . That is, the code can be read
in the following way:
Declare a variable named dragonsBox and assign a value to it
of "Stolen Gold ' .
In the course of work on the program, you can declare an arbitrary number
the number of variables.
In order to change the value of a variable, you must assign
it has a new meaning. The var operator is not reused.
(Listing 3.2).
NOTE The var statement is used once for each variable
only when it is announced.
Be careful: when initializing a new value, the old
destroyed.

1 // variables are declared using the var keyword


2 var dragonsBox = "Stolen Gold"
3 // change the value of the previously declared variable
4 dragonsBox = "Swords of the Defeated Knights"

As a result of the code execution, the dragonsBox variable will store


the meaning of "Swords of the defeated knights" .
And what will happen if the storage "Dragon Chest" is locked without
opening-
removable lock? In this case, this chest will be forever tied
with the stolen gold and put something else in it (that is,
thread existing value) would be simply impossible.
Swift has the ability to specify the value of a variable once
(hang an unbreakable lock on it) and make it impossible to
change this value in the future. Such variables have their own
name - constants . They are declared using the let statement .
SYNTAX

let constant_name = constant_value


The let statement is followed by the name of the constant being created, with the help of which
it will refer to the value written in the constant. Further,
after the name and the assignment operator, an initialized constant follows
value.
An example of declaring a constant and initializing its value when
listed in Listing 3.3.
Listing 3.3
1 // constants are declared using the let keyword
2 let dragonsBox = "Stolen Gold"
As a result, the dragonsBox constant will be declared containing the value
chenie "Stolen Gold ' . This value cannot be deleted at any
what circumstances.
In other words, a constant is some named store-
the value of which can be set only once. By specifying the value
once, it will not be possible to change it throughout the work
bots of the program.
When trying to change the value of a constant, Xcode will report an error
(Listing 3.4).

Listing 3.4

1 let dragonsBox = "Stolen Gold"


2 dragonsBox = "Swords of the Knights" // ERROR: trying to change a constant

Earlier, programming in other languages, you are probably not very


actively used constants. In Swift to optimize performance
programs, you should use constants in all
cases where the initialized value should not and will not
change while the application is running. Moreover, Xcode keeps track of
whether the value of declared variables changes during operation
program, and, if necessary, notify you that the variable is
preferably change to a constant.
NOTE As mentioned earlier, declaring a variable or constant
the operation of setting the name and type of a variable or constant is called. Initiative
lization is the operation of assigning a variable or a constant to some
values. If you simultaneously define the name and value of a variable or con-
stants, then declaration and initialization are combined.
Remember that the var and let statements need only be used
when declaring a parameter. In the future, when referring to the declared
ny variables and constants need to use only their names.
NOTE Swift does not limit the number of declared variables or
constants after var and let statements. The following code will execute correctly:

var x = 0.1, y = 5
let a = 2, b = 7.1

All variables and constants declared in one expression must be separated.


separate with comma.
The use of variables and constants brings convenience and logical
ness in Swift in comparison, for example, with Objective-C, in which
everything was much more complicated.

3 .3. Announcement rules


variables and constants
Swift gives a wide scope for creating (declaring) variables
and constants. You can call them arbitrary names by using
use Unicode, emoji (even so!), but at the same time you should
live some rules:
❑ Variables and constants should be named in the lower chamber
style . This means that when naming, only

to Latin characters (without underscores, dashes, mathematical


formulas, etc.) and every significant word (except for the first)
in the name starts with an uppercase letter. For example: myBestText ,
theBestCountry , highScore .
Although their names can contain any Unicode characters (whether
sting 3.5), their use only interferes with the readability of the code.
Listing 3.5

1 // Try to read the name of this variable


2 var dØw = "RϙЇ"
❑ Names must be unique. Can't create variable
or a constant with a name already taken by another variable, or
constant.
NOTE This rule has exceptions related to the scope of the
bridging variables and constants. We'll talk about this later.
If you need to give a variable or constant a name, reserve
defined in Swift for some service operator, then it follows
write it in apostrophes ( ` ), but I highly recommend you
avoid this so as not to break the readability of the code (Listing 3.6).
Swift has an operator named var . In order to create
a variable with the same name, you must use apostrophes.

Listing 3.6

1 // create a variable named var


2 var `var` =" Example variable in single quotes "
"Variable example
in apostrophes "
3 `var`
"Variable example
in apostrophes "

Note that in the previous example, in the results pane


the value of the declared variable `var` was printed twice . Next we
let's talk about how to display information in this area.

3 .4. Displaying text information

As mentioned earlier, in Xcode when working with playground projects


you can display any value in the result window. This requires
just write the name of the declared and initialized
the previously named variable or constant (Listing 3.7).
Listing 3.7

1 // declare a variable and assign a value to it


2 var dragonsBox = "Knight Swords"
"Swords of the Knights"
3 // display the value in the results area
4 dragonsBox
"Swords of the Knights"

When developing projects (be it a playground project or a full-fledged


app for iOS) any data can be displayed on the debug
console (we got acquainted with it earlier). Console output is
This is done using the global print () function .
NOTE A function is a named piece of program code that
which can be accessed multiple times.
Functions are designed to avoid code duplication. They are group
code that you use frequently and allow you to access it with a unified
cal name.
Swift has a large number of built-in functions, thanks to which you can
greatly simplify and speed up the development process. In the future, you will
learn to create functions yourself, depending on your needs.

An example of working with the print () function is shown in Listing 3.8.


Listing 3.8

1 // output information to the debug console


2 print ("Hello Dragon!")
"Hello Dragon! \ N"

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).

The console output function can accept as input not only


text, but also an arbitrary parameter (variable or constant) like
shown in Listing 3.9.

Listing 3.9

1 // declare a variable and assign a value to it


2 var dragonsBox = "Knight Swords"
"Swords of the Knights"
3 // Print the value of the previously created variable
4 print (dragonsBox)
"Swords of the Knights \ n"

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

1 // declare a variable and assign a value to it


2 var dragonsBox = "Knight Swords"
3 // Print the value of the previously created variable
4 print ("This chest contains \ (dragonsBox)")

Console :

This chest contains the Swords of the Knights


The output to the console and the text in the results pane are the same again
(for
excluding the line break character).

3 .5. Comments

Correctly written code should be well commented.


Comments help us not to get confused and not to forget about the intended
reading written. If you do not use them, then sooner or late you will find
yourself in a situation where navigation on your own The ectu will become
unbearably difficult.
Comments in Swift, as in any other programming language, are blocks of
non-executable code, such as notes or reminders. Swift has one-line and
multi-line comments.

One-line comments are written with double slash-


you ( // comment ) in front of the comment text, while many-
lowercase are framed with asterisks and forward slashes on both sides
( / * comment * / ). An example of comments is shown in Listing 3.11.
Listing 3.11

1 // this is a one-line comment


2 / * this is -
3 multiline
4 comments * /

All text in a comment is ignored by the compiler


and in no way affects the execution of the program.
In the previous chapter, it was mentioned that the playgound project is
supported
markdown comments are a special formatted kind of comments
tariev. They allow you to turn a playground project into a real one.
teaching material. This kind of comment should start
with a double slash and a colon ( //:) followed by
comment text. A few examples of unformatted com-
mentarii are shown in Fig. 3.4.
Enable comment formatting whereby all markdown-
comments will be displayed in a nice and easy-to-read style,
by choosing Editor > Show Rendered Markup from the Xcode menu .
The result is shown in Fig. 3.5.
Revert markdown comments to their former unformatted
view by choosing Editor > Show Raw Markup from the menu .

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)

Swift's syntax is extremely friendly. This wonderful language is


gramming does not require you to write extra characters, giving
this provides ample opportunities for the code to be clear and understandable
transparent. In the following chapters, you will see many more unusual and
interesting
teresny - something that has not been seen in other programming languages.
4 Data types
and operations with them
Any variable or constant is an in-memory data store the computer that
contains the particular value. Each temple A file can contain data of a certain
kind (type). For example measures, the familiar "Dragon Chest" may contain
various solid objects (values), and in the "Troll's Cauldron" there can only be
liquids. This division is logical, since it allows us to understand that exactly
we can do with the item in the store (its value):
objects can be melted, water can be boiled, numbers can be added, strings
combine with each other.
Each variable or constant can contain a value of a certain type, be it an
integer, fractional number, string, a sensible character, boolean, or object. In
Swift, by analogy with C, Objective-C, as well as other programming
languages, there is
a number of pre-installed (as Apple calls them, fundamental) data types.
NOTE Let me remind you that the data type of a variable or constant determines
the type of data stored in it.

4 .1. Types of data type definition


When initializing a variable or constant, you must specify
its data type. Swift is a very smart programming language, more often
in total, it determines the type of the initialized variable or constant
you automatically. Consider the example from Listing 4.1.
Listing 4.1
1 // declare a variable and assign a value to it
2 var dragonsBox = "Knight Swords"

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

1 // declare an empty variable dragonsBox


2 var dragonsBox: String
3 // create a non-empty trollsPot variable with an implicit type definition
4 var trollsPot = "Yellow Stew"
5 // create a non-empty gragonsName variable with an explicit type definition
6 var dragonsName: String = "Dragon Dragon"
All three variables have a string data type String , that is,
can store string values. The dragonsBox variable -
empty, and the trollsPot and gragonsName variables store the text.
Any declared variable or constant must have a defined
divided type. This means that you must either initialize the value

data, or explicitly specify the data type. Ignoring this rule


leads to an error (Listing 4.3).
Listing 4.3
1 var newDragonsBox // ERROR: variable type not specified

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.

All fundamental data types are so-called types-


values , or value types . Beyond value types
Swift exist in the types of following links, or reference types (reference type).
We'll see examples of reference types in future chapters.
When passing the value of a variable or constant of a value type
to another variable or constant, this is copied
values, and when passing a value of a reference type, a reference is passed
to the area in memory that stores this value.
Imagine that the "Troll's Cauldron" stores the meaning of a significant
type, and "Dragon Chest" - reference.
To assign the value of the storage "Troll's Cauldron" (remember,
it is equal to "Yellow Stew") new storage "Troll Cup",
you just need to pour out some of the contents from the boiler
into the cup. As a result, we get two exactly the same
independent variables (unless, of course, you look at the quantity,
but only on the type and name of the content), the values of which
will be located separately from each other.
But this trick will not work with a chest. When trying to copy
the value in the storage "New Dragon Chest" we will not be able to take
half of the gold and transfer it, because the stolen gold is
not just a faceless homogeneous gold mass, like a yellow soup,
and coins of various types and denominations. The only way out is
put in a new chest a note on which it will be written that
the stolen gold is kept in an old chest.
As a result, when you try to get or change the value
gold "through the new chest, we will get to the link
in "Dragon Chest".
In the future, you will regularly encounter both of these types.
data.

4 .2. Numeric data types


Working with numbers is an integral part of almost any
programs, and Swift has some fundamental
data types. Some of them allow you to store integers,
and some are fractional.

Integer data types


Integers are numbers that do not have a fractional part, for example
measures 81 or –18. Integer data types can be signed
(can take zero, positive and negative values)
and unsigned (can only accept zero and positive
values). Swift supports both signed and unsigned integers
numeric data types. To specify the value of numeric types
numeric literals are used.
A numeric literal is a fixed sequence of numbers,
starting with either a digit or the prefix operator minus /
a plus.
To declare a variable or constant of an integer type
you must use the Int (for signed) and UInt keywords
(for unsigned ones). An example of initializing integer variables-
are shown in Listing 4.4.

Listing 4.4

1 // declare a variable of a signed integer data type


2 var signedNum: Int
3 // and assign a value to it
4 signedNum = -32
-32
5 / * declare a variable unsigned
6 integer data type
7 and immediately initialize
8 by its value * /
9 var unsignedNum: UInt = 128
128

NOTE The difference between signed and unsigned types is


in that the value of the signed data type can be in the range from - N to + N ,
and unsigned - from 0 to +2 N , where N is determined by the bit width of the operating
systems.
It's also worth noting that Swift has additional integers
native data types: Int8 , UInt8 , Int16 , UInt16 , Int32 , UInt32 , Int64

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:

Grocery_automatic capacity = 490


Grocery_automatic.destruct ()

NOTE About why and for what brackets are added after the method name
ki, we'll talk later.

Since "everything is an object", then any numeric data type also


is an object. Each of the numeric data types (and there are many)
describes the entity "integer", but at the same time the properties "maximum
but the possible number "and" the minimum possible number "of each type
differ. In your program, you can access data
characteristics through the properties min and max . As stated earlier, these
the values depend on the bitness of the system on which the
the appendix (Listing 4.5).
Listing 4.5

1 // minimum value of the Int8 type parameter


2 var minInt8 = Int8.min
-128
3 // maximum value of the Int8 type parameter
4 var maxInt8 = Int8.max
127
5 // minimum value of the parameter of type UInt8
6 var minUInt8 = UInt8.min
0
7 // maximum value of the parameter of type UInt8
8 var maxUInt8 = UInt8.max
255

The techniques under consideration are object-oriented.


programming (OOP) that you may have encountered
in other languages. The objects are discussed in more detail in the last part.
book in which you will learn to use the full power of OOP.
NOTE Remember that Apple recommends using only data types.
Int and UInt, but for training we will work with the rest of the types.
Even a data type as simple as integers is endowed in Swift with
ample opportunities. In the future, you will learn about other fur
nizma that allow you to process different numerical values.
And now it's time to do your own work.
NOTE For each task, create a new playground project and co-
keep it in a dedicated folder on your computer.

The task

1. Declare two empty integer variables of Int8 types


and Uint8.
2. In one of them, write down the maximum value that can
can accept a parameter of type Uint8, in another - the minimum
a value that an Int8 parameter can take. About-
pay attention to which value is in which variable
can be recorded.
3. Output the obtained values to the console.
4. Declare two integer variables of the same type, if
In this case, the data type must be set implicitly first, and the second
swarm - obviously. Both variables must be assigned
values.
5. Swap the values of the variables.
6. For this you will have to use one more variable,
which will serve as a buffer.
7. Print the resulting values to the console. Wherein
in each version of the output data, write in text,
what variable are you outputting.

Floating point numbers

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

1 // fractional number of type Float with an explicit indication of the type


2 var numFloat: Float = 104.3
104.3
3 // empty constant of type Double
4 let numDouble: Double
5 // fractional number of type Float with implicit type indication
6 var someNumber = 8.36
8.36
Please note that the type of the constant someNumber is set implicitly
(using the passed fractional numeric value). In such
Swift always sets the data type to Double by itself .
NOTE Fractional values cannot start with a decimal
points. I draw your attention to this because you may have seen a similar approach
in other programming languages.

The task

1. Declare three parameters. The first one should be con-


a float stant with an arbitrary numeric value,
the second is an empty constant of type Float, the third is empty
variable of type Double.
2. Set a new arbitrary value for all parameters,
for which this operation is possible.

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.

* Binarymultiplication operator multiplies the first and second


operands and returns the result of the operation ( a * b ). Result type
The given value corresponds to the type of the operands.
/ The binary operator of division divides the first number by the second and
rotates the result of the operation ( a / b ). Result value type
matches the type of the operands.
% Binary modulus operator divides the first operand by the second and returns
the remainder of the division. Or, in other words, determines how many
values of the second operand will fit in the first, and returns the value that is
left - it is called
is the remainder of the division ( a% b ). Result value type
matches the type of the operands.
++ Unary decrement operator increments the value of the operand
by one, stores its new value and returns the result

operations ( ++ a or a ++ ). The return value depends on whether


the operator is placed before or after the operand.
- The unary increment operator decreases the value of the operand by
one, stores the new value and returns the result
walkie-talkie ( --a or a-- ). The return value depends on whether
the operator is placed before or after the operand.
The listed operators can be used to perform ma-
thematic operations with any numeric data types (integer
or floating point).
To demonstrate the use of these operators,
create two integer variables and two variables of type Double
(Listing 4.7).
Listing 4.7
1 // integer variables
2 var numOne = 19
19
3 var numTwo = 4
4
4 // variables of type floating point
5 var numThree = 3.13
3.13
6 var numFour = 1.1
1.1
The first two variables are implicitly assigned an integer datatype.
GOVERNMENTAL Int , for the second set of two implicit type Double .
To perform arithmetic operations, just use
required operator and operands. Consider the example in Listing 4.8.
Listing 4.8
1 // integer variables
2 var numOne = 19
3 var numTwo = 4
4 // addition operation
5 var sum = numOne + numTwo
23
6 // subtraction operation
7 var diff = numOne - numTwo
fifteen
8 // multiplication operation
9 var mult = numOne * numTwo
76
10 // division operation
11 var qo = numOne / numTwo
4

Each of the operators performs the assigned operation on


the operands transferred to it. You probably have a question about
the result of the division operation. Think: How

when dividing the variable numOne equal to 19 by the variable numTwo ,


equal to 4, could it be 4? After all, when multiplying the value 4 by
numTwo is not 19 at all. Logically, the division result should
was equal to 4.75.
The answer lies in the data type. Both variables are integer
data type Int , which means that the result of any operation will also have
data type Int . In this case, the result of division is simply discarded
fractional part and no rounding occurs.
NOTE Arithmetic operations can only be performed between
variables or constants of the same data type. When trying to execute
an operation between different data types Xcode will report an error.
The considered operations will work including with fractional
numbers (Listing 4.9).
Listing 4.9
1 // variables of type floating point
2 var numThree = 3.13
3 var numFour = 1.1
4 // addition operation
5 var sumD = numThree + numFour
4.23
6 // subtraction operation
7 var diffD = numThree - numFour
2.03
8 // multiplication operation
9 var multD = numThree * numFour
3.443
10 // division operation
11 var qoD = numThree / numFour
2.84545454545455

The result of each operation is a Double .


Performing arithmetic operations in Swift is no different from
performing the same operations in other programming languages.
In the previous listing, the integer operation was not presented.
division. Let's look at it separately (Listing 4.10).
Listing 4.10
1 // integer variables
2 var numOne = 19
19
3 var numTwo = 4
4
4 // operation of getting the remainder from division
5 var res1 = numOne% numTwo
3
6 var res2 = -numOne% numTwo
-3
7 var res3 = numOne% -numTwo
3

In this example, three variants of the integer operation are considered.


lent division, differing in signs of the operands. An analogue of the first
the given operation is the following expression:
numOne - (numTwo * 4) = 3
19 - (4 * 4) = 3
19 - 16 = 3
3=3
In other words, in numOne can be placed 4 values numTwo , and 3
will be the result of the operation since the given number is less than numTwo .
4
4
4
4
3
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15 16 17 18 19
Thus, the remainder is always less than the divisor.
This example also uses the unary minus operator. About-
Please note that the sign of the operation result is equal to the sign of the
dividend, that is, when the dividend is less than zero, the result will also be
less
scratch.
As noted earlier, the decrement ( ++ ) and increment ( - ) operators
respectively increase and decrease the value of the operand by one
face. Consider the example of how the decrement works in Listing 4.11.
Listing 4.11

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

The decrement operator in this expression corresponds to the operation:


numOne = numOne + 1
The return value depends on where the operator is located
(before or after the operand). Remember the following rules:

❑ The decrement (increment) operator returns the old value


the operand when it is located after the operand.
❑ The decrement (increment) operator returns a new value
the operand when it is located before the operand.
In the numOne ++ expression, the decrement operator is located after the
operand.
yes, which means the old value will be returned. That is, Swift increases
Vaeth value numOne unit, but returns the old value.
Therefore, in the results area opposite the second line there is a number
RA 19. When you try to print the value of the variable numOne displayed
its new value since it was changed on the previous line.
When a statement is placed in front of a variable ( ++ numOne ), the re-
From the results, we can see that a new value is returned immediately.
The considered example fully reflects the logic of the opera-
the increment rator.

Casting Numeric Data Types

When doing arithmetic operations in Swift, you should


Make sure that the operands are of the same type. Nevertheless
there are situations when it is necessary to perform an operation with
numbers,
which are of different data type. When trying to directly
multiplications like Int and Double , Xcode will report an error
and will stop the execution of the program.
This situation did not remain out of sight of the Swift developers,
and they have developed a special mechanism that allows the transformation
call one numeric data type to another. This mechanism
implemented as global functions.
NOTE It is fair to say that the global
These functions are methods of data types. We said earlier that numeric
data types are objects, and they have programmed actions,
called methods.
Each object has a special method called an initializer. is he
is automatically called when a new object is created, and since as a result
calling the object "numeric data type" creates a new object - "number", then
and the initializer fires.
The initializer has its own fixed notation - init (), and for the type
data Double it can be called like this:
Double.init ()
As a result of calling this method, a new object describing
the entity "fractional number". But Swift allows you not to write the name of the initializer when
create an object, but use the shorthand syntax:
Double ()

As a result of executing this code, an object describing


"fractional number" .
In the last part of the book, we will analyze in great detail what initializers are.
and what they are for.
The names of the called functions in Swift, with which you can
convert datatypes match datatype names:
Int ()
Conversion to Int data type .
Double ()
Conversion to Double data type .
Float ()
Convert to Float data type .
NOTE If you are using data types like UInt, Int8, etc. etc. in his
program, then to convert numbers to these data types also use
functions that have the same names as types.
To use these functions in brackets after the name, you need
pass the element to be converted (variable, constant, number). Ras-
look at an example in which you need to multiply two numbers: an integer
and fractional (Listing 4.12).
Listing 4.12
1 // variable of type Int
2 var numInt = 19
19
3 // variable of type Double
4 var numDouble = 3.13
3.13
5 // operation of multiplying numbers
6 var resD = Double (numInt) * numDouble
59.47
7 var resI = numInt * Int (numDouble)
57
There are two ways to multiply these numbers:
❑ Convert a Double to Int and multiply two integers
numbers.
❑ Convert Int to Double and multiply two fractional
numbers.
From the output in the results area, you can see that the resD variable has
a more accurate value than the resI variable . This suggests that
a variant converting an integer to a fraction using a function
Double () , more accurate than using Int () function on variable
of type Double .

NOTE When converting a floating point number to an integer


type the fractional part is discarded, no rounding is performed.

Compound assignment operator

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

1 // variable of type Int


2 var someNumInt = 19
19
3 // add an arbitrary number to it
4 someNumInt + = 5
24
5 / * this operation is similar to the expression
6 someNumInt = someNumInt + 5 * /
7 // display the result of the operation
8 someNumInt
24
9 // multiply it by an arbitrary number
10 someNumInt * = 3
72
11 / * this operation is similar to the expression
12 someNumInt = someNumInt * 5 * /
13 // display the result of the operation
14 someNumInt
72
15 // subtract an arbitrary number from it
16 someNumInt - = 3
69
17 / * this operation is similar to the expression
18 someNumInt = someNumInt-5 * /
19 // display the result of the operation
20 someNumInt
69
21 // find the remainder of the division
22 someNumInt% = 8
five
23 / * this operation is similar to the expression
24 someNumInt = someNumInt% 5 * /
25 // display the result of the operation
26 someNumInt
Five

To use the compound assignment operator, you need


I’m just after the arithmetic operator without

spaces to write an assignment operator. Operation Result


is instantly written to the variable to the left of
compound operator - this can be seen from duplicate entries in the
of the results.
At the moment, we already know three different ways to increase
variable values per unit (Listing 4.14).

Listing 4.14

1 // variable of type Int


2 var someNumInt = 19
19
3 // increase its value using a decrement
4 ++ someNumInt
20
5 // increase its value using arithmetic
addition operations
6 someNumInt = someNumInt + 1
21
7 // increase its value using a compound operator
assignments
8 someNumInt + = 1
22

Each subsequent action increments the someNumInt variable


exactly one.
It's time to practice working with numeric datatypes.
them. Go to the task.

The task

1. Declare three empty constants: one of type Int, one of type


Float and one Double. Do it on one line.
2. Initialize the following values for them: Int -
18, Float - 16.4, Double - 5.7. Do it on one line.
3. Find the sum of all three constants and write it down in re-
variable of type Float .
4. Find the product of all three constants and write it down
to a variable of type Int . Remember that you need to
read the result with a minimum error.
5. Find the remainder of the Float constant divided by
a constant of type Double and write it into a variable of type
Double .
6. Print all three results to the console using
explanatory text.

Ways to write numeric values

If computer science was present in your school curriculum, then


you may know that there are different number systems,
for example decimal or binary. In the real world overwhelming
in most cases the decimal system is used, while
in a computer all calculations are done in binary.
When developing programs Swift allows you to use the most advanced
popular number systems:
❑ Decimal . Numbers are written without using a prefix
sa in a familiar and understandable form for us.

❑ Binary . Numbers are written using the 0b prefix


before the number.
❑ Octal . Numbers are written using a prefix
0o before the number.

❑ Hexadecimal . Numbers are written using pre-


fixing 0x before the number.
Integers can be written in any of the following systems
reckoning. Listing 4.15 shows an example of 17
personal views.
Listing 4.15

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.

For decimal numbers, the exponent indicates the power of ten:


1.25e2 corresponds to 1.25 * 10 , or 125.0.
2

For hexadecimal numbers, the exponent indicates the degree


deuces:
0xFp-2 corresponds to 15 * 2 , or 3.75.
-2

Listing 4.16 shows an example of writing the decimal number 12.1875.


in different number systems and using the exponent.

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

Arithmetic operations are available for numbers written in any


from number systems. In the results area, you will always see
the result of execution in decimal form.
When writing numeric values, you can use the lower symbol
underscores (underscore) for visual separation of orders
numbers (Listing 4.17).

Listing 4.17

1 var number = 1_000_000


1,000,000
As a result, we have all the same number as without the lower symbol
underscores, only written in a more readable form.
Underscores can be used for any numeric data type
and for any number system.

4 .3. Text data types

It would be wrong to assume that Swift has limited development


botloader only with numeric data types. Think for yourself: about what
work with the user could be talked about if the programs
did not know how to store text information?

Initializing string values

There are two types of data in Swift for storing


text information:
❑ The Character type is intended for storing individual characters.
❑ The String type is intended for storing arbitrary text
information.
Both types provide fast and correct work with text
in your code, with full support for Unicode characters.
The syntax, as with other data types, is very simple
and readable. For storing information in text data types
string literals are used.
A string literal is a fixed sequence of text
out characters surrounded on both sides by double quotation marks ( "" ).
Let's look at the Character type first . This data type allows
stores in a variable or constant a text literal of length
in one character (Listing 4.18).
Listing 4.18
1 var char: Character = "a"
"a"
2 char
"a"
There is only one character stored in the char variable .
When you try to write two or more characters to a parameter of type Character
La Xcode will report a mismatch error for the types of writing
value and variable. When trying to pass a string literal
longer than one character Swift treats it as
values of data type String and cannot write it to a variable
of type Character .
It turns out that the String data type is an ordered set of sym-
oxen. It allows you to store an arbitrary string in a variable
length.
NOTE Swift version 2 has changed the way information is stored in a type
data String. Previously, it was, in fact, a set of values of type Character.
Now String is a completely new data type with new properties.
features and capabilities that allow you to access the stored in the parameter
a string literal as a set. This change allowed significant
extend the functionality of the String type.

Let's look at an example of working with the String data type (Listing 4.19).
Listing 4.19

1 // variable of type String


2 var stringOne = "Dragon"
"Dragon"
When declaring a variable, we pass it a string literal, so
most implicitly setting the data type to String .
NOTE Do not forget that strings assigned to a variable can be
changed, but assigned to a constant - not.

Empty string literals


An empty string is also a string literal. You can
pass it as a value to the text data type parameter
(Listing 4.20).
Listing 4.20
1 // using an empty string literal
2 var emptyString = ""
""
3 // using a String initializer
4 var anotherEmptyString = String ()
""
Both lines will have the same (empty) value as a result.
Let me remind you that an initializer is a special method built in
to the String data type .
When explicitly specifying the String type without initializing the value in the
the variable or constant is empty, not empty
string literal. Before using the parameter declared
in this way, you need to initialize its value (Listing 4.21).
Listing 4.21

1 // declaring a variable without specifying a value


2 var str: String
3 // specifying the value of a string variable
4 str = "Hello, Troll!"
"Hello, Troll!"
5 str
"Hello, Troll!"

The str variable must get some value before


you can turn to her. This example first declares
variable, and only then its value is initialized.

Casting to string data type


In Swift, the String () initialization method can be passed an arbitrary
value to assign to the declared parameter (
Thing 4.22).
Listing 4.22
1 // initialize the text value
2 var notEmptyString = String ("Hello, Troll!") "Hello, Troll!"
As a result , the text is stored in the variable notEmptyString.
the meaning of "Hello, Troll!" ...
The String () initializer can receive not only text input
value, but also a variable of an arbitrary data type (Listing 4.23).
Listing 4.23
1 // variable of type Double
2 var numDouble = 74.22
74.22
3 // string created based on a variable of type Double
4 var numString = String (numDouble)
"74.22"
String () function converts the value passed
to it to type
String . We have already encountered a similar mechanism when we
considered
The functions Float () , Double () and Int () were trivial .

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"

3 // constant of type String using


interpolation
4 let hello = "Hello, my name is \ (name)!"
"Hello my Name Is
The Dragon!"
5 // interpolate using expression
6 var meters: Double = 10
ten
7 let text = "My length is \ (meters * 3.28) feet" "My length is 32.8 feet"

When initializing the value of the constant hello , the value


variable name within the framework of the already known construction. Such a
sub-
move is perfectly legal in Swift. We saw a similar construction earlier.
when printing information to the console using the print () function .
Expressions can be used in interpolation, including
arithmetic operations, as shown in the example.

Concatenation concatenates different strings

values into one using the word arithmetic operation symbol


Genius (Listing 4.25).
Listing 4.25
1 // constant of type String
2 let firstText = "My weight"
"My weight "
3 // variable of type Double
4 var weight = 12.4
12.4
5 // constant of type String
6 let secondText = "tons"
"tons"
7 // string concatenation on initialization
new variable values
8 var text = firstText + String (weight) +
"My weight is 12.4 tons"
secondText
This example uses the addition operator to combine
converting different string values into one. Variable data type
weight is not a string, so its value is converted with
String () functions .
NOTE Values of type Character must also be converted when concatenated.
evolve to type String.

Collection of characters in a string


The String data type provides the ability to refer to the number of
lectures of its included characters using the characters property .
Like any property in Swift, it is written with a dot after the name.
a string parameter (Listing 4.26).
Listing 4.26

1 // variable of type String


2 var str = "Hello, Troll!"
"Hello, Troll!"
3 // get collection of symbols
4 var collection = str.characters
String.CharacterView
As a result, the collection variable will contain a set
all characters.
A set of elements in the Swift context is called a collection.
A collection is a separate data type, all of which are
we'll look at it later.
A collection, like an object, has its own properties. For example
get the number of characters in the string using the created collection
tion sollection (listing 4.27).
Listing 4.27
1 // number of characters in a line
2 collection.count
thirteen
The resulting result corresponds to the number of characters in the line
ke str .
NOTE Don't forget to use the auto-complete feature in Xcode.
With its help, you can get a complete list of properties and methods available when
work with objects.

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.

4 .4. Boolean values

Boolean data type

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-

The if-else clause checks if the expression passed to it is


muddy, and, depending on the result, performs the appropriate
branch.
If the variable isDragon contained the value false , then to the console
the phrase "Hello, Troll!" would be displayed.

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

1 let firstBool = true, secondBool = false, thirdBool = false


2 // group different conditions
3 let one = firstBool || secondBool
true
4 let two = firstBool || thirdBool
true
5 let three secondBool || thirdBool
False

The logical OR operator allows you to determine if there is a


at least one true operand passed to it.
Various logical operators can be grouped together,
creating complex logical structures (Listing 4.33).

Listing 4.33

1 let firstBool = true, secondBool = false, thirdBool = false


2 var resultBool = firstBool && secondBool || thirdBool
false
3 var resultAnotherBool = thirdBool || firstBool && firstBool
True

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

1 let firstBool = true, secondBool = false, thirdBool = true


2 var resultBool = firstBool && (secondBool || thirdBool)
true
3 var resultAnotherBool = (secondBool ||
true
(firstBool && thirdBool)) && thirdBool
This example uses execution priority pointers
operations - parentheses. You know these pointers
from classes in mathematics. Those expressions that are in brackets
are performed first.

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)

4 .5. Type aliases

Swift provides the ability to create an alias for anyone


data type. A type alias is an alternate name that is
which will refer to this type. For this
the typealias operator is used . Alias must be applied
then, when the existing type name is inconvenient to use in the context
ste program (Listing 4.35).

Listing 4.35

1 // define an alias for the UInt8 type


2 typealias ageType = UInt8
3 / * create a variable of type UInt8,
4
using an alias * /
5 var myAge: ageType = 29
As a result, the variable myAge will be created with the values
type UInt8 .
A type can have an arbitrary number of aliases. And all pseudo
donyms together with the original type name can be used
in the program (Listing 4.36).

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"

9 var someString: String = "String type String" "String type String"


This example defines three distinct data types for the String data type.
alias. Each of them, along with the main type, can
be used to declare parameters.
The created alias has the same capabilities as
as the data type itself. Once you declare it, you can use
call this alias to access properties and methods of the type
(Listing 4.37).

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

1. Declare a string constant and write your name into it.


2. Declare a variable of type Double and write your
weight in kilograms.

3. Declare a variable of type Int and write your height into it


in centimeters.
4. Calculate your body mass index and write it down in the re-
change. Formula for calculating BMI:
BMI = weight [kg] / height [m] 2

4 .6. Comparison Operators

Swift allows you to compare values of the same type each


with friend. For this purpose, comparison operators are used, the result
whose work is a value of type Bool . Of everything exists
six standard comparison operators:
==
The binary equivalence operator ( a == b ) returns true ,
when the values of both operands are equivalent.
!=
The binary nonequivalence operator ( a! = B ) returns true ,
when the values of the operands are different.
>
The binary greater than operator ( a> b ) returns true when
the value of the first operand is greater than the value of the second operand.
<
The binary operator "less than" ( a <b ) returns true when
the value of the first operand is less than the value of the second operand.
>=
Binary operator "greater than or equal" ( a> = b ) returns true ,
when the value of the first operand is greater than the value of the second
operand
yes or equal to him.
<=
The binary operator "less than or equal" ( a <= b ) returns true ,
when the value of the first operand is less than the value of the second
operand
rand or equal.
Each of the above operators returns a value indicating
which is valid for a certain statement. A few examples
and the values they return are shown in Listing 4.38.

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

A tuple is a special object that groups the values of different


types within a single composite value. Moreover, the tuple
offers the simplest way to combine values of different
types within the same value. Each individual value
a tuple can have its own data type, which is not
as independent of others.
SYNTAX

(value_1, value_2, ...)


A tuple consists of a set of independent values, written in parentheses
and separated from each other by a comma. The number of elements in a tuple can be
arbitrary.
A tuple is stored in variables and constants in the same way as the values of the fundamental
data types.
let constantName = (value_1, value_2, ...)
var variableName = (value_1, value_2, ...)
To write a tuple to a variable, you must use the var operator, and for
writing to a constant is a let statement.
For example: we will declare a constant, which includes three values of
different
personal data types (Listing 5.1).

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)

In this example, myProgramStatus is a constant containing


as a value, a tuple that describes the status of the work of a certain
program and contains three different parameters:
❑ 200 - an integer of the Int type ;
❑ "In work" is a string literal of the String type ;
❑ true - a boolean value of type Bool .
Each of these values has a different data type: Int , String, and Bool . In re-
As a result, this tuple groups together the values of three different
data types, allowing them to be stored within a single variable, or
constants.

Tuple data type


You might have a question: if a tuple groups values of different
personal data types into one, then what is the data type of the
tuple?
A tuple data type is a fixed ordered set of types
data of its values, which is written in brackets and the elements of which
Roots are separated by commas from each other. For a tuple from the
previous one
the listing data type is ( Int , String , Bool ), and in this example it
implicitly specified. The order of specifying data types must correspond
the order of the elements in the tuple, therefore, for example, the type ( Bool ,
String , Int ) is a completely different tuple data type.
When declaring a tuple, you can explicitly set the required type to it. A type
a tuple is specified in exactly the same way as the type of variables and
constants, - separated by a colon (Listing 5.2).
Listing 5.2
1 / * declare a tuple with explicitly
2
given type * /
3 let myProgramStatus: (Int, String,
Bool) = (200, "In Work", true)
(.0 200, .1 "In Work", .2 true)
4 myProgramStatus
(.0 200, .1 "In Work", .2 true)
The tuple presented here exactly matches the declared
in the previous example, but this time its data type is explicitly set.
You can create a tuple of any number of values
different types of data, and it will contain so many different
values as you like. Moreover, you can use
the same data type (or even its aliases) within one
tuple an arbitrary number of times (Listing 5.3).
Listing 5.3

1 // declare an alias for the Int type


2 typealias numberType = Int
3 // declare a tuple
4 let numbersTuple: (Int, Int, numberType,
numberType)
5 // initialize its value
6 numbersTuple = (0, 1, 2, 3)
(.0 0, .1 1, .2 2, .3 3)
7 numbersTuple
(.0 0, .1 1, .2 2, .3 3)

There are four integer values grouped in numberTuple.


niya, the type of two of which is defined through an alias.

5 .2. Interacting with elements of a tuple


A tuple is not only intended to set up and store some
set of values, but also to interact with those values. Swift by
allows you to split a tuple into separate values and assign them to a set
variables or constants. This is done to ensure the familiar
a way to access values using separate variables.

Initializing values through parameters


Variables that are assigned tuple values are written are in parentheses after a
var or let statement , with the number variables (constants) must correspond to
the number of values in a tuple (Listing 5.4).

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

4 var (statusCode, statusText,


statusConnect) = myProgramStatus
5 // display information
6 print ("Response code - \ (statusCode)")
"The response code is 200 \ n"
7 print ("Response text - \ (statusText)")
"Response text - In Work \ n"
8 print ("Server connection - \
(statusConnect) ")
"Connection to server - true \ n"

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 :

My name is Troll and I am 140 years old


The variables myName and myAge are initialized appropriately
the values of the elements of the tuple ( "Troll" , 140 ).
When setting the values of variables, you can ignore the ar-
free elements of the tuple. To do this, as a variable name,
corresponding to the element to be ignored,
an underscore character is indicated. An example is given in the
Tinge 5.6.
Listing 5.6
1 // declare a tuple
2 let myProgramStatus: (Int, String,
(.0 404, .1 "Error",
Bool) = (404, "Error", true)
.2 true)
3 / * we get only the necessary
4
tuple values * /
5 var (statusCode, _, _) = myProgramStatus
6 // display information
7 print ("Response code - \ (statusCode)")
"The response code is 404 \ n"

Console :

Response code - 404


As a result, the value of the first one will be written to the statusCode variable .
The th element of the tuple is myProgramStatus . The rest of the values will be
ignored.

NOTE Underscore character in Swift means ignore


parameter. You can use it in almost any situation where there is no
it is necessary to omit the creation of a new parameter. With examples of the work of this mech-
you will get acquainted later.
Indexes for accessing elements
To access the values of individual elements of a tuple, you need
use a numeric index separated by a period after the name
tuple (Listing 5.7).

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 :

Response code - 200


Response text - In Work
Server connection - true
The indices of the indicated elements correspond to the ordinal numbers
elements in a tuple.
ATTENTION The index of the first element in tuples is always zero. When this ele-
cops always go in order and sequentially. In a tuple of N elements, the index
the first element will be 0 and the index of the last will be N - 1.
Names for accessing elements
For each element of the tuple, you can set not only a value, but
and name. The element name is indicated separately before each element

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)

The specified tuple element names can be used when receiving


the values of these elements. This applies the same syntax
sis as when accessing through indexes. Naming values not
robs you of the ability to use indexes. Indexes in a Tuple
can always be used. Listing 5.9 uses the created
previously the tuple myProgramStatus .
Listing 5.9
1 // display information using
indices
2 print ("The response code is \ (myProgramStatus.
statusCode) ")
"The response code is 200 \ n"
3 print ("Response text - \ (myProgramStatus.
statusText) ")
"Response text - In
Work \ n "
4 print ("Server connection - \
(myProgramStatus.2) ")
"Communication with the server -
true \ n "
Console :
Response code - 200
Response text - In Work
Server connection - true
Accessing elements using names is more convenient and clearer than
access through indexes.
In the previous example, when declaring a tuple myProgramStatus , we
gave the names of the elements directly in the value of the tuple, but you can
specify not only during value initialization, but also in the type itself
tuple (Listing 5.10).
Listing 5.10
1 / * declare a tuple with
2
specifying element names
3
in type description * /
4 let myProgramStatus: (statusCode: Int,
(.0 200, .1
statusText: String, statusConnect: Bool) =
"In Work", .2 true)
(200, "In Work", true)
5 / * display the value of the element
6
tuple by name
7
this element * /
8 myProgramStatus.statusCode
200
9 / * declare a tuple with
10 specifying element names
11 when initializing their values * /
12 let myNewProgramStatus = (statusCode: 404,
(.0 404, .1 "Error",
statusText: "Error", statusConnect: true)
.2 true)
13 / * display the value of the element
14 tuples by name
15 of this item * /
16 myNewProgramStatus.statusText
"Error"

Changing the values of tuples


For tuples of the same type, you can perform the initialization operation.
storing the value of one tuple in another (Listing 5.11).
Listing 5.11

1 // declare an empty tuple


2 var myFirstTuple: (Int, String)
3 // create a tuple with value
4 var mySecondTuple = (100, "Code")
(.0 100, .1 "Code")
5 // pass the value of the tuple
6 myFirstTuple = mySecondTuple
(.0 100, .1 "Code")
7 myFirstTuple
(.0 100, .1 "Code")
The tuples myFirstTuple and mySecondTuple have the same datatype.
so we can assign the value of one tuple to another.
The first type is set explicitly, and the second one - through the initialized
value.
Besides changing the value of the tuple entirely, you can use
indices or names, change the values of individual elements (fo-
Thing 5.12).
Listing 5.12
1 // declare a tuple
2 var someTuple: (200, true)
(.0 200, .1 true)
3 // change the value of an individual element
4 var someTuple.0 = 404
5 var someTuple.1 = false
6 someTuple
(.0 404, .1 false)

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

1. Create a tuple with three parameters: your favorite movie,


your favorite number and your favorite food. All elements
tuples must be named.
2. Write down each element of the tuple in one expression
into three constants.
3. Create a second tuple similar to the first in parameters
tram, but describing another person (with other meanings
niyami).
4. Exchange values in tuples with each other (using
by adding an additional intermediate tuple).
5. Create a new tuple with the elements you like
my number from the first tuple, favorite number from the second
tuple and the difference between the favorite numbers of the first and second
cor-
tezhey.
6 Optional
data types
Optional data types are a tremendous innovation in the language
Swift, which you've probably never seen in other languages
programming and which significantly expands the possibilities
working with data types.

6 .1. Optionals

Optional data types , also called optional , are


a special data type that indicates that some variable
or a constant either has a value of a certain type, or in general
it does not matter.
NOTE It is important not to confuse the absence of any value in the option
a native data type with an empty string or null. An empty string is normal
a string literal, that is, a very specific value of a variable of type String,
and zero is a very specific value for a numeric data type. In the case of ot-
the absence of a value in an optional data type, there is a complete absence
meaning as such.

Let's look at an abstract example. Imagine you have endless


plane. A point with certain coordinates is set on it.
( x , y ). At any moment in time, we can talk about this point and semi-
to read information about it. Now let's remove this point from the plane. Not-
looking at this, you can still talk about this point, but get
information about it will not work, since the point no longer exists on
plane. In this example, a point is some object (change
naya, constant, etc.), and its coordinates are an optional data type, they are
may have a certain meaning, or may be absent in principle.
Now let's look at the optional in practice. Remember method-init-
An int class analyzer , denoted Int () . This method pre-

assigned to create an integer value or convert


some numeric value to an integer value. Not everyone re-
the given literal can be converted to an integer type
data: for example, the string "1945" can be converted and returned
as an integer, but the string "Hello, Dragon!" return as
the number won't work (Listing 6.1).

Listing 6.1

1 // variable with numeric string literal


2 let possibleString = "1945"
"1945"
3 / * when trying to convert
4
converts to integer * /
5 let convertPossibleString = Int (possibleString)
1945
6 // variable with string literal
7 let unpossibleString = "Hello Dragon!"
"Hello,
The Dragon!"
8 / * when trying to convert
nine
not convertible to integer * /
10 let convertUnpossibleString = Int (unpossibleString)
nil

As a result of its work, the Int () function returns an optional


data type, that is, a data type that in this case can
either contain an integer ( Int ), or not contain at all
nothing.
The optional data type is postfixed as
a question mark after the name of the underlying data type (that is, the data
type
on which the optional is based). For example from the previous
In the listing, the optional Int type is denoted Int? ... Optionals
can be based on any data type including Bool , String ,
Float and Double .
To tell Swift that a value is in some way
object is missing, the keyword nil is used , specified
as the value of this object. If an option is specified for a variable
data type, then it can take
either the value of the underlying data type, or nil . The value of the optional
the constants are specified once (Listing 6.2).

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

The dragonAge variable is an optional variable


data. Initially, it is assigned a value corresponding to
the main data type for the optional (type Int in this case).
Since dragonAge is a variable, we can change its value
at any time. As a result, we assign nil to it , after which
dragonAge contains no value.

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.

To declare a parameter of an optional data type, you can


but use the Optional () function , as shown in Listing 6.3.

Listing 6.3

1 // optional variable with set value


2 var optionalVar = Optional ("stringValue")
"stringValue"
3 optionalVar
"stringValue"
4 // destroy the value of the optional variable
5 optionalVar = nil
nil
6 optionalVar
nil
Since the Optional ()
variable as an input argument is
given a value of type String , then its return value has an optional
a native string data type.
As the value of this function, you must pass the value
the type of data that should become the main one for the created
optional.

6 .2. Retrieving an optional value

In order for the values contained in the options, you can


was to work, they must be removed in a special way.
Forcing value retrieval
Remember that the optional datatype is completely new.
data type: Int? and Int , String? and String , Bool? and Bool is it all
different data types other than the types on which they are based.
Therefore, despite the fact that optionals can take on the values

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

Swift provides a mechanism for solving this problem, which is


is called forced extraction of an optional value (forced
unwrapping). In this case, using a special operator, the value the optional data
type is converted to the value of the primary (for this optional) data type,
such as Int? converts to Int .
Exclamation mark is used for forced extraction as a postfix of the parameter
name (variable or constant you) containing an optional value.
Let's fix the code given in the previous example for correct calculating the
sum of integers (Listing 6.5).
Listing 6.5

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

To convert an optional variable value


trollAge into an Int value , add an operator to the variable name
a torus for forced extraction of an optional value. Eventually
expression trollAge! + 10 will add two numbers of the same type,
and the result can be written as an optional value
Int to trollAge variable .
When forced to retrieve a value, you must ensure that
that a parameter with an optional data type contains any
value, not nil . Otherwise, there will be
an attempt to convert a non-existent value to the datatype,
so Xcode will report an error.

Indirect value extraction

As opposed to forcibly retrieving an optional value Swift supports indirect


optional value extraction (implicitly unwrapping).
If, when initializing the value of an optional, you are sure that the given
the parameter is guaranteed to have a value and never will is nil , it makes
sense to abandon forced retrieval values using an exclamation mark
whenever it is
reading is required. Indirect extraction is used for this purpose optional value.
When extracting indirectly as a postfix to the data type, it is necessary to
indicate not a question mark, but an exclamation mark (for example,
Int! instead of Int? ). Listing 6.6 shows the difference in usage
forced and indirect retrieval of optional values.

Listing 6.6

1 var type: String


2 // forced extraction of an optional value
3 let monsterOneType: String? = "Dragon"
"The Dragon"
4 type = monsterOneType!
"The Dragon"
5 type
"The Dragon"
6 // indirectly retrieve the optional value
7 let monsterTwoType: String! = "Troll"
"Troll"
8 type = monsterTwoType
"Troll"
9 type
"Troll"
When trying to indirectly retrieve a non-existent (i.e. equal to nil )
an optional value Xcode will report an error (Listing 6.7).
Listing 6.7

1 let pointCoordinates: (Int, Int)? = nil


2 coordinates = pointCoordinates! // ERROR

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.

NOTE Debugging is a stage in the development of a program at which


errors are found and eliminated.

Assertion is a special function assert () ,


checking some condition for its truth
nosti. The following input pairs are passed to this function.
meters:
❑ checked condition;
❑ debug message.
We can say that the assert () function "asserts" that the passed
her condition is true. If the function returns true , then the execution
the program continues; if the function returns false , then you
the program completes. Moreover, if you are testing a
gram in the Xcode development environment, the debug message is displayed
on
console, and Xcode itself reports the error. Others
in words, the mechanism of action of a statement can be interpreted
van as follows:
If the passed condition is met, then continue with
program, otherwise interrupt it and output the transferred
debug message.
Let's look at an example of using a statement (Listing 7.1).

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

1 // optional with set value


2 var isDragon: Bool? = true
true
3 // check if the condition is correct
4 assert (isDragon! = Nil, "No character")
If the variable isDragon had no value(was equal to nil ), then
the execution of the program would be interrupted upon approval.
The disadvantage of this approach is that the assertion mechanism
only allows you to interrupt the execution of the program and display the
debug
specific message. Execute arbitrary code depending on
he does not allow the result of the check.

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.

8 .1. If condition statement

If statement syntax

The statements you saw in the previous chapter are


are a simplified form of the condition operator. They are analyzing
the transferred condition and allow either to continue
the program, or end it with the output of the debug
communication. Conditions, unlike assertions, allow
certain blocks of code depending on the result of the check
some condition. Sometimes this mechanism becomes simple
irreplaceable.
SYNTAX
if test_condition {
// operator body
}

The condition statement starts with the if keyword, followed by a check-


my condition. If the checked condition is true, then the code from the body of the operator is executed.
rator. Otherwise, this operator is ignored and program execution
continues with the next statement.

A condition is a boolean expression. It can take on the value


either "true" or "false". For storing such values in Swift
there is a fundamental Bool datatype that we have considered
earlier. It turns out that, as in the statements, the condition that is necessary
dimo for the if statement to work , must be a Bool value .
In its simplest form, an example of using the operator is shown in
sting 8.1.

Listing 8.1

1 // variable of type Bool


2 var logicVar = true
3 // check the value of the variable
4 if logicVar {
five
print ("The variable logicVar is true")
6}

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}

If you need to execute a block of code if the passed


conditions, then you can use the logical negation operator
(Listing 8.3).

Listing 8.3

1 // variable of type Bool


2 var logicVar = false
3 // check the value of the variable
4 if! LogicVar {
five
print ("The variable logicVar is false")
6}

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

1 // variable of type Bool


2 var logicVar = false
3 // check the value of the variable
4 if logicVar {
five
print ("The variable logicVar is true")
6 } else {
7
print ("The variable logicVar is false")
8}
Console :
LogicVar is false
As a result, we have provided for both results of the checked condition.
Previously, we looked at the logical operators AND and OR, allowing
grouping values. With their help, you can combine
several conditions (Listing 8.5).
Listing 8.5
1 // variables of type Bool
2 var firstLogicVar = true
3 var secondLogicVar = false
4 // check the value of variables
5 if firstLogicVar || secondLogicVar {
6
print ("One of the variables is true")
7 } else {
8
print ("Both variables are false")
9}
Console :
One of the variables is true
Since the logical OR operator returns true when at least
one of the operands is true , in this case the if condition statement
executes the first block of code.
The if statement also offers extended syntax for writing
si, which kind of concatenates multiple if statements , allowing
check an arbitrary number of conditions.
SYNTAX
if checked_condition1 {
// the code...
} else if checked_condition2 {
// the code...
} else {
// the code...
}

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.

❑ If the number of tenants is more than 7, then the rent is


500 rubles per person.
Let's write a program that calculates the total amount of money,
using the if-else construct (Listing 8.7). The program receives
the number of residents in the house and depending on the cost of rent for
one tenant returns the total amount collected.

Listing 8.7

1 // number of residents in the house


2 var tenantCount = 6
6
3 // rental price per person
4 var rentPrice = 0
0
5 / * determination of the price for one
6
person in accordance with the condition * /
7 if tenantCount <5 {
8
rentPrice = 1000
9 } else if tenantCount> = 5 && tenantCount <= 7 {
ten
rentPrice = 800
800
11 } else {
12
rentPrice = 500
13 }
14 // calculate the total
15 var allPrice = rentPrice * tenantCount
4800

The conditions being tested use comparison operators and logi-


iCal operators. The result of each of them is a logical
value.

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

checked_condition? code1: code2

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 :

A is less than or equal to B


The functionality of the ternary operator is exactly the same as that of the
stan-
a free form of notation of a condition operator.
The most interesting thing is that ternary operators do more than just perform
co-
the corresponding block of code, and return its value. For this reason-
they cannot be used as part of other operators (Listing 8.9).
Listing 8.9

1 // variable of type Int


2 var height = 180
180
3 // variable of type Bool
4 var isHeader = true
true
5 // calculate the value of the constant
6 let rowHeight = height + (isHeader? 20: 10) 200
7 // output the value of the variable
8 rowHeight
200
The ternary condition operator used returns an integer
a specific value that depends on the value of the isHeader variable . This
the returned value is added to the height variable and written
gets into the constant rowHeight .
If statement for optionals

Earlier we figured out how to determine whether a value exists


in an optional using assertions. The main disadvantage of the
The only difference is that they do not allow arbitrary code to be executed.
We draw the analogy between statements and the if condition statement .
or earlier, so now you can try to use this
operator including for processing optionals.
The if-else construct gives you much more flexibility
when developing programs and, most importantly, avoids
errors when the value of the optional needs to be obtained, but it is absent
(Listing 8.10). The given code allows you to determine the fact
the presence of trolls, as evidenced by the value of the trollsCount variable .
If there are no trolls, the corresponding message is displayed on the console
nie. Otherwise, the number of boilers for
trolls to purchase.

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 .

NOTE This method of checking the existence of an optional value is


only works when forced extraction of an optional value,
since the indirectly retrieved value cannot be nil, and therefore cannot be compared
stringing it with nil doesn't make sense.

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.

Let's look at an example of optional bindings (Listing 8.11).

Listing 8.11

1 / * variable of optional type


2
with a preset value * /
3 var monstersCount: Int? = 8
8
4 // global constant
5 var monsters = 0
0
6 // determine if a value exists
7 if var monsters = monstersCount {
8
print ("Total \ (monsters) monsters")
"Only 8 monsters \ n"
9 } else {
ten
print ("There are no trolls")
11 }
12 monsters
0
Console :
Total 8 monsters
With optional binding used in an if statement ,
automatic extraction of the value of the monstersCount variable ,
and since it exists, it is initialized with this value
declared variable monsters .
Note that the monsters variable created outside of the
pa conditions, and the variable monsters of the same name , created in the
process
optional bindings are different variables. Variable,
created by optional binding, local to the operator
conditions, therefore it can be used only within this opera-
rator. In this case, the global variable monsters is not affected,
what the output of the value of this variable at the end of the listing tells us
about.
If there was no value in the monstersCount variable , then
control would go to the else block .
A good example of optional binding is using
Int () function, which is already familiar to us . Let me remind you that this
function
converts a string literal to a number if possible and returns
This is an optional Int type . Imagine you have a group of dragons.
Most dragons have their own chest of gold, and the amount
gold coins in each of them are different. At any given time, you
you need to know the total number of coins in the whole group. Suddenly
a new dragon comes to you. You need to write some code that
will determine the number of coins in the chest of the new dragon (if, of
course,
he has a chest) and add them to the total amount of gold (fox-
Thing 8.12).
Listing 8.12
1 / * variable of type String,
2
containing the amount of gold coins
3
in the chest of a new dragon * /
4 var coinsInNewChest = "140"
"140"
5 / * variable of type Int,
6
which will store the general
7
number of coins for all dragons * /
8 var allCoinsCount = 1301
1 301
9 // check if the value exists
10 if Int (coinsInNewChest)! = Nil {
eleven
allCoinsCount + = Int (coinsInNewChest)!
1,441
12 } else {
thirteen
print ("New dragon has no gold")
14 }
In this case, the Int () function is used
twice:
❑ The operator checks the condition if compared with nil preob-
the expanded value of the coinsInNewChest variable .
❑ When added to the allCoinsCount variable in the body of the if statement .

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

1 / * variable of type String,


2
containing the amount of gold coins
3
in the chest of a new dragon * /
4 var coinsInNewChest = "140"
"140"
5 / * variable of type Int,
6
which will store the general
7
number of coins for all dragons * /
8 var allCoinsCount = 1301
1 301
9 / * retrieve the value of the optional
10 to a new variable * /
11 var coins = Int (coinsInNewChest)
140
12 / * check if the value exists
13 using the created variable * /
14 if coins! = Nil {
fifteen
allCoinsCount + = coins!
1,441
16 } else {
17
print ("New dragon has no gold")
18 }

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

1 / * variable of type String,


2
containing the amount of gold coins
3
in the chest of a new dragon * /
4 var coinsInNewChest = "140"
"140"
5 / * variable of type Int,
6
which will store the general
7
number of coins for all dragons * /
8 var allCoinsCount = 1301
1 301
9 / * check if the value exists
10 using optional bind * /
11 if var coins = Int (coinsInNewChest) {
12
allCoinsCount + = coins
1,441
13 } else {
fourteen
print ("New dragon has no gold")
15 }

Thus, we got rid of repeated calls of functions and distribution


running RAM, having received a beautiful and optimized
bathroom code. In this example, you probably will not feel the increase
the speed of the program, but when developing in Swift, more
complex applications for mobile or stationary devices
this approach will allow you to get quite tangible results.

The task

1. Create a String alias named Text.


2. Declare three constants of type Text . The values of the two constants
should consist only of numbers, the third - of numbers and letters.
3. From all three constants, find those that consist only of
digits and output them to the console. To convert a string
to a number, use the Int () function .
4. Create an alias for the type (numberOne: Text ?, numberTwo:
Text?)? named TupleType . This type is optional.
nal and also contains optional values.
5. Create three variables of type TupleType containing the following
The following values are: ( "190" , "67" ), ( "100" , nil ), ( "-65" , "70" ).
6. Output the values of the elements of those tuples in which neither
one of the elements is not initialized to nil .

8 .2. Guard statement

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

guard else statement {


// operator body
}

The guard keyword is followed by some verifiable statement. If


the statement returns true, then the body of the statement is ignored and control is
goes to the code following the guard.
If the statement returns false, then the code inside the statement body is executed.
ATTENTION There is a restriction for this operator: its body must
contain one of the following statements - return, break, continue, throw (none
of these has not yet been considered).
We will get acquainted with examples of using this operator.
in the following chapters.

8 .3. Range Operators

We have already met a large number of different opera-


tori. There are special operators in Swift with which
many consecutive numeric values can be combined
(for example, 1, 2, 3, 4), that is, create a range (for example, 1 to 4).
Such operators are called operators range (range operators).
Swift offers two range operators:
❑ Private operator: a ... b defines a range that includes
all numbers from a to b (including a and b ). For example, for an integer
type range 1 ... 4 includes numbers 1, 2, 3, 4.
❑ Semi-open operator: a .. <b defines a range of numbers from a to b
(including only a ). For example, for an integer type, the range
2 .. <5 includes numbers 2, 3, 4.
We will look at some examples of data usage shortly.
operators.

8 .4. The switch statement


In some situations, it is necessary to implement a large number of
different options for executing the code, depending on the possible
th parameter value. Of course, you can use
extended syntax of if statement , repeating blocks many timeselse-if . However,
this approach introduces confusion to the code and

this is bad programming practice. For solutions


tasks in Swift there is a branch operator switch , which allows
like if , depending on the value of the passed expression choose
the corresponding block of code. The main difference is that switch gives
the ability to work not only with a logical data type. He can-
can accept an arbitrary type parameter for verification.

Switch statement 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

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 if userMark == 1 {
ten
print ("One on the exam! It's awful!")
11 } else if userMark == 2 {
12
print ("You will stay with a 2 for the second year!")
13 } else if userMark == 3 {
fourteen
print ("You did not study the material well this year!")
15 } else if userMark == 4 {
sixteen
print ("Not bad, but could be better")
17 } else if userMark == 5 {
eighteen
print ("You are guaranteed a free place at the university!")
19 }

Console :

Not bad, but it could be better


To find the required message, the if statement sequentially
goes through each of the specified conditions until it finds one that
swarm returns true .
Although this program works correctly, it is better to use
use the branching operator switch , which allows you to perform that
the same operation, but the code is more readable
and transparent.
SYNTAX
switch expression_test {
case value1:
code1
case value2, value3:
code2
...
case valueM:
break
default:
codeN
}

Switch statement, also called switch-case construct, after evaluation


of the passed expression searches for the received value among the specified
after the case keywords.
The number of case blocks can be arbitrary. After each keyword
case any number of values can be specified, which are separated from each
friend with commas. Values are indicated by a colon followed by
followed by a block of executable code.
Depending on the desired value, the corresponding case block is selected and
the code in this block is executed.
Suppose the expression being tested returned a value corresponding to
value 1. In this case, the first block of code is executed. If neither
one of the case statements does not match the desired value, then execute-
there is the Nth block of code after the default keyword. This key
word contains code to be executed when no matches are found
Denia in case blocks. It is similar to the else statement in an if-else construct.
The default block should always appear last in a switch-case construct.
The body of a switch-case clause is surrounded by curly braces, just like
individual blocks of code were framed in the if-else construct.
At the end of each case block, there is no need to put a break statement, like this
require other programming languages. This operator is placed only if
if the case or default block does not contain executable code.
The code executed in any case block leads to the termination of the statement
switch.

NOTE The switch-case construct must be exhaustive, i.e.


contain information about all possible values of the checked parameter.
This is ensured by the presence of the default block. If this block is not required
wives contain no executable code, then just place the opera-
torus break.
Let's rewrite the student grade checker using

switch-case constructs (Listing 8.16).

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 :

Not bad, but it could be better


The switch statement gets the
value of the variable passed to it
userMark and sequentially checks each case block looking for
matching value.
As you can see, the switch-case construct is really de-
made the code more readable.
Operator switch in Swift is one of the most advanced sre-
di similar operators of programming languages. It can be used
use for any data type, object, and even tuples. is he
provides truly remarkable opportunities for creating
code. For example, to point immediately to many numerical values
niy, as a value in case blocks, you can use the operators
range or conditional operators (Listing 8.17).
Listing 8.17
1 / * variable of type Int
2
contains the estimate obtained
3
user * /
4 var userMark = 4
5 // check the value using a range
6 switch userMark {
7
case 1 .. <3:
8
print ("Exam failed!")
nine
case 3 ... 5:
ten
print ("Exam passed!")
eleven
default:
12
assert (false, "Score \ (userMark) out of available
range ")
13 }

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

As noted earlier, after executing the code located in the block


ke case , the switch-case construction is terminated . Od-
however, in some cases it is required not to complete the construction
switch-case , and go to code execution in the next case block .
To do this, at the end of the case block, specify the fallthrough keyword .
(Listing 8.18). Imagine there are three levels of preparedness
to emergency situations: A, B and C. Each degree provides
the implementation of a number of activities, and each subsequent
a level includes activities from previous levels. Depending on
from the transmitted level, all
designed to carry out activities. In this case, the minimum
the level of readiness is B, the maximum is A (includes
activities of levels C and B).

Listing 8.18

1 / * variable of type Character


2
contains level
3
readiness * /
4 var level: Character = "B"
5 // determine the readiness level
6 switch level {
7
case "A":
8
print ("Turn off all electrical appliances")
nine
fallthrough
ten
case "B":
eleven
print ("Close front doors and windows")
12
fallthrough
thirteen
case "B":
fourteen
print ("Keep calm")
fifteen
default:
sixteen
break
17 }

Console :

Close front doors and windows


Keep calm
If the value is "B" of the variable level , the lines are output to the console,
corresponding to the values "B" and "B" , that is, when the program
encountered
the keyword fallthrough is given , it proceeds to the execution of the code,
corresponding to the value "B" .

Where keyword

With case blocks, you define possible values that


can take an expression. In one case block, you can define
even several possible values. However, Swift allows
lakh of one case block indicate not only the value of the considered
parameter, but also on dependence on any condition. This function
The functional is implemented using the where keyword in the case block .
Let's look at an example. Have three times in your dragon pen
new sites. You need to distribute incoming dragons
between the sites, depending on their color and weight according to the
following
rules:

❑ Corral 1: green dragons weighing less than 2 tons.


❑ Corral 2: Red dragons weighing less than 2 tons.
❑ Corral 3: green and red dragons weighing 2 tons or more.
It uses two different parameters (color and weight) to determine
division of the corral number. In this case, the switch can accept for
verification
only one of them.
To solve the problem, you can, of course, use the construction
if-else , which checks the color, in each of the branches of which create
switch-case constructs for weight testing. But this approach -
bad programming practice as there is a better
and a convenient way. To solve the problem, let's create one
switch-case construct and use the where keyword (fox-
Thing 8.19).
Listing 8.19
1 / * variable of type Float
2
contains the weight of the dragon * /
3 var dragonWeight: Float = 2.4
4 / * variable of type Float
five
contains dragon color * /
6 var dragonColor = "green"
7 // define the pen for the incoming dragon
8 switch dragonColor {
nine
case "green" where dragonWeight <2:
ten
print ("Put the dragon in pen 1")
eleven
case "red" where dragonWeight <2:
12
print ("Place the dragon in pen 2")
thirteen
case "green", "red" where dragonWeight> = 2:
fourteen
print ("Place the dragon in pen 3")
fifteen
default:
sixteen
break
17 }
Console :

Place the dragon in pen 3


The where keyword is placed in the case block after the enumeration of the
chen, followed by a boolean expression that should return
true or false .
The code in the case block is executed when a matching value is found
and the where clause returned true .
With the where keyword you can also point to
the value of the parameter checked in the switch statement . In this case
instead of the value of the case block, use the symbol you already know
underscore.
Let's rewrite the earlier example to test the user assessment.
vendor (Listing 8.20).
Listing 8.20
1 var userMark = 4
2 switch userMark {
3
case _ where userMark> 1 && userMark <3:
4
print ("Exam failed!")
five
case _ where userMark> = 3:
6
print ("Exam passed!")
7
default:
8
assert (false, "Score \ (userMark) out of available
range ")
9}

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

1 / * tuple of type (String, Int)


2 contains the color and weight of the dragon * /
3 var dragonCharacteristic = ("green", 2.4)
4 // define a pen for an incoming dragon
5 switch dragonCharacteristic {
6
case ("green", 0 .. <2):
7
print ("Put the dragon in pen 1")
8
case ("red", 0 .. <2):
nine
print ("Place the dragon in pen 2")
ten
case ("red", _), ("green", _) where
dragonCharacteristic.1> 2:
eleven
print ("Place the dragon in pen 3")
12
default:
thirteen
print ("Dragon with unknown parameters")
14 }

Console :

Place the dragon in corral 1


The case keyword can also contain multiple values.
tuple, separated by commas, as well as the values of individual
parameters.
In order to find all dragons weighing more than 2 tons, we have
was to ignore the second element of the tuple (using the symbol
underscore) and transfer the check of its value to the
lovie where clause .
In order to abstract from this example on behalf of the transmitted
in the parameter switch statement , you can use the wonderful
a technique called value binding (by analogy with optional
binding).
Its essence is to declare a new variable or constant
and in automatic mode initialize it with the value
a verified parameter (or an element of a verified parameter) for
checking it with the where keyword . An example is given
in Listing 8.22.
Listing 8.22

1 / * tuple of type (String, Int)


2 contains the color and weight of the dragon * /
3 var dragonCharacteristic = ("green", 2.4)
4 // define a pen for an incoming dragon
5 switch dragonCharacteristic {
6
case ("green", 0 .. <2):
7
print ("Put the dragon in pen 1")
8
case ("red", 0 .. <2):
nine
print ("Place the dragon in pen 2")
ten
case ("green", let weight) where weight> = 2:
eleven
print ("Place the dragon in pen 3")
12
case ("red", let weight) where weight> = 2:
thirteen
print ("Place the dragon in pen 3")
fourteen
default:
fifteen
print ("Dragon with unknown parameters")
16 }
Console :
Place the dragon in pen 3
In order to handle both options for large dragons (mas-
soy more than 2 tons), you need to create two separate case blocks :
the first for green, the second for red. This is due to the fact
that Swift prohibits binding the same element on multiple
values in a case statement . In other words, the following code will
calls the error:
case ("red", let weight), ("green", let weight) where weight> = 2:
Therefore, it is necessary to divide both options into two separate blocks
case .
This method, in which matching conditions are divided into
separate case blocks , available only if the quantity
the values of the element causing the split are small.
However, imagine that you have 5-10 different color options
dragon. In this case, the number of case blocks will become too much
great.

To solve the problem, all possible values of the element can be


concatenate into a variable-set, and when binding values
use the entire tuple (Listing 8.23).
Listing 8.23

1 / * tuple of type (String, Int)


2 contains the color and weight of the dragon * /
3 var dragonCharacteristic = ("green", 2.4)
4 // define a pen for an incoming dragon
5 switch dragonCharacteristic {
6
case ("green", 0 .. <2):
7
print ("Put the dragon in pen 1")
8
case ("red", 0 .. <2):
nine
print ("Place the dragon in pen 2")
ten
case let (color, weight) where (color == "green" || color ==
"red") && weight> = 2:
eleven
print ("Place the dragon in pen 3")
12
default:
thirteen
print ("Dragon with unknown parameters")
14 }

Console :

Place the dragon in pen 3

To apply value bindings to all elements


array, you must specify in parentheses after the let (or var ) statement
the names of the declared local parameters that will be initialized are defined
by the values of the tuple. After that, as with linking from- specific values,
after the where keyword , conditions are specified, which will execute the code
from the case block .
Arrays are similar to the previously discussed String data type , which ry
contains a collection of elements (symbols). The array is does not just contain
a collection of elements as one of its characteristics, but directly is this
collection. Under- You will learn more about arrays in the next chapter.
The techniques described in this chapter open up fantastic opportunities in
software development. Other very useful ways You will learn how to use the
switch statement when you describe the numbers. And to complete the topic, do
your own work.

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: + , - , * , / .

2. Create a constant of type Operation and fill in its values


elements in an arbitrary way, for example ( 3.1 , 33 , "+" ).

3. Using the switch-case construction , calculate the value


the operation specified in the operation element of the created shortcut
The same for the operands in its first two elements. Operator
switch must correctly handle any of the following
lent operations.

4. In the created constant, replace the operation symbol with another


arbitrary symbol and check the correctness of work
switch-case constructs .
9 Collection types
A collection is a collection of items. Swift offers three functions
collection types: array , set and dictionary (dictionary). All three types of
collections, by analogy with tuples of pre- are separate data types.
The material in this chapter is important enough to spend time to study it in
detail.

9 .1. Arrays

Array declaration

An array is an ordered collection of elements of the same type, for


access to which the indices of these elements are used. Order- a collection is
called a collection in which the elements are located in the order set by the
developer. Arrays are very an important functional type that you will use
in almost every program.
The elements of the array are index-value pairs.
The element index is of the Int datatype and is automatically generated
depending on the ordinal number of the element. Array indexes
start from scratch. An array containing 5 elements has an index
the first element is 0, and the last - 4. The indices are always sequential.
are sensitive and inseparable.
An element value is an arbitrary value of some definite
local data type. As mentioned earlier, values are available by
their corresponding indices. A very important feature of arrays in Swift
is rigid typing of the values of its elements, that is, the data type
all elements within one array must be one

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

[value_1, value_2, ..., value_N]

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.

Arrays are stored in variables and constants, so to declare them


phenomena, the var and let statements are used .
To create an immutable array (composition and values of elements
such an array cannot be changed) use the let statement ,
otherwise, the var statement .
An example of creating an array is shown in Listing 9.1.
Listing 9.1

1 // immutable array
2 let alphabetArray = ["a", "b", "c"]
3 // mutable array
4 var mutableArray = [2, 4, 8]

The above example creates two arrays: alphabetArray and mu-


tableArray . The immutable alphabetArray is for
storing values of type String , and mutable array mutableArray -
to store elements of type Int . Both arrays contain three elements
cop. The indices of the corresponding elements of both arrays have
values 0.1 and 2.
Array type is value type, not reference type
(reference type). This means that when you try to copy or re-
give the value of the array, its copy is created, with which everything happens
further work (Listing 9.2).

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.

Accessing array elements

To access a single element of an array, use


the index of the given element, enclosed in square brackets and indicated-
after the array name (Listing 9.4).
Listing 9.4

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"]

A highlighted set of elements is treated as complete in Swift.


valuable array.
After replacing elements from the range [1 ... 2] with ["five"] indices
elements have been rearranged As a result, the element "four" ,
which initially had index [3] , received index [2] , as it became the third
element of the array.

NOTE The indices of the array elements are always sequential.


one after another without gaps in values, they are rebuilt if necessary.

Explicitly specifying the type of elements

As with fundamental data types, when declaring


variables and constants Swift can automatically detect
the type of the elements of the declared array by the initialized values
holes. If necessary, you can explicitly specify the data type of the elements.
There are two forms of specifying the type of an array (and the types of
elements into it).
SYNTAX
Full form of entry:
var ArrayName: Array <DataType>
Short form of entry:
var ArrayName: [DataType]
In both cases, an array is declared, the elements of which must have the specified
data type.
The array type in this case will be [DataType] (with square brackets) or
Array <DataType>. Both array type notations are equivalent. By the type of each
a separate item is DataType (no square brackets).
Using this syntax, an array is declared, but its
the value is not initialized. The declared array can be used
call only after some value has been assigned to it.
This can be done using the same expression as a declaration indicating
type, or write a new expression (Listing 9.7). A similar approach
you have met fundamental variables and constants
types.
Listing 9.7
1 / * declare an array in one expression,
2
we explicitly indicate the type of its elements
3
and initialize its value * /
4 var firstArray: [String] = ["x", "y", "z"]
["x", "y", "z"]
5 // declare an array with no elements
6 var secondArray: Array <Int>
7 // initialize its value
8 secondArray = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
9 // display the values of both arrays
10 firstArray
["x", "y", "z"]
11 secondArray
[1, 2, 3, 4, 5]
When trying to access an uninitialized Xcode array
will report an error.

Creating an empty array

If your goal is to get an empty array, then it should


initialized with a value that does not contain any elements
Comrade To do this, you can use one of the following methods:
❑ Explicitly specify the type of the created array and pass the value [] to it .

❑ By analogy with the empty value of one of the fundamental types


use a special function (like Int () for integer-
type), but enclose the type name in a square
parentheses, since the array has the [DataType] data type .

Listing 9-8 demonstrates both methods.


Listing 9.8
1 / * declare an array with an empty value
2 using the passed value * /
3 var emptyArray: [String] = []
[]
4 / * declare an array with an empty value
5 using a special function * /
6 var anotherEmptyArray = [String] ()
[]

This creates two empty arrays emptyArray and another-


Empty Array , already initialized with values (although not containing
elements), which means that you can interact with them.
The [DataType] () function also allows you to create an array consisting of
from a certain number of identical values. For this, as
input parameters, you must pass the count parameter , indicating

the number of elements, and the parameter repeatedValue , indicating


on the value of the elements (Listing 9.9).

Listing 9.9

1 / * declare an array with five


2 identical values * /
3 var alphaArray = [String?] (Count: 5, repeatedValue: nil)
[nil,
nil, nil, nil, nil]
As a result, an array of type [String?] Is created , containing five elements
cops nil . To get such an array, one could also pass
literal [nil , nil , nil , nil , nil] as a value.
The type of the repeatedValue parameter must be the same as the type
array elements.

Merging arrays

With an array value, as with values of fundamental types


data, various operations can be performed. One of them is
a merge operation is performed, in which the values of two arrays are
are combined into one, forming a new array. Note a few
moments:
❑ The resulting array will contain values from both arrays.
Islands, but the indices of these values may not coincide with the parent.
❑ The values of the elements of the arrays to be merged must have
the same data type.
The merge operation is performed using the already known operator
addition ( + ), as shown in Listing 9.10.

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"

The resulting value of alphabet is composed of three


other arrays.

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

1 var arrayOfArrays = [[1,2,3], [4,5,6], [7,8,9]]


This example creates an array containing arrays of type [Int]
as their elements. The type of the main array arrayOfArrays
is [[Int]] (with doubled square brackets with each
side).
To access an element of a multidimensional array, you must specify
multiple indices (Listing 9.12).

Listing 9.12

1 var arrayOfArrays = [[1,2,3], [4,5,6], [7,8,9]]


2 // get nested array
3 arrayOfArrays [2]
[7, 8, 9]
4 // get an element of the nested array
5 arrayOfArrays [2] [1]
7
The string arrayOfArrays [2] returns the third nested element
array arrayOfArrays . The string arrayOfArrays [1] [2] using
two indices, returns the second element of the subarray contained
in the third element of arrayOfArrays .

Basic array properties and methods

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

1 var someArray = [1, 2, 3, 4, 5]


[1, 2, 3, 4, 5]
2 // remove the third element of the array (with index 2)
3 someArray.removeAtIndex (2)
3
4 // remove the first element of the array
5 someArray.removeFirst ()
1
6 // remove the last element of the array
7 someArray.removeLast ()
five
8 / * the resulting array contains
9 only two elements * /
10 someArray
[2, 4]
After removing the indices of the remaining elements of the array, rebuild
are. In this case, the final array someArray contains only
two elements with indices 0 and 1.
If the array is immutable (stored in a constant), then you can
but use dropFirst () and dropLast () methods returning
a new array missing the first or the last
elements. Moreover, if there is nothing as a parameter to the function
pass, then one element is removed from the result. Otherwise
case, as many elements as passed to the method (Listing 9.21).
Listing 9.21
1 let someArray = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
2 // remove the last item
3 someArray.dropLast ()
[1, 2, 3, 4]
4 // remove the first three elements
5 var newArray = someArray.dropFirst (3)
[4, 5]
6 someArray
[1, 2, 3, 4, 5]

When using these methods, the main array someArray , with


which operations are performed does not change.
The contains () method determines the presence of some element in the array.
sive and returns Bool depending on the result (Listing 9.22).

Listing 9.22

1 let intArray = [1, 2, 3, 4, 5, 6]


[1, 2, 3, 4, 5, 6]
2 // check for element existence
3 let resultTrue = intArray.contains (4)
true
4 let resultFalse = intArray.contains (10)
False

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

The value 4 first occurs in the intArray in the element


with index 3. There is no value 99 in intArray , as a result
nil is returned .
To find the minimum or maximum element in an array
the global methods minElement () and maxElement () are applied . Dan-
methods only work if the array elements
can be compared with each other (Listing 9.24).
Listing 9.24
1 let intArray = [3, 2, 4, 5, 6, 4, 7, 5, 6]
[3, 2, 4, 5, 6, 4, 7,
5, 6]
2 // find the minimum element
3 intArray.minElement ()
2
4 // find the maximum element
5 intArray.maxElement ()
7

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

A collection is an unordered collection of unique items.


Unlike arrays, the elements of a set do not have any definition.
order, only the fact of the presence of some values in the set. A certain
element value may exist to live in it only once, that is, each value within one
set must be unique. Perhaps in Russian-speaking In the Swift documentation,
you have seen a different name sets - sets .
Based on the definition of a set, it is clear that it allows you to collect
multiple
number of unique values within one store.
Imagine that you offered your friends a joint visit to family. Each of them
should take one or two dishes with them. You semi- you are reading a
message from the first friend that he will take bread and vegetables. The
second friend is ready to bring the stew and water. All received values you
put it in a separate set to avoid duplication dishes. Your set already contains 4
elements: "Bread" , "Vegetables" , "Stew" and "Water" . A third friend later announces
that he is ready to take meat and vegetables. However, when trying to place
the Vegetables item in the set an exception will be thrown because the given
element
is already in the set. And rightly so, why do you need a second
set of vegetables!
The collection is created using a collection literal . In terms of syntax
it is identical to an array literal but guarantees that there is no
duplicate values.

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.

NOTE The datatype of the set members must be hashable, i.e.


support Hashable protocol.

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.

The data type of the set is Set <DataType> .


If there is no need to explicitly specify the data type of the values,
set, you can use the Set keyword without
structure <DataType> .

NOTE To create an immutable set, use the let statement,


otherwise, the var statement.
Listing 9.26 shows every possible way to create
sets.

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.

Creating an empty set

An empty set, that is, a set whose value has no elements,


is created using an empty set literal [] . you also can
pass the given literal to remove all existing elements
set (Listing 9.27).
Listing 9.27
1 // set with values
2 var dishes: Set <String> = ["bread", "vegetables"]
{"vegetables", "bread"}
3 // create an empty set
4 var members = Set <String> ()
[]
5 // remove all elements of the set
6 dishes = []
[]

Kit access and kit modification

Since a set is an unordered collection of items, there is no


any indexes or keys providing access
to values, the use of the subscript syntax is not possible.
The insert () method is used to create a new value in the set ,
to which the generated value is passed (Listing 9.28).
Listing 9.28

1 // create an empty set


2 var musicStyleSet: Set <String> = []
[]
3 // add a new element to it
4 musicStyleSet.insert ("Jazz")
"Jazz"
To remove an element from the set, use the remove () method , which
ry removes the element with the specified value and returns the deleted
value, or nil if the element to be removed does not exist. Also you
you can use the removeAll () method to remove all elements
set (Listing 9.29).

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 ()
[]

Checking whether a value exists in a set is carried out by the method


contains () . This method returns a Bool value depending on
from the result of the check (Listing 9.30).
Listing 9.30
1 // create a set
2 var musicStyleSet: Set <String> = ["Jazz", "Hip-Hop", "Rock", "Funk"]
3 // check for the existence of a value in the set
4 if musicStyleSet.contains ("Funk") {
five
print ("you like good music")
6 } else {
7
print ("listen to what I'm listening to")
8}
Console :
you like good music

Basic properties and collection methods

Earlier, we compared sets to math sets.


Different sets, like sets, can contain intersecting
non-overlapping and non-overlapping values. Swift allows
get the values of such sets depending on the needs
developer.
Listing 9.31 creates three different integer sets
(fig.9.1). One of the sets contains even numbers, the second - odd
nye, the third - both.
Listing 9.31

1 // dial with even digits


2 let evenDigits: Set = [0, 2, 4, 6, 8]
3 // set with odd digits
4 let oddDigits: Set = [1, 3, 5, 7, 9]
5 // set with mixed numbers
6 let differentDigits: Set = [3, 4, 7, 8]
There are both unique and common elements in the kits.
Figure: 9.1. Three sets of integer values

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.

NOTE The essence of call chaining is that if any


either a function, method, or property returns an object that has its own
properties or methods, they can be called in the same expression. Chain length
call (the number of called properties and methods) can be arbitrary.
To get all non-intersecting values, use the method
exclusiveOr () , shown in Listing 9.33.
Listing 9.33
1 var exclusive = differentDigits.exclusiveOr
[1, 4, 5, 8, 9]
(oddDigits) .sort ()
To get all elements from both sets, we apply
the union method union () , as shown in Listing 9.34.
Listing 9.34
1 var union = evenDigits.union (oddDigits) .sort ()
[0, 1, 2, 3, 4,
5, 6, 7, 8, 9]
The subtract () method returns all the elements of the first set that
rye are not in the second set (Listing 9.35).
Listing 9.35
1 var subtract = differentDigits.subtract (evenDigits) .sort () [3, 7]
Equivalence of sets
In fig. 9.3 shows three sets: aSet , bSet and cSet . The sets include
there are both unique and common elements. Set ASET -
this is a superset for bSet since it includes all elements
Figure: 9.3. Three sets of values with different relationships to each other

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 :

Sets are equivalent


The most important thing is that when creating a set, you do not forget to use
call the Set keyword , otherwise the result will be an array.
The isSubsetOf () method determines if one set is a subset
another, as bSet for aSet (Listing 9.37).
Listing 9.37
1 var aSet: Set = [1, 2, 3, 4, 5]
2 var bSet: Set = [1, 3]
3 if bSet.isSubsetOf (aSet) {
4
print ("bSet is a subset of aSet")
5}
Console :
bSet is a subset of aSet
The isSupersetOf () methodcalculates whether a set is a superset
for another set (Listing 9.38).
Listing 9.38
1 var aSet: Set = [1, 2, 3, 4, 5]
2 var bSet: Set = [1, 3]
3 if aSet.isSupersetOf (bSet) {
4
print ("aSet is a superset of bSet")
5}

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

A dictionary is an unordered collection of elements of the same


a type whose values are accessed using keys. Each
a dictionary item consists of a unique key that points to
the given element, and values. The key is not automatic

a statically set index (as in arrays), and a unique one for


dictionary is an arbitrary literal set by the developer
com. Most often, keys are string literals.
NOTE Unique dictionary keys do not need to be of type String. Basic
the requirement that a value of a type can become a key is that the given
the type must be hashable.
In other words, if each element of the array is an index pair -
value, then each element of the dictionary is a key-value pair.
The idea behind dictionaries is to use unique, arbitrary
keys for accessing values, while, as in sets, the order
the sequence of elements is not important.
The dictionary value is set using the dictionary literal.

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

are intended not only to get the values of dictionary elements,


but also to change them (Listing 9.42).
Listing 9.42
1 var countryDict = ["RUS": "Russia", "BEL":
["BEL": "Belarus",
"Belarus", "UKR": "Ukraine"]
"UKR": "Ukraine",
"RUS": "Russia"]
2 // get the value of the element
3 countryDict ["BEL"]
"Belarus"
4 // change the value of the element
5 countryDict ["RUS"] = "Russian Federation"
"Russian
Federation"
6 countryDict
["BEL": "Belarus",
"UKR": "Ukraine",
"RUS": "Russian
Federation"]

When changing a value using a Swift subscript, return-


Sets the value to set for this element.
As a result of the execution of this code, the countryDict receives
the changed value of the element with the key RUS .
To update the value of a dictionary item, you can also use
method updateValue () . As shown in Listing 9.43, upon installation
new value, this method returns an optional old value
value (or nil if the value for the mutable key does not exist).
Listing 9.43

1 var countryDict = ["RUS": "Russia", "BEL":


["BEL": "Belarus",
"Belarus", "UKR": "Ukraine"]
"UKR": "Ukraine",
"RUS": "Russia"]
2 // change the value of the element
3 countryDict.updateValue (value: "Russian
"Russia"
Federation ", forKey:" RUS ")
4 countryDict
["BEL": "Belarus",
"UKR": "Ukraine",
"RUS": "Russian
Federation"]

To change the value, a new value is passed to the updateValue method .


element reading and the forKey parameter , the value of which contains the key
the element to be changed.
Unlike the option using a subscript, this method
returns not the new, but the old value of the dictionary element.

In order to create a new element in the dictionary, it is enough to use


to create a subscript indicating a new non-existent key,
passing it the desired value (Listing 9.44).

Listing 9.44

1 var countryDict = ["RUS": "Russia", "BEL":


["BEL": "Belarus",
"Belarus", "UKR": "Ukraine"]
"UKR": "Ukraine",
"RUS": "Russia"]
2 // create a new dictionary item
3 countryDict ["TUR"] = "Turkey"
"Turkey"
4 countryDict
["BEL": "Belarus",
"UKR": "Ukraine",
"RUS": "Russia",
"TUR": "Turkey"]

To remove some element (key-value pair), it is enough


set the element to be removed to nil or use the remo-
veVa lueForKey by specifying the item key (Listing 9.45).

Listing 9.45

1 var countryDict = ["RUS": "Russia", "BEL":


["BEL": "Belarus",
"Belarus", "UKR": "Ukraine"]
"UKR": "Ukraine",
"RUS": "Russia"]
2 // delete a dictionary item
3 countryDict ["UKR"] = nil
nil
4 countryDict.removeValueForKey ("BEL")
"Belarus"
5 countryDict
["RUS": "Russia"]
The removeValueForKey method returns a value
the item to be removed.
If you try to access a non-existent element
a dictionary, this will not result in an error - Swift will simply return nil . it
says that any return value of a dictionary item is
an optional (Listing 9.46).
Listing 9.46
1 var countryDict = ["RUS": "Russian
["BEL": "Belarus",
Federation "," BEL ":" Belarus "," UKR ":" Ukraine "] " UKR ":" Ukraine ",
"RUS": "Russian
Federation"]
2 // get the value of the element
3 let myCountry: String = countryDict ["RUS"]!
"Russian
Federation"

To convert the return value of the element country-


Dict ["RUS"] of type String? forced into type String
value extraction.

Explicitly specifying a dictionary data type

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.

Creating an empty dictionary


In order to create an empty dictionary, it must be initialized
is a value with no elements. This is done using the construct
tion [:] , which is just a literal of a dictionary that does not have
elements (Listing 9.47).
Listing 9.47

1 var emptyDictionary: [String: Int] = [:]


[:]
2 var AnotherEmptyDictionary: Dictionary <String, Int> = [:]
[:]
Using the construction [:], you can also destroy all elements
you are a dictionary if you assign it to the dictionary as a value (fox-
Thing 9.48).
Listing 9.48
1 var countryDict = ["RUS": "Russian
["BEL": "Belarus",
Federation "," BEL ":" Belarus "," UKR ":" Ukraine "]
"UKR": "Ukraine",
"RUS": "Russian
Federation"]
2 countryDict = [:]
[:]
3 countryDict
[:]

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.

Basic properties and methods of dictionaries

Dictionaries, like arrays with sets, have a large number


properties and methods. You have already met some of them.
We will now consider the most important of the remaining ones.
The count property returns the number of elements in the dictionary (Lis-
Thing 9.49).
Listing 9.49

1 var someDictionary = [1, 2, 3, 4, 5]


[1, 2, 3, 4, 5]
2 // number of elements in the dictionary
3 someDictionary.count
five
If the count property is zero, then the isEmpty property returns true
(Listing 9.50).
Listing 9.50
1 var someDictionary: [Int] = []
[]
2 someDictionary.count
0
3 someDictionary.isEmpty
true
You can get all keys or all values if needed
a dictionary using the keys and values properties (Listing 9.51).
Listing 9.51
1 var countryDict = ["RUS": "Russian
["BEL": "Belarus",
Federation "," BEL ":" Belarus "," UKR ":" Ukraine "] " UKR ":" Ukraine ",
"RUS": "Russian
Federation"]
2 // all keys of the countryDict dictionary
3 var keys = countryDict.keys
LazyMapCollection
<Dictionary <String,
String>, String>
4 // all values of the countryDict dictionary
5 var values = countryDict.values
LazyMapCollection
<Dictionary <String,
String>, String>

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

1. Create a Chessman alias for the dictionary type [String: (alp


ha: Character, num: Int)?]. This type describes a chess
figure on the playing field. The dictionary key should store
the name of the figure, for example "White King", and in the meaning - cor-
tag indicating the coordinates of the piece on the playing field.
If nil is found instead of a tuple, it means the figure
killed (has no coordinates on the playing field).
2. Create a variable dictionary Chessmans type Chessman and pre-
add three arbitrary figures to it, one of which is not
must have coordinates.
3. Create an if-else construction that checks if killed
the figure passed to her (element of the Chessmans vocabulary ), and the output
sends information to the console either about the coordinates of the figure,
or about her absence on the playing field.
4. To get the coordinates of the passed figure, use
optional binding.
5. Save this program, as we will return to it
in the future.

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

The for statement is designed to loop through blocks of code.


Swift offers two forms of this operator: for and for-in .

For loop
SYNTAX

for start_expression; end_condition; action {


block_code
}

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.

2. The end_condition is checked , and if it returns a value


false , then the loop ends. If it returns
the value to true , then runs blok_koda enclosed in figur-
brackets.
3. After the execution of the block_code , an action is performed that changes
the value of the variable created in the start expression.
4. A new loop iteration starts, starting from step 2.
This loop has a classic C-shaped syntax and uses
is treated in the same way as in other programming languages. Example
shown in Listing 10.1.

Listing 10.1

1 for var i = 0; i <5; i ++ {


2
print (i)
(5 times)
3}
Console :
0
1
2
3
4

Here in the for loop the variable i is declared , initialized


starting value 0. After that, the condition i <5 is checked . Because
it is true, the first iteration of the loop body is executed, which
appears in the output of the next value of i to the console. Next is executed
an i ++ expression that increments i by one. This is the first
the iteration ends.
The second iteration is started, at the very beginning of which the
the condition i <5 is true, and if it is true, the body code is executed
cycle. Etc.

This procedure continues as long as the condition i <5


will not return false . After that, the cycle ends its work.
During its execution, five values are printed to the console: integers
numbers from 0 to 4.

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

1 var myArray = ["RUS", "UK", "USA"]


["RUS", "UK", "USA"]
2 for var i = 0; i <myArray.count; i ++ {
3
print (myArray [i])
(3 times)
4}
Console :

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.

The for-in loop


SYNTAX

for variable in sequence {


block_code
}
The for-in loop executes a block of code for each item in the sequence. Pe-
before each iteration, the next element from the sequence is assigned
variable that is available in the code block. After going through all the elements,
sequence, the cycle ends its work.
The body of the loop containing the code_block being executed is enclosed in curly braces.
The for-in loop , just like the for loop , allows you to perform certain
a limited number of code iterations. Moreover, he has much more
ample opportunities. An example of using a loop is given
in Listing 10.5. This code adds all the numbers from 1 to 10 and displays
Outcome in Results Area.

Listing 10.5

1 var totalSum = 0
0
2 for i in 1 ... 10 {
3
totalSum + = i
(10 times)
4}
5 totalNum
55

The for statement is followed by the name of the declared variable,


in this case it is i . Then it is assigned the first value from the dia-
zone 1 ... 10 , that is, 1, and the code of the cycle body is executed. In the next
the iteration variable i is assigned the second value in the range
and the body of the loop is re-executed. Etc.
Even though a new variable i is created in the loop , Swift does not
requires writing a var statement .
The variable that is created in the for-in loop is local
for a given cycle. That is, if there is an external of the same name
variable or constant, then its value will not intersect with the
local variable and all changes to the local parameter are not
affect the external one (Listing 10.6).
Listing 10.6

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 .

But what if you want to get all the elements of a non-array or


typing, but a dictionary? To do this, you must use the already familiar
we use tuples as a mutable parameter for the loop (Listing 10.8).
Listing 10.8
1 var countrysAndBlocks = ["Russia": "EAEU", "USA": "NATO",
"France": "EU"]
2 for (countryName, blockName) in countrysAndBlocks {
3
print ("\ (countryName) entered into \ (blockName)")
4}

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!

A situation is possible when you need to get not a key-value pair


from a dictionary, but only the key or only the value. To do this, in a tuple
in place of the element that should not be loaded, you must
insert an underscore character (Listing 10.9).
Listing 10.9
1 var countrysAndBlocks = ["Russia": "EAEU", "USA": "NATO",
"France": "EU"]
2 for (countryName, _) in countrysAndBlocks {
3
print ("country - \ (countryName)")
4}

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

4 for (teamName, results) in resultsOfGames {


5 // processing the array of results
6
for oneResult in results {
7
print ("Playing with \ (teamName) - \ (oneResult)")
8
}
9}

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.

10 .2. While repetition statements


and repeat while
The while and repeat while statements let you execute a block of code until
as long as the tested condition is true. That is, in a sense, it is
combined for and if statements .

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.

An example implementation of a while loop is shown in Listing 10.13, in which


all numbers from 1 to 10 are added and the result is displayed.

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.

Repeat while loop


In contrast to the while statement, the repeat while statement is cycle followed by
checking the condition. In such a cycle, first the code is executed, and only
then the condition is checked.
NOTE In the first version of Swift, the repeat while loop was called do while.

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

The difference between the while and repeat while statements is


that the body code of the repeat while statement is executed at least once.
That is, even if the condition returns false during the first iteration , the body
code
the cycle has already been completed at this point.

10 .3. Cycle management

Swift, by analogy with other programming languages, has two


operators that can influence the course of the loops are operators
break and continue .

The continue statement

The continue operator is intended to go to the next iteration,


ignoring the following code. Listing 10.15 shows a pro
gram, in which the variable alternately takes values from 1 to
10, and when the value is odd, it is printed to the console.
Listing 10.15
1 for i in 1 ... 10 {
2
if i% 2 == 0 {
3
continue
4
} else {
five
print (i)
6
}
7}
Console :
1
3
five
7
Nine

The parity of a value is checked using the compute operation


dividing the remainder by two. If the remainder of the division is zero,
hence, the number is even and the transition to the next iteration occurs.
The continue statement is used for this .
Break statement

The break operator is intended for early termination of work


you cycle. In this case, all subsequent code in the body of the loop is ignored.
eats. Listing 10.16 randomly computes ten times
a number in the range from 1 to 10. If this number is 5, then to the console
a message is displayed with the number of the iteration and the execution of
the loop
is done.
Listing 10.16

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.

A situation may arise when it is necessary to


interrupt the execution of the external - there are labels for this in Swift
(Listing 10.17).
Listing 10.17
1 mainLoop: for i in 1 ... 5 {
2
for y in i ... 5 {
3
if y == 4 && i == 2 {
4
break mainLoop
five
}
6
print ("\ (i) - \ (y)")
7
}
8}
Console :
eleven
12
thirteen
fourteen
fifteen
2-2
2-3
A label is an arbitrary set of characters that
is placed before the repetition operator and separated from it by the colon
chiem.
In order to change the course of the outer loop, after the operator
break or continue you must specify a label name.

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.

11 .1. Function declaration


Functions help structure code by combining repetitive
There are blocks for their repeated use. The functions allow
follow the principle of "don't write the same code twice".
NOTE Programmers have a joke that “any code dreams of becoming
function ".

SYNTAX

func functionName (input_parameters) ->


ReturnValueType {
function_body
}

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
}

In the examples presented, the function declaration is spaced out to different


lines for the convenience of reading the code. You don't have to do this
you can write any elements of a function declaration on one line.
For instance:

func myFirstFunc (someValue: Int) -> String {


return String (someValue)
}

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")
}

Also, if the function does not return any value, then


the writing of any construction can be completely omitted.
For instance:

func printMessage () {
print ("Message received")
}

The process of calling a function is called a function call .


11 .2. Input parameters

and the return value


The function can take some arguments as input
values and return some result of their work. Wherein
outputting information to the console is not a return value.
Suppose you need to write a function that folds
gives two values, then divides them by π (3.14) and returns
result. In this function, the input parameters will be two un-
known numbers in advance. It makes no sense to convey as
as the input argument and the number π, since it is known in advance and its
can be hardcoded in the body of the function. Calculation result
will be the return value of the function.

Simple inputs and outputs

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

1 func printCodeMessage (requestCode: Int) -> () {


2
print ("Response code - \ (requestCode)")
3}
4 // call the function
5 printCodeMessage (200)
6 printCodeMessage (404)

Console :

Response code - 200


Response code - 404
The printCodeMessage ()
function has one input parameter -
requestCode . The name of this parameter is used inside the body of the function.
tions to generate an outputted string literal.

All parameters in the input argument list must


without fail to have values at the very beginning of the work
tion. For this, the values of these parameters must be passed
into the function when it is called. They are transmitted in the same order in
which
described in the function declaration. Moreover, only for the first
parameter there is no need to specify the name, the values of all the rest
parameters must have the name of the input argument, which
they are assigned. In Listing 11.3, we implement a function that
takes three values as input, adds them and outputs the result
to the console.

Listing 11.3

1 func sum (a: Int, b: Int, c: Int) -> () {


2
print ("Sum - \ (a + b + c)")
3}
4 sum (10, b: 51, c: 92)
Console :

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.

There is a way to tell Swift that there is no non-


the need to specify the names of the input arguments when calling it.
To do this, before the name of such a parameter, you must put
an underscore character separated from the name with a space (or
sting 11.4).
Listing 11.4
1 func sum (a: Int, _ b: Int, c: Int) -> () {
2
print ("Sum - \ (a + b + c)")
3}
4 sum (15, 12, c: 9)
Console :
Amount – 36
As you can see, there is an underscore before the b argument.
and when the function is called, only the name is specified for the c parameter
.

Parameter copy variables

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 :

Response code - 200


Response code - 404
The function accepts two constants as input: a and b . These constants
passed to the function as pass-through parameters, which allows
change the values of external passed parameters inside the function
with the preservation of their values after the completion of its work.
NOTE The pass-through parameter argument can only be a change
naya. Constants or literals cannot be passed as they are immutable.
dumb.

Function as argument value

You can use the value returned by some function


taking as the value of an input argument to another function. Ta-
thus, if any operator or function as
the input argument requires a value of a certain type, then you
you can pass the function itself as an argument
(Listing 11.7).
Listing 11.7

1 func generateString (code: Int, message: String) -> String {


2
let returnMessage = "Received message" \ (message) "
with code \ (code) "
3
return returnMessage
4}
5 // use function as value
6 print (generateString (200, message: "Server is available"))
Console :

Received the message "Server available" with code 200

The generateString () function returns a String value . Due


with this at the end of the function, the return keyword is used with the
the return parameter.
The print () function already known to us takes as input a string
a literal of type String that is printed to the console. Since the first
the function returns a value of the same type that it takes as input
the second, then you can specify as an input argument to the function
print () is the name of the first function.
Input parameter with a variable number of arguments
In some situations, it is necessary for the function to receive an unknown
a known number of arguments of the same type. For this you can
either take an array of values, or create a variadic pair-
meter, that is, a parameter with a variable number of arguments.
Variable parameter is indicated in the list of incoming parameters
by specifying the closed range operator ( ... ) immediately after the type input-
parameter. Values for this parameter when calling the function
are set with commas.
Consider the example from Listing 11.8. Imagine that the remote
the server sends you multiple responses for each request. Each
the answer is an integer, but the number of answers is not known in advance.
You need to write a function that takes everything as input
received answers and prints them to the console.

Listing 11.8

1 func printRequestString (codes: Int ...) -> () {


2
var codesString = ""
3
for oneCode in codes {
4
codesString + = String (oneCode) + ""
five
}
6
print ("Answers received - \ (codesString)")
7}
8 printRequestString (600, 800, 301)
9 printRequestString (101, 200)

Console :

Answers received - 600 800 301


Answers received - 101 200
The codes parameter can contain any number of values
of the specified type. Inside the function, it is treated as an array of values,
so it can be processed in a for-in loop .

One function can have only one variable parameter. is he


must be at the very end of the list of input parameters.

Tuple as return value


When we got acquainted with tuples, we said that their strength
Noah is the ability to group several values in one.
This group of values can be used as a return value.
the function. The function shown in Listing 11.9 takes on
input, the server response status code and depending on which dia-
the passed code belongs to the section, returns a tuple with its description.
Listing 11.9

1 func getCodeDescription (code: Int) -> (Int, String) {


2
let description: String
3
switch code {
4
case 1 ... 100:
five
description = "Error"
6
case 101 ... 200:
7
description = "Correct"
8
default:
nine
description = "Unknown"
ten
}
eleven
return (code, description)
12 }

13 print (getCodeDescription (150))

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 a default value


niyu, that is, the value that will be assigned to the parameter if for
no input value was passed to it. Let's finalize
the sumWallet () function from the previous listing to correctly display
working of a situation when no input wallet is transferred to it
(Listing 11.12).
Listing 11.12
1 func sumWallet (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 // wallet with bills
12 var wallet = [50, 100, 100, 500, 50, 1000,
[50, 100, 100, 500,
5000, 50, 100]
50, 1,000, 5,000, 50,
100]
13 // count the sum of all bills
14 sumWallet (wallet)
6950
15 sumWallet ()
nil
As you can see, in the function in question appeared immediately
several innovations. Let's look at each of them separately.
Has the wallet input data type changed from [Int] to [Int]? ...
This is an optional array of integer values. Pay attention
mania that the optional sign is after the square bracket
array, which means that the array itself may not be present, but
if it is present, then at least one
element of type Int .
Also, the input parameter has a default value - nil .
It is assigned to the wallet variable if, when calling
the function is not passed the value of this parameter.
Additionally, the return type has changed. In that case,
if the input value for the wallet parameter does not exist (that is, equal to
nil ), the function will return nil as a response .
To correctly determine if wallet has a value, use
used if statement . Its body contains a return statement , which
terminates the function and returns nil .
NOTE A function can have multiple return statements. Each of
them terminates the function execution and returns some value.

External argument names

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

17 func sumWallet (banknotsFunction wallet: (Int) -> ([Int]))


18 -> Int? {
19
// call the passed function
20
let myWalletArray = wallet (Int (arc4random_uniform (10)))
21
var sum: Int = 0
22
for oneBanknote in myWalletArray {
23
sum + = oneBanknote
24
}
25
return sum
26 }
27 // passing function to function
28 sumWallet (banknotsFunction: generateWallet)
2700

The value in the results area is likely to differ from


what will be displayed for you. This is due to the use of the global
function arc4random_uniform () , which returns a random number.
The generateWallet () function creates an array of bills of such length that
passed to it as an input parameter. In the typesOfBanknotes array
contains all possible types of notes. The essence of the function is as follows:
the bill is randomly removed from the typesOfBanknotes array ,
It follows that it is placed in an array purse wallet , which is the
the return value of this function.
The for loop uses the underscore instead of a variable
scribbles. With this wonderful variable replacer, we already
met more than once. In this case, it replaces the created
parameter in the loop, since it is not used inside the loop. In re-
As a result, no memory is allocated for this parameter, which is favorable
affects work.
As a wallet input parameter of the sumWallet () function
the type of the generateWallet () function is specified . When calling sumWallet ()
does not-
you only need to pass the name of the required function.
NOTE As noted earlier, the underscore character can be
change the declared parameter almost anywhere, provided that the given
this parameter will not be used. They can also replace the input
function parameters, if only the fact of their transfer is important, and not the value:
func someFunction (_: Int) -> () {}
someFunction (100)
Function as return value
Since the function has its own individual type, it can be specified
not only for input arguments, but also for the output value
niya. In Listing 11.15, we'll rewrite the money counting function again.
in your 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"

As the return type of the returnPrintTextFunction () function


values are of type () -> (String) . It matches the data type
printText () functions .
As a result of assigning the returned function to the constant newFunc-
tionInLet, its data type becomes () -> (String) , and it itself stores
in itself a function that can be called. This can be verified by
if you display the help window for this constant (Fig. 11.1).
Figure: 11.1. Help window for a constant holding a function
as a value

11 .3. Function body as value


A variable or constant can store a function in itself - about this
we have already found out. But in order to assign a constant

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.

11 .4. Nested functions


All functions that we created earlier are global.
In addition to them, you can create local functions nested each
into a friend. They have a limited scope, i.e.
are directly accessible only within the parent function. Pre-
put an infinite plane and a point on this plane. Dot
has some coordinates. She can move on a plane.
Let's create a function that takes the coordinates of a point as input
and the direction of movement, after which it returns the new coordinates
(Listing 11.17).

Listing 11.17

1 func oneStep (var coordinates: (Int, Int), stepType: String) ->


(Int, Int) {
2
func up (inout coords: (Int, Int)) -> (Int, Int) {
3
return (coords.0 + 1, coords.1)
4
}
five
func right (inout coords: (Int, Int)) -> (Int, Int) {
6
return (coords.0, coords.1 + 1)
7
}
8
func down (inout coords: (Int, Int)) -> (Int, Int) {
nine
return (coords.0-1, coords.1)
ten
}
eleven
func left (inout coords: (Int, Int)) -> (Int, Int) {
12
return (coords.0, coords.1-1)
thirteen
}
fourteen
fifteen
switch stepType {
sixteen
case "up":
17
return up (& coordinates)
eighteen
case "right":
19
return right (& coordinates)
20
case "down":
21
return down (& coordinates)
22
case "left":
23
return left (& coordinates)
24
default:
25
return (0, 0)
26
}
27 }
28 var coordinates = (10, -5)
(.0 10, .1 -5)
29 coordinates = oneStep (coordinates, stepType: "up") (.0 11, .1 -5)
30 oneStep (coordinates, stepType: "right")
(.0 11, .1 -4)
The oneStep () function moves a point along the plane.
It has several nested functions that call
depending on the value of the stepType parameter . This set
functions is only available inside the parent function oneStep () .

11 .5. Reloading functions

The Swift can reboot (overloading) functions . It means,


that functions can be created in the same scope
with the same names. Functional differences should be
only in the types and names of the input parameters and the return type
values. Listing 11.18 shows functions that can
coexist at the same time.
Listing 11.18
1 func say (what: String) {}
2 func say (what: Int) {}
These functions have the same say () name , but a different set of input
parameters. Although the parameters have the same name, their data type
differs.
If you have the same list of input parameters (their names
and types are identical), then to reload functions, you must specify
different types of return values. Consider an example from listing
ha 11.19. The functions presented in it can also coexist
at the same time.
Listing 11.19
1 func cry () -> String {
2
return "one"
3}
4 func cry () -> Int {
five
return 1
6}
However, in this case, you cannot assign a value to the function
a variable or constant without an explicit type specification (Listing 11.20).
Listing 11.20
1 let resultOfFunc = say ()
In this case, Swift simply does not know what type of data the constant has,
therefore cannot determine which function to call. As a result
Xcode will report an error.
If you specify in any way the data type of a constant that is consistent
with the return type of one of the functions, then the code will work
correct (Listing 11.21).
Listing 11.21
1 let resultString: String = say ()
2 let resultInt = say () + 100

11 .6. Recursive function call

The function can call itself. This mechanism is called re-


course . It is possible that you have encountered recursions in others.
programming languages. You need to be extremely careful-
with this mechanism, since inadvertently you can create
An "infinite loop" in which the function will call infinitely

herself. When using recursions correctly, the function is always


eventually will complete its work.
An example of recursion is shown in Listing 11.22.
Listing 11.22
1 func countdown (firstNum num: Int) {
2
print (num)
3
if num> 0 {
4
// recursive function call
five
countdown (num -1)
6
}
7}
8 countdown (20)
This function counts down the digits, starting
from the passed parameter firstNum and ending with zero. This algorithm
implemented by recursive function call.

Exercise 1

Let's go back to task 2 from the previous chapter. Combine writing


sled code for analyzing a chess collection stored in change
Noah Chessmans , in a function named chessAnalizer () . As
as an input parameter, the function must take a dictionary of that
the same type as the Chessmans variable .

Assignment 2

Create a function that is meant to change


the composition and characteristics of the figures in the Chessmans variable . In
ka-
as input parameters, it must take the transfer itself.
Chessmans variable (as a pass-through parameter) into which
changes are made, shape name ( String value )
and an optional tuple of shape coordinates (value of type
(Character, Int)? ).
In this case, the fact of the existence of
figures in the dictionary. If the figure does not exist, then the information
about it is added, otherwise the information is updated
according to the transmitted information.

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.

12 .1. Functions as closures


Functions are a special case of closures, since they have
with the following properties:
❑ group code for reuse;
❑ can be called multiple times by means of the assigned
name;
❑ can be passed as arguments.
Let's look at working with closures using examples. Let's go back to the
example
with an electronic wallet and banknotes of various denominations in it
and write a function that will accept an array-co-
silk and return an array of all hundred-ruble bills (Listing 12.1).
Listing 12.1
1 // function of selecting notes
2 func handle100 (wallet: [Int]) -> [Int] {
3
var returnWallet = [Int] ()
4
for banknot in wallet {
five
if banknot == 100 {
6
returnWallet.append (banknot)
7
}
8
}
nine
return returnWallet
10 }
11 // electronic wallet
12 var wallet = [10,50,100,100,5000,100,50,50,500,100]
13 handle100 (wallet)
On each call, the handle100 () function will return an array
hundred-ruble bills. Here handle100 () is a closure, since it is
possesses the previously described properties:
❑ groups the code;
❑ can be reused;
❑ can be passed as an argument (with this property we know
comics when passing functions as input parameters
and returned as output values).
Let's expand the functionality of the code by writing an additional function
for
selection of banknotes worth 1000 rubles or more (Listing 12.2).
Listing 12.2

1 func handleMore1000 (wallet: [Int]) -> [Int] {


2
var returnWallet = [Int] ()
3
for banknot in wallet {
4
if banknot> = 1000 {
five
returnWallet.append (banknot)
6
}
7
}
8
return returnWallet
9}
10 var wallet = [10,50,100,100,5000,100,50,50,500,100]
11 handleMore1000 (wallet)

As a result, it turns out that when writing two functions,


code duplication occurs to a great extent. Function difference
handle100 () and handleMore1000 () only in a checked condition. The rest
The code in both functions is the same.
There are two ways to solve this problem:
1. Implement all functionality within one function. With this
approach we met when we studied the topic of nested functions
tions.

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

1 // single function of forming the resulting array


2 func handle (wallet: [Int], closure: (Int) -> Bool) -> [Int] {
3
var returnWallet = [Int] ()
4
for banknot in wallet {
five
if closure (banknot) {
6
returnWallet.append (banknot)
7
}
8
}
nine
return returnWallet
10 }
11 // comparison function with the number 100
12 func compare100 (banknot: Int) -> Bool {
thirteen
return banknot == 100
14 }
15 // comparison function with number 1000
16 func compareMore1000 (banknot: Int) -> Bool {
17
return banknot> = 1000
18 }
19 var wallet = [10,50,100,100,5000,100,50,50,500,100]
20 handle (wallet, closure: compare100)
21 handle (wallet, closure: compareMore1000)

The handle () function takes a closure as an input parameter


one of the functions for checking the condition and in the if statement calls the
this function. Validation functions take as input the analysis
the closed bill and return Bool depending on the result
comparisons.
To get bills of a certain denomination, you need
call the handle () function and pass the name of one of the functions into it
checks.
As a result, we will get a very high-quality code, which is enough
easy to expand.
12 .2. Closing expressions
Imagine that there was a need to write functions for
boron of bills for many and many conditions (find all fifty dollars, all
bills with denominations of less than 1000 rubles, all bills that are without
remainder divided by 100, etc.). Selection conditions can be great
a bunch of. At some point, write a separate function to
verification for each of them will be a rather difficult task, since
in order to use a single check function, you must
know the name of the checking function, and there can be dozens of them.
In such a situation, it becomes much more effective to use
closure expressions.
Closure expressions are unnamed closures written
in lightweight syntax.

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.

Closing expressions that we met when studying functions


tion, did not have any input parameters, and their functional
the type was not specified.
NOTE The lightweight syntax of closure expressions makes it easier to work with
that allows you not to write unnecessary code, and optimized code saves time
and brings you additional benefits.

Now let's start optimizing the already used closure


expressions. When declaring an input parameter closure in a function
the handle () function specifies its functional type (it takes
function of type (Int) -> Bool ), so when passing a closing
expressions there is no need to duplicate this information
(Listing 12.5).
Listing 12.5
1 // selection of banknotes with denominations over 1000 rubles
2 handle (wallet, closure: {banknot in
3
return banknot> = 1000
4 })
5 // selection of banknotes worth 100 rubles
6 handle (wallet, closure: {banknot in
7
return banknot == 100
8 })
In terms of the closing before the keyword in need
pass only the name that will be assigned to the transmitted into it
the value of the next element of the wallet array .
In the code of the handle function, when the closure function is called, it is
passed
the banknot parameter - it is for this reason that we specify as
the input argument is a variable with the same name.

12 .3. Implicit value return

Closing expressions allow you to greatly optimize


to write programs. This is just one of the many features Swift provides
providing beautiful and understandable source code for your projects.
If the body of the enclosing expression contains only one expression,
which returns some value (using the operator
pa return ), then such closures can implicitly return an output
value. Implicitly means without using the return statement
(Listing 12.6).
Listing 12.6
1 // selection of banknotes with denominations over 1000 rubles
2 handle (wallet,
3
closure: {banknot in banknot> = 1000})
4 // selection of banknotes worth 100 rubles
5 handle (wallet,
6
closure: {banknot in banknot == 100})
As a result, we have achieved that the closing expression is
is compiled into just one short line and at the same time the code becomes
clearer.

12 .4. Abbreviated parameter names

Let's continue optimizing the closures we use. For one-


inline closure expressions Swift automatically provides
accessing input arguments using shorthand names to-
mortar. The shorthand names for accessing input arguments are written
in the form $ parameter_number . Input parameter numbers start with
from scratch.
NOTE In abbreviated notation of input parameter names,
the value $ 0 points to the first argument passed. To access the second
the argument must use the notation $ 1, the third one must use $ 2, etc. etc.
Let's rewrite the call to the handle () function using shorthand
names (Listing 12.7).
Listing 12.7
1 // selection of banknotes with denominations over 1000 rubles
2 handle (wallet,
3
closure: {$ 0> = 1000})
4 // selection of banknotes worth 100 rubles
5 handle (wallet,
6
closure: {$ 0 == 100})
Here $ 0 is the input argument banknot of the input argument-closure
Kania closure in the function handle () .
In cases where the input function parameter consists of only one
expression, using closure expressions makes the code
more understandable.

If the input function parameter is the last in the list


the input parameters of the function (as in this case in the function
handle () where the closure is the last parameter ), Swift allows
put its value (closing expression) outside parentheses
(Listing 12.8).

Listing 12.8

1 // selection of banknotes with denominations over 1000 rubles


2 handle (wallet)
3
{$ 0> = 1000}
4 // selection of banknotes worth 100 rubles
5 handle (wallet)
6
{$ 0 == 100}

This example may not fully demonstrate the need


the possibility of moving the closure outside the parentheses, but in the case
when
a multi-line closure expression, this technique makes the code
much clearer. In Listing 12.9, in addition to the wallet array , which
ry contains the bills in the wallet, we will create an array
successbanknots , containing only those allowed for use
bills. The task is to select everything from the wallet
bills that are presented in successbanknot .

Listing 12.9

1 let successbanknot = [100, 500]


2 handle (wallet)
3
{banknot in
4
for number in successbanknot {
five
if number == banknot {
6
return true
7
}
8
}
nine
return false
10 }

Inside the closing expression, we access the success- array


banknot without passing it directly through the input arguments,
but the work inside is not done with the successbanknot array itself ,
but with a copy of it. Due to this Swift feature, any changes
an array inside a closure expression will not contribute any of the
changes to the original array.

12 .5. Closure variables


Earlier, when we looked at working with functions, we passed
an unnamed function as the variable value. So we find out
whether that variables can store closure expressions. we
confined themselves to assigning value, while omitting the questions
setting the functional type and input parameters.
Consider the example from Listing 12.10 in which we create a constant
closure and define a closure expression as its value,
accepting nothing and returning nothing.
Listing 12.10

1 let closure: () -> () = {


2
print ("Closing expression")
3}
4 closure ()

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.

NOTE Closing expressions can be stored either in constants or


and in variables. Choose the correct parameter type depending on whether you are
whether its value has been overwritten.
In most cases, it doesn't make sense to store the closure in a variable.
12 .6. Capturing Variables
We have not considered another interesting Swift feature that allows-
fixing the values of parameters (variables and constants),
which they had at the time of the definition of the closure.
Let's look at an example (Listing 12.12). There is a set of parameters,
which are not passed as input arguments to the closure,
but they are used for their own purposes. Each time you call such a
it will determine the values of these parameters before
to proceed with the operation with their participation.
Listing 12.12

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 ()

The closure stored in the constant closureSum adds values


variables a and b . When their values change, the return closure
the meaning also changes.
In order to fix the values of some parameters, do not
it is necessary to write their names.
There is a way to "capture" the values of the parameters, that is,
fix the values that these parameters have at the moment
writing a closing expression. To do this, at the beginning of the closure
in square brackets, you must list the captured trans-
mennye, separated by a comma, followed by the keyword in .
Rewrite the closureSum variable initialized closure
so that it captures the original values of the re-
variables a and b (Listing 12.13).

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.

12 .7. Array sorting method

Swift offers a large number of functions and methods to enable


which greatly simplify application development. Od-
one such method is sort () , which is used to sort
ki arrays (both string and numeric). It takes at the entrance
the array to be sorted and the sort condition.
The accepted sorting condition is an ordinary closing
the expression that is called inside the sort () method takes on
input two successive elements of the sorted array and returns
the Bool value depending on the result of their comparison. For
to get a sorted array, you must pass co-
the corresponding closure. In Listing 12.14, we'll sort the array
myArray so that the elements are positioned as needed
plant growth. To do this, we pass the following closure to the sort () function
expression that returns true when the second comparison is
more.
Listing 12.14
1 var array = [1,44,81,4,277,50,101,51,8]
2 array.sort ({(first: Int, second: Int) -> Bool in
3
return first <second
4 })
Now we will apply all the previously discussed ways to optimize the
Closing expressions:
❑ remove the functional type of closure;
❑ Replace variable names with abbreviated names.
The result is the expression in Listing 12-15.
As in the previous example, here you also need to sort
an array myArray so that the elements are positioned
Ascending. To do this, the following closure is passed to the sort () method.
an expression that returns true when the second being compared
the number is greater.
Listing 12.15
1 var array = [1,44,81,4,277,50,101,51,8]
2 var sortedArray = array.sort ({$ 0 <$ 1})
As a result, the code is more readable and beautiful.
Swift has a special implementation of the variable comparison closure-
data, which takes two compared elements as input, and
rotates Bool values . It is called a binary operator
comparisons. We have known him for a long time.
That is, as a closing expression, you can write a binary
comparison operator without specifying the names of the compared
parameters,
since this operator is binary, that is, committing
an operation with two operands (Listing 12.16).
Listing 12.16
1 var array = [1,44,81,4,277,50,101,51,8]
2 var sortedArray = array.sort (<)
I hope you are pleasantly surprised by the awesome features of Swift!
Part IV
Non-trivial Swift features
In the previous chapters of the book, there was a smooth immersion in the philosophy
Sofia Swift by learning the basic features of this language. Very
it is important that you understand and understand the principles of programming in this
wonderful language. It is for this reason that a large number of
There are many examples, and each of them was described in detail.
And although earlier we have repeatedly discussed various objects, their properties
properties and methods, so far we have not described how these objects are created
and work.
Since Swift adheres to the paradigm "everything is an object", then any
a parameter (variable or constant) with a specific data type is
an object . To implement new objects, you have already studied many different
personal data types, but as noted earlier, Swift has the functionality
cash creation of your own object types. There are three
mechanisms: enumerations (enum), structures (struct) and classes (class). In what
difference between them? How to create and use them? This part is
talks about it. We will discuss what object types are in general and why.
the difference between them. The next step will be to study the mechanisms for
allowing to extend the capabilities of object types, including protocols,
extensions, generics, etc. etc.
When studying new material, the already studied
ny, so if you missed any of the tasks in the previous
chapters, I ask you to return to them.

✓ Chapter 13. OOP as a foundation


✓ Chapter 14. Enumerations
✓ Chapter 15. Structures
✓ Chapter 16. Classes
✓ Chapter 17. Properties
✓ Chapter 18. Subscripts
✓ Chapter 19. Inheritance
✓ Chapter 20. Any and AnyObject aliases
✓ Chapter 21. Initializers and Deinitializers
✓ Chapter 22. Deleting Instances and ARC
✓ Chapter 23. Optional chains
✓ Chapter 24. Extensions
✓ Chapter 25. Protocols
✓ Chapter 26. Non-standard data types and getting help
information
✓ Chapter 27. Generic templates
✓ Chapter 28. Error processing
✓ Chapter 29. Non-trivial use of operators
13 OOP as a foundation
Object-oriented programming style is fundamental
the whole development of programs in Swift. We have repeatedly
met with one fundamental rule for this language
"Everything is an object." It's time to start learning the most
interesting and complex mechanisms available in Swift. This chapter
will tell you about the most important terms and concepts that
can you master all the following material.
You may have studied OOP in the past in computer science lessons
at school or at the institute, or maybe independently. In this case
you, of course, have memorized three OOP tenets: encapsulation,
sequencing and polymorphism. In this book, I do not offer their sub-
hard learning and memorization, I prefer a hands-on approach.
And the principles of development in Swift language differ in many respects
from
those for Pascal, Visual Basic, Python, PHP, etc.

13 .1. Instances

Enumerations, structures and classes have one very important feature.


ness: they can be instantiated . When you define
an object type, you just specify the data type. Instantiation
object type is the creation of a storage (variable or constant
you) for data of a certain type.
NOTE Despite the fact that we have talked about objects many times, the correct
call them instances. Apple talks about this in the Swift documentation.
Instances of classes were called objects in other programming languages,
and instances of structures and enumerations are just instances. Since the functional
the tax of structures, enumerations and classes is very similar in its capabilities, in Swift
the corresponding objects are simply called instances.

Imagine some class Automobile is defined .


This class is a data type. This "Car" is
not some specific car, but only a design, with the help
which can identify this particular vehicle. If
create a variable of type Automobile , then as a result we get
an instance of this class (Figure 13.1).
Figure: 13.1. Class and its instance
The class itself in the figure does not have pronounced features, since there is
still no
it is known what specific object of the real (or unreal)
the world he will determine. But when bmw variable is created like
Automobile , we already know that with an instance in this variable we
we will work as if with a real BMW car.
An object type can endow an instance with some characteristics.
kami. For the Automobile class, it could be: make, model, color,
maximum speed, engine displacement, etc.
The ect types are called properties . For bmw variable values
these properties could be as follows:
a.brand = "BMW"
a.type = "3"
a.maxSpeed = 210
a.engineCapacity = 1499

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 ()

Thus, creating an instance, we can fill it with our own


information and use its methods. And properties and methods
determined by the data type.
A way of developing programs using object types on
called Object Oriented Programming (OOP).
A lot can be achieved with this programming style.
when developing programs. Despite the fact that instead of the term "ob-
ect "the term" instance "is used, OOP abbreviation is
well-established in programming and in Swift it does not transform
in the image intensifier.

13 .2. Namespaces

Namespaces are named fragments of


gram. Namespaces have one very important property - they
hide their internal implementation and do not allow getting
accessing objects inside the namespace without accessing yourself
namespace. This is a wonderful feature that makes you
you can have objects with the same name in different programs
wanderings of names.
We've already talked about variable scopes a number of times.
and functions. Namespaces are exactly what the application implements
different scopes.
The simplest example of scoping is
live function. All variables declared in it outside the function
are not available. But the function is not a namespace,
since it does not allow access to objects within itself from the outside.
Namespaces include enumerations , structures, and class
sy . It is their study that we will do. Also to spaces
names include modules, but their study is not the subject of this
books. Modules are actually the top level of namespaces.
At its simplest, your program is a module, which means it is
a separate namespace whose name is the name of the
your application. Also modules are various frameworks.
By the way, we already worked with one of them when we imported
module: import Foundation .
This framework is called Cocoa's Foundation Framework and contains
a large number of functional mechanisms that allow
expand the capabilities of the program.
Some namespaces may include others: for example, the UIKit module,
designed for developing iOS applications, in your code you-
completes the import of the Cocoa's Foundation Framework module.

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.

14 .1. Enumeration syntax

An enumeration is an object data type that provides additional


stupas to various predefined values. Consider it
as a list of possible values, that is, a set of constants, values
which are alternatives to each other.
Consider a storage that describes an arbitrary monetary
unit (Listing 14.1). In order to solve the task
using the data types you learned earlier, you can use the type
String . With this approach, you will have to keep a record of all possible
meanings
cheniya to describe monetary units.
Listing 14.1

1 var russianCurrency: String = "Rouble"


This approach creates more problems than it solves,
since it does not exclude the influence of the "human factor", due to
which accidentally changing just one letter will lead
to the fact that the program will not be able to correctly process the received
value. And what to do if you need to add new processing
currency value?
An alternative to this method is to create an array (Lis-
Thing 14.2). The array contains all possible values that are
feet in the program. If necessary, you receive
the required array element.
Listing 14.2
1 var currencyUnit: [String] = ["Ruble", "Dollar", "Euro"]
2 var russianCurrency = currencyUnit [0]
This is actually a very good way to maintain lists of
possible values. And its positive properties end
exactly where they start at the enumerations.
In order to maintain additional supporting information
for array elements (for example, country, available bills, and
net of a certain merit), it will be necessary to create many
additional arrays and dictionaries. The ideal would be to have ot-
a useful data type that would allow you to describe the monetary unit
tsu, but, unfortunately, Apple experts did not provide it in Swift.
The situation can be significantly improved by using enumerated
leans instead of arrays or fundamental data types.
An enumeration is a collection of values of a specific data type that
allowing you to interact with these meanings. So, with the help
enumerations, you can create a set of available values and one of
values to assign to some parameter.
SYNTAX

enum EnumerationName {
case Value1
case Value2
...
}

An enumeration is declared using the enum keyword followed by


the name of the enumeration. The name should define the purpose of the created re-
numbers and, like the name of any data type, match the top style
camelcase.
The body of the enumeration is enclosed in curly braces and contains a list of access
values. These values are called enumeration members. Each member
defined using the case keyword followed by no quotes
the value itself is indicated. These values must start with an uppercase letter.
Their number in the enumeration can be arbitrary.

NOTE By declaring an enumeration, you are creating a new data type.


Let's solve the problem of specifying the type of currency using
transfers (Listing 14.3). An enumeration is like an array. It
contains a list of values, one of which we can assign
some parameter.
Listing 14.3
1 enum CurrencyUnit {
2
case Ruble
3
case dollar
4
case Euro
5}
Multiple enumeration members can be written on one line via
comma (Listing 14.4).
Listing 14.4
1 enum CurrencyUnit {
2
case Ruble, Dollar, Euro
3}

Although the CurrencyUnit enumeration is created and its members


are defined, no value is assigned to any parameter
rub. In order to initialize some parameter, some
The next member of the enumeration uses a special syntax.

SYNTAX

Access to enumeration values is the same as accessing properties of instances.


plyar, that is, through the dot symbol. There are two to initialize the value
way.
var ParameterName = EnumerationName.Value
The initialized value is written after the enumeration name, separated from
point to him.
let ParameterName: EnumerationName
ParameterName = .Value
The enumeration name acts as the data type of the parameter. After that, before-
stupas to meanings occur without specifying his name.
Let's create several parameters that will contain values
the enumeration created earlier (Listing 14.5).
Listing 14.5
1 // method 1
2 let roubleCurrency = CurrencyUnit.Rouble
Ruble
3 // method 2
4 let dollarCurrency: CurrencyUnit
5 dollarCurrency = .Dollar
Dollar
As a result, two constants of type CurrencyUnit are created , each of
which contains a specific member of the enumeration
change CurrencyUnit .

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.

14 .2. Associated parameters

Each of the enumeration members can have an associated


with it meanings, that is, its characteristics. They are indicated for
each term is exactly the same as the incoming function arguments, that is
in parentheses giving names and types, separated by colons.
The set of associated parameters can be arbitrary for
each individual member.
Let's create associated parameters for the Currency Unit enumeration ,
adding parameters that allow you to describe the countries in which
The currency is given with the short name of this currency (Listing 14.6).
Listing 14.6

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}

Parameter countrys is an array, so as currency can The use


be used not in one, but in several countries: for example, the euro is used
is found throughout the Eurozone.
Now, in order to create a variable or constant of type Cur-
rencyUnit , you must provide values for all associated
parameters (Listing 14.7).

Listing 14.7

1 var roubleCurrency: CurrencyUnit


2 roubleCurrency = .Rouble (сountrys:
["Russia"], shortName: "RUB")
Ruble (["Russia"], "RUB")
The rubleCurrency variable now storesthe Rouble member with the value
the two associated parameters. When describing the associated
naming parameters in the enumeration, their names are optional.
If necessary, only their types can be specified.
In order to expand the possibilities of enumeration, we turn
to his member Dollar . As you know, the dollar is the national currency
fiercely a large number of countries: US dollar, Australian dollar
lar, Canadian dollar, etc. Let's create a new transfer that
contains this list of countries. We will also create a new associated
parameter for the Dollar member , which will tell you the currency
which country the dollar is in (Listing 14.8).
Listing 14.8

1 // countries using the dollar


2 enum DollarCountrys {
3
case USA
4
case Canada
five
case Australia
6}
7 // data type "currency"
8 enum CurrencyUnit {
nine
case Rouble (сountrys: [String], shortName: String)
ten
case Dollar (сountrys: [String], shortName: String, national:
DollarCountrys)
eleven
case Euro (сountrys: [String], shortName: String)
12 }
13 var dollarCurrency: CurrencyUnit
14 dollarCurrency = .Dollar (сountrys: ["USA"], Dollar (["USA"], "USD",
shortName: "USD", national: .USA)
DollarCountrys.USA)
For the national parameter of the CurrencyUnit enumeration , the type is
DollarCountrys data . When this parameter is specified, its type is already
is known, therefore its name can be omitted when specifying a value.
Since the DollarCountrys listing is used exclusively
in the CurrencyUnit enumeration and created for it, it can be transferred
inside this enumeration (Listing 14.9).
Listing 14.9
1 // data type "currency"
2 enum CurrencyUnit {
3
// countries using the dollar
4
enum DollarCountrys {
five
case USA
6
case Canada
7
case Australia
8
}

nine
case Rouble (сountrys: [String], shortName: String)
ten
case Dollar (сountrys: [String], shortName: String, national:
DollarCountrys)
eleven
case Euro (сountrys: [String], shortName: String)
12 }

Now transfer DollarCountrys has limited area


visibility and is only accessible through the parent enum. Can-
but to say that it is a subtype of a type, or a nested type. Nevertheless, when
if necessary, you can create a parameter containing the value
this enumeration and outside the CurrencyUnit enumeration (Listing 14-10).
Listing 14.10
1 var someVar: CurrencyUnit.DollarCountrys
2 someVar = .Australia
Australia
Since the DollarCountrys enumeration is within the enumeration
When using CurrencyUnit , you need to refer to it as a property
of this type, that is, through a point.
Once again, I will note how easy-to-use the Swift language is.
After moving the DollarCountrys enum to CurrencyUnit
the code continues to run and Xcode gives the correct window hints
autocompletion.

14 .3. Switch statement for enumerations

We have learned to set as values of variables and con-


stant enumeration members and their associated parameters. For their
The switch statement is intended for analysis and parsing .
Consider the example from Listing 14.11, in which we need to
parse a variable containing an enum member as
value, and display information from the associated
parameters.
Listing 14.11
1 // create variable
2 var someCurrency = CurrencyUnit.Rouble (сountrys: ["Russia",
"Ukrain", "Belarus"], shortName: "RUB")
3 // analysis of the variable
4 switch someCurrency {
five
case .Rouble (let countrys, let shortname):
6
print ("Ruble. Countries: \ (String (countrys)), short
name: \ (shortname) ")
7
case let .Euro (countrys, shortname):
8
print ("Euro. Countries: \ (String (countrys)), short
name: \ (shortname) ")
nine
case .Dollar (let countrys, let shortname, let national):
ten
print ("Dollar \ (national). Countries: \ (String (countrys)),
short name: \ (shortname) ")
11 }

Console :

Ruble. Countries: ["Russia", "Ukrain", "Belarus"], short name: RUB


The switch statement describes each element of the Currency-
Unit ,so using the default statement is optional. Access
to associated parameters is implemented by binding values.
Since for all parameters a constant is created with a bindable
value, the let statement can be placed immediately after the keyword
case (demonstrated for a Euro member ).

14 .4. Related values

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.

Specifying related values


To set the initial values of the enumeration members, you must
specify the data type of the enumeration itself corresponding to the value
member pits (Listing 14.12).
Listing 14.12
1 enum Smile: String {
2
case Joy = ":)"
3
case Laugh = ": D"
4
case Sorrow = ":("
five
case Suprise = "o_O"
6}

The Smiles enumeration contains a set of emoticons. As a link


the given values of the members of this enumeration are specified values of
the type
String .
Associated values and associated parameters are not the same thing.
Initial values are set when the enumeration is defined,
and is mandatory for all its members and in the same data type.
The associated parameters can be different for each re-
numbers and are set only when an instance of this
transfers.
ATTENTION Simultaneous determination of initial values and associated
parameters are prohibited.
If you specify Int as the data type of the enumeration , then the initial
values are created automatically by increasing the value by
one for each subsequent term (the value of the first term
is 0). However, you can of course specify these values yourself
worthwhile. For example, Listing 14.13 shows an enumeration,
containing a list of planets in the solar system in order of distance
from the sun.
Listing 14.13
1 enum Planet: Int {
2
case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus,
Neptune, Pluton = 999
3}

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.

The Pluton member is originally listed, so it is 999.

Accessing related values

When instantiating an enumeration, you can access the


the default member value of this enumeration instance. For this
the rawValue property is used . Let's create an instance of the created work
enumeration of it Smile and get the initial value of the set
in this member instance (Listing 14.14).

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 .

Initialization via bound value

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

1 var myPlanet = Planet.init (rawValue: 3)


Earth
2 var anotherPlanet = Planet.init (rawValue: 11)
Nil

The initializer is the init () method of the Planet enumeration . He re-


a pointer to the original value associated with the search term is given
this listing. Don't be surprised that init () method is not described
within the enumeration - it always exists there by default.
The init () initializer returns an optional, so if you specify-
those non-existent associated value will return nil .
NOTE Initializers are called every time a new
th instance of any enumeration, structure, or class. For some
designs, they can and should be created independently, and for some, like
enums, they exist by default.
The initializer carries out the initialization process, that is, it performs the installation
all required values for parameters with direct instantiation
play and placing it in storage.
An initializer is always a method named init (). But even if you don't call
it directly, it still works.
We will learn more about initializers in the next chapters of the book.

14 .5. Properties in enumerations

Using enumerations, you can simulate a situation in which


there are a limited number of outcomes. In such situations,
past possible results (enumeration members) there may be
some properties.
Properties allow you to store auxiliary information in the enumeration
formation. We have already repeatedly met with properties in
learning Swift. A property in an enum is a storage similar to a variable
or a constant declared within the enumeration and available through his copy.
There is a certain limitation in Swift for properties in enumerations: their
values cannot be Pass fixed literal values, but only closures. Such properties
are called computed . Every time you contact them evaluating the assigned
closure with a return
the resulting value. For a computed property, after the name, separated by a
colon, indicates -
the return type and further without the assignment operator
in curly braces - the body of the closing expression that generates
return value. Let's declare a computed property for the previously developed
enumeration
change (Listing 14.16). We will take the enumeration Smile and co-
let's create a computed enumeration that returns the associated
with the current enum member value.
Listing 14.16
1 enum Smile: String {
2
case Joy = ":)"
3
case Laugh = ": D"
4
case Sorrow = ":("
five
case Suprise = "o_O"
6
var description: String {return self.rawValue}
7}
8 var mySmile: Smile = .Sorrow
9 mySmile.description
The computed property must be declared as a variable ( var ).
Otherwise (if you use a let statement ), you get the same
communication about the error.

14 .6. Methods in enumerations

We've seen the init () initializer method earlier . is he


suggests that enumerations can group in themselves not only

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

1 enum Smile: String {


2
case joy = ":)"
3
case laugh = ": D"
4
case sorrow = ":("
five
case suprise = "o_O"
6
// method for displaying description
7
func description () {
8
print ("The enumeration contains a list of used emoticons:
their names and graphic symbols ")
nine
}
10 }
11 var mySmile = Smile.joy
12 mySmile.description ()

Console :

The enumeration contains a list of used emoticons: their names


and graphic symbols
This enumeration declares the description () method. After creation
an instance of the method and placing it in a variable method descrip-
tion () can be called.

14 .7. Self operator

Methods can not only perform any non-trans-


enumerate things, but also handle its members and associated values.
To access the value of an instance within the enum itself, use
uses the self operator , which returns a pointer to the given
an instance of the enumeration.
Let's take an example. Suppose we need to write two methods, one
of which the enum member itself will return, and the second will return it
associated value. For this we use the enumeration Smile (whether
sting 14.18).
Listing 14.18
1 enum Smile: String {
2
case joy = ":)"
3
case laugh = ": D"
4
case sorrow = ":("
five
case suprise = "o_O"
6
func description () {
7
print ("The enumeration contains a list of used smilies:
their names and graphic symbols ")
8
}
nine
func descriptionValue () -> Smile {
ten
return self
eleven
}
12
func descriptionRawValue () -> String {
thirteen
return self.rawValue
fourteen
}
15 }
16 var mySmile = Smile.joy
joy
17 mySmile.descriptionValue ()
joy
18 mySmile.descriptionRawValue ()
":)"
When the descriptionValue () method is called , self is returned ,
that is, the instance itself. That is why the return type is
of this method - Smile , it corresponds to the type of the instance
transfers.
The descriptionRawValue () method returns the associated member value
of this instance also using self .
You can even parse the enum if needed
inside the enumeration itself using the switch self {} construction ,
where the values are members of the enumeration.
The self operator can be used not only for enums, but
and for structures and classes. We'll talk about this later.

14 .8. Recursive enums


Enumerations do a great job of modeling situations,
when there are only a few options for the development of the situation. But you can-
you can use them not only to store some
associated and associated values. You can go further
and endow the enumeration with the functionality of the analysis of its own
analysis and calculations based on it some expressions.

Take, for example, the simplest arithmetic operations:


nation, subtraction, multiplication and division. They are all known in
advance,
therefore can be placed in an enumeration as its members
(Listing 14.19). For simplicity, consider only two operations: the word
zening and subtraction.

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)

This enumeration has two members in accordance with arithmetic


ical expressions. The sign of each of the arithmetic operations
in Swift, it is a binary operator, that is, an operator that conducts an
walkie-talkie with two operands. In this regard, each of the operations can
take two values for associated parameters.
As a result, a member of the Arithme enumeration is stored in the expr variable .
ticExpression , which defines an arithmetic addition operation.
The declared enumeration does not carry any functional
loads in your application. But you can create within it
method that defines the name of the member and returns the result
That operation is required (Listing 14.20).
Listing 14.20

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

When the evaluate () method is called , a search for a specific


in a given instance of an enumeration member. For this are used
operators switch and the self . Further, after the term is determined, by
value binding returns the result of the given arithmetic
operations.
This method works just fine, but it has a serious
limitation: it is able to simulate only single-level arith-
metical expressions: 1 + 5, 6 + 19, etc. In a situation where the expression
has nested expressions: 1 + (5 - 7), 6 - 5 + 4, etc., we have to
calculate each individual action using its own
instance.

To solve this problem, it is necessary to modify the enumeration


ArithmeticExpression in such a way that it enables
add not only values of type Int , but also other expressions. By-
it turns out that the enumeration describing the expression should give
the ability to perform operations with itself. This mechanism re-
is aliased in a recursive enumeration . To create this type of re-
number, the indirect keyword is used , which is set:
❑ either before the enum operator - in this case, each member is
numbers can refer to this listing;
❑ either before the case statement of the member in which you need to
start listing.

Consider the example from Listing 14.21. If as an associate


enumeration parameters specify values of the type itself
enumeration, then the question arises: where to store the numbers over which
are the operations being performed? Such numbers also need to be stored
in the listing itself, in its individual member. In this example, you
the value of the expression 20 + 10 - 34 is calculated.
Listing 14.21

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

Add the ArithmeticExpression enumeration as follows:


so that it can implement any expression using
operations of addition, subtraction, multiplication, division
and exponentiation.
15 Structures
Enumerations are the entry ticket to developing awesome pri-
positions. Now it's time to get acquainted with even more functional
nal and interesting constructions - structures.
Don't be surprised, but you've been using structs for a long time.
writing your code. True, all the structures used were
implemented by other developers. All fundamental types
data - arrays, collections - are structures. Structures
are somewhat similar to enumerations and are similar in many ways
with the classes (we will also get to know them shortly).

15 .1. Syntax for declaring structures


Let's imagine a problem in which it is necessary to describe a chess player.
It is required to implement functionality that allows for various
let create various storages (variables) and work with them
independently of each other. At the same time, a convenient
access to data in these storages.
To solve this problem, you can use tuples and store
in the variable the name and number of wins of the player (Listing 15.1).
Access
values will be accessed through the elements of the tuple.
Listing 15.1

1 var playerInChess = (name: "Vasily", wins: 10)


This method, of course, solves the task, but if
tries to take into account a large number of heterogeneous characteristics,
then the tuples will become overly complex. And how to remember which
characteristics and how they are called, I just can not imagine.

Accordingly, we need a mechanism that serves as a kind of contract


rum, or a set of rules, which will describe all possible
parameters (their names and data types).
Structures are precisely this mechanism that allows
create a "skeleton" describing an entity. For example, structure
tour Int describes the entity "integer".
SYNTAX
struct StructureName {
// properties and methods of the structure
}

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.

NOTE By declaring a structure, you are defining a new data type.


Let's declare a structure that will describe the entity "chess player
mats ”(Listing 15.2).

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.

15 .2. Properties in structures

Declaring properties

The currently created PlayerInChess structure is empty, i.e.


it does not describe any characteristics of the player. Let's add two
properties according to the tuple we created earlier (Listing 15.3).
NOTE A property is a characteristic of an entity that is described by
structure. It can be a variable or constant and is declared in the body of the structure.
tours:

struct StructureName {
var property1: DataType
let property2: DataType
// ...
}

The number of properties in the structure is unlimited.


While learning Swift, we have already encountered properties many times.

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

1 var oleg = PlayerInChess (name: "Oleg", wins: 32)


As a result, a new instance of the PlayerInChess structure is created ,
possessing the values of all its properties.
ATTENTION Property values must be specified. Skipped
any of them is unacceptable! If the value of any of the properties will not
specified, Xcode will report an error.

Default property values

You can set a default value for properties. Moreover, Swift


automatically creates a new initializer that allows co-
create an instance without specifying property values. You will be able to see
the given initializer in the auto-completion window at creation time
instance (fig.15.1).
The figure shows two different initializers available with
instantiation: one does not require you to specify property values,
because it uses their default values, the other, on the contrary,

requires these values to be specified. An initializer that doesn't require


to specify any values is called an empty initializer .
Figure: 15.1. Two initializers in the auto-completion window
Default values are specified along with property declarations
just like you specify the value of any variable or con-
stants. Moreover, if you decide to give a default value though
to one property, then you must specify it for all the others
properties. Swift doesn't allow defining default values only
for some properties.
Let's declare the default values for the PlayerInChess structure ( lis-
Thing 15.5).

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

In both cases, an instance of the PlayerInChess structure is created . If


to create an instance, an empty initializer is selected, the parameter
tram name and wins are assigned their default values.

15 .3. Structure as a namespace

As noted earlier, the structure forms a separate space


names. Therefore, to access the elements of this namespace do not
you must first of all get access to the space itself.
In the previous example, a PlayerInChess structure was created with two
properties. Each of the properties has some meaning, but they do not
it will be of no use if you do not describe the data access mechanisms
properties.

Elements of structures are accessed using instances


this structure (Listing 15.6).

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.

15 .4. Custom initializers

An initializer is a special method called init () .


If you are not satisfied with the initializers that are created for
structures automatically, you have the opportunity to develop your own
natural.
ATTENTION Automatically generated initializers are removed when declaring
the first native initializer.
You need to constantly adhere to the rule: “all properties
structures have to matter. " You can create initialization-
a torus that takes as an input parameter values not
for all properties, then the rest of the properties should be assigned
values either within a given initializer or via values
default. Initializers are declared without using
the func keyword . In this case, one structure can contain pro
an arbitrary number of initializers, each of which must
have a unique set of input parameters.
Let's create an initializer for the PlayerInChess structure , which will
only takes on the name property (Listing 15.7).
Listing 15.7
1 struct PlayerInChess {
2
var name: String
3
var wins: UInt = 0
4
init (name: String) {
five
self.name = name
6
}
7}
8 var oleg = PlayerInChess (name: "Oleg")

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 .

The wins property has a default value, it is assigned given


property automatically.

Remember that creating your own initializers for structs is not


mandatory, since structs have built-in initializers.
NOTE If the instance of the structure is stored in a constant,
modification of its properties is impossible. If it is stored in a variable, then the
you can modify those properties that are declared using the var statement.
ATTENTION Structures are value types. When passing an instance, the structure
from one parameter to another, it is copied. In the following case
At least two independent instances of the same structure are created:
var olegMuhin = PlayerInChess (name: "asd")
var olegLapin = olegMuhin
15 .5. Methods in Structures

Method declaration

In addition to properties, structures can contain methods. Syntax


method declarations in structures are similar to method declarations
in enumerations. They, like ordinary functions, can take
input parameters. To access the structure's own properties
operator self is used .
Let's write a method that displays help information about the player
chess to the console (Listing 15.8).
Listing 15.8
1 struct PlayerInChess {
2
var name: String
3
var wins: UInt
4
func description () {
five
print ("Player \ (self.name) has \ (self.wins) wins")
6
}
7}
8 var oleg = PlayerInChess (name: "Oleg", wins: 15)
9 oleg.description ()

Console :

Player Oleg has 15 wins

Modifying methods

By default, struct methods, other than initializers, cannot be


change the values of properties declared in the same structures. For
in order to bypass this limitation, before the name of the declared
method, you must specify the mutating modifier .
Let's create a method that has no restrictions on changing properties
an instance of the structure. For example, if the need arises from-
to change the number of wins of a chess player, you can create a method
implementing this functionality (Listing 15.9).
Listing 15.9

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

ATTENTION The structure can change property values only if


if the structure instance is stored in a variable.
16 Classes
Classes are the most functional constructs in terms of
application development. This chapter is intended to introduce you to these
wonderful designs. If you have previously developed a
in other programming languages, then you may already
aware of the classes. In this case, this experience will be useful to you
when learning them in Swift.
Classes are similar to structures, but they are distinguished by several key
moments:
❑ Type . A class is a reference type. Instances of the class are passed by
link, not copying.
❑ Variability . An instance of a class can change the values of its
properties declared as a variable ( var ), even if the given
the instance is stored in a constant ( let ). In this case, use
the mutating keyword to remove the change restriction
property value methods are not required.
❑ Inheritance . Two classes can be in a superclass relationship—
subclass to each other. Thus, unlike sets ( sets ), where
the superset includes all elements of the subset, here the situation is
military: subclass inherits and includes all characteristics
(properties and methods) of the superclass and can be additionally distributed
wide. Restrictions related to inheritance discussed
in the corresponding chapter.
❑ Initializers . The class only has an empty inline initial-
liser init () {} , which does not require passing the value of the input
parameters to set them to properties.
❑ Deinitializers . Swift allows you to create a deinitializer -
a special method that is automatically called when the
leasing an instance of a class.
❑ Type casting . While the program is running, you can
check an instance of a class for compliance with a specific type
data.
Each of the features is detailed in the book.
When modeling any entity, you need to learn
correctly choose the design required for its implementation: re-
number or collection, structure, class or just a tuple. Than
more time you will spend on the implementation of various ideas,
the more experience you gain.

16 .1. Class syntax


A class declaration is very similar to declaring a structure and re-
numbers. An exception is the keyword used.
SYNTAX
class ClassName {
// class properties and methods
}
Classes are declared using the class keyword followed by a name
the class being created. The class name must be written in the top camelcase style.
The class body is enclosed in curly braces and can contain methods, properties
and other elements that we are not familiar with yet.
NOTE At the moment of declaring a new class, a new one is created in the program
data type.

16 .2. Class properties


Let's move on to the practical side of learning classes. Class like
and structure, can provide mechanisms for describing some
swarm of essence. This entity usually has a number of characteristics,
expressed in the class as class properties . Properties can take
the mother of the value according to a specific class of data types.
They can also have default values specified.
As noted earlier, the class has one built-in initializer,
which is empty. If the structure has an initializer
is automatically calculated together with a change in the composition of its
properties, then
the class for setting property values needs to be developed
the initializers themselves. When instantiating a class, each
a property must have a certain value, and any property
for which no default value is specified should get
value using an initializer you designed.
Let's create a class designed to describe the entity "shah-
matte figure ". Using the class for modeling
a chess piece is preferable primarily due to the fact that
that each individual figure is a unique object with its own
characteristics. Its modification in the program using
references (and a class is a reference type) will make things much easier.
For a chess piece, the following set of characteristics can be distinguished
stick:
❑ body type;
❑ shape color;
❑ coordinates on the playing field.
Additionally, we will save the symbol corresponding to the check
matte figure. There is a set of characters in Unicode, each of which
which depicts a single figure of a certain color. You can
find them on the Internet yourself.
Coordinates will serve not only to determine the location
placing a piece on a chessboard, but in general for determining
the fact of her presence. If a piece is killed or has not yet been spotted, then
the coordinate value must be absent ( nil ).
Don't forget: since properties exist, initialization is required
torus (Listing 16.1).

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: " ♚ ")

Each of the characteristics of the figure is expressed in a separate property


class. The coordinate property data type is optional
a tuple. This is due to the fact that a piece can be removed from the playing
fields. The coordinates of the shape are specified using a string and a number.
The developed initializer specifies the input arguments, the value
which are used as the values of the properties of the instance.
As a result, we got an instance of the class describing the shape
"White King". The figure does not yet have coordinates, which means that it
has not
Lena on the playing field (its coordinates are nil ).
For each of the type and color properties , a list of options can be created.
possible values. In this regard, it is necessary to determine two
numbers: one for describing the types of chess pieces, another for
descriptions of their colors (Listing 16.2). The created transfers should
we find a place as the types of the corresponding properties of the class
Chessman . Don't forget that the input arguments of the initializer
should change accordingly.
Listing 16.2
1 // types of figures
2 enum ChessmanType {
3
case King
4
case Castle
five
case Bishop
6
case pawn
7
case Knight
8
case Queen
9}
10 // shape colors
11 enum CheesmanColor {
12
case Black
thirteen
case White
14 }
15 class Chessman {
sixteen
let type: ChessmanType
17
let color: CheesmanColor
eighteen
var coordinates: (String, Int)? = nil
19
let figureSymbol: Character
20
init (type: ChessmanType, color: CheesmanColor, figure:
Character) {
21
self.type = type
22
self.color = color
23
self.figureSymbol = figure
24
}
25 }
26 var kingWhite = Chessman (type: .King, color: .White, figure: " ♔ ")

Now, when creating a model of a chess piece, you need to re-


give values of types ChessmanType and CheesmanColor instead of String .
Created additional links ensure correct input
data when instantiating the class.

16 .3. Class methods

The essence of "Chess Piece" is quite working. On her basis


any shape can be described. However, the described figure
is still "dead" in the sense that it cannot be used
used directly for the game. This is due to the fact that not yet
mechanisms have been developed to set the figure on the playing
field and move it dynamically.
Classes, like enumerated structures, can contain
free methods that provide the functional load of the class.
Remember that you do not need to use a key in classes
a word mutating for methods that change property values.
Let's revive the created model of a chess piece a little by creating a non-
how many methods (Listing 16.3). The Cheesman class requires re-
List the following functions:
❑ Setting the coordinates of the figure (when placing on the field or when
movement).
❑ Removing a piece from the playing field (end of the game or death
figures).
Listing 16.3
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
// method for setting coordinates
12
func setCoordinates (char c: String, num n: Int) {
thirteen
self.coordinates = (c, n)
fourteen
}
fifteen
// method that kills the figure
sixteen
func kill () {
17
self.coordinates = nil
eighteen
}
19 }
20 var kingWhite = Chessman (type: .King, color: .White, figure: " ♔ ")
21 kingWhite.setCoordinates (char: "E", num: 1)
As a result, the "White King" figure is created and displayed in the
position with coordinates E1.
In fact, for the actual placement of the figure on the game
the field needs to simulate the chessboard itself. This question
we will deal with catfish shortly.

16 .4. Class initializers


A class can contain an arbitrary number of developed
initializers that differ only in the set of input arguments.
This does not in any way affect the work of the class itself, but only gives
we have more opportunities when creating instances.
Let's look at the process of creating an additional initializer. Su-
the existing Chessman class does not allow one expression to create
piece and place it on the field. Now two are used for this.
independent expressions. Let's develop a second initialization-
a torus that will additionally take the coordinates of the figure
(Listing 16.4).

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.

16 .5. Nested types


Very often enumerations, structures and classes are created in order to
to support the functionality of a particular data type- them. We saw this
approach when we developed enums CheesmanColor and ChessmanType used in the
Chessman class .
In this case, the enumerations are needed exclusively in the context
class describing a chess piece, and nowhere else. In such a situation, you can
nest enums in a class, i.e. describe them not globally, but within the class
body (Listing 16.5).
Listing 16.5

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 .

References to nested types


In some situations, it may be necessary to use Call nested types outside of
their defining context. For this you must specify the name of the parent type,
after which you must follow the name of the required data type (Listing
16.6). In this example we access one of the members of the ChessmanType
enumeration ,
declared in the context of the Chessman class .
Listing 16.6
1 var linkToEnumType = Chessman.ChessmanType.Bishop
17 Properties
In the course of studying Swift, we have already repeatedly encountered the
property- mi instances of different data types. However, to explore
everything
the possibilities of properties, you need to dive deeper into this topic.

17 .1. Property types

Properties are repositories declared within an object type


data. They allow you to store and calculate values, as well as semi-
to access these values.
By the type of stored value, there are two main types
properties:
❑ Stored properties can be used in structures and classes.
❑ Computed properties can be used in enumerations,
structures and classes.

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.

Lazy stored properties

Stored properties can be "lazy". The value that should


but stored in a lazy property, not created until the first
referring to him.

SYNTAX

lazy var PropertyName1


lazy let PropertyName2
The lazy modifier is added before the store declaration statement, indicating-
lazyness of a certain property.
Let's look at an example. Let's create a class that allows you to get
some information about a person, namely name and surname (fox-
Thing 17.1).
Listing 17.1
1 class AboutMan {
2
let firstName = "Egor"
3
let secondName = "Petrov"
4
lazy var wholeName: String = self.generateWholeName ()
five
func generateWholeName () -> String {
6
return self.firstName + "" + self.secondName
7
}
8}
9 var Me = AboutMan ()
10 Me.wholeName
Here, an instance of the AboutMan class describes a person. In your-
stve wholeName be stored his full name, but when you create
no instance of the value of this property exists. However, it does not
is nil , it just isn't generated or written. It's connected
with the fact that the property is lazy. As soon as the
to this property, its value is formed.
Lazy properties allow you to save RAM and not
spend it until the value of some property is
required.
NOTE It is worth noting that as values for stored properties
methods of the same object type cannot be specified. Lazy properties don't have
this restriction, since their values are formed after the creation of an instance
lyarov.

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

var PropertyName1: ValueType {Closing_Expression_Body}


Computed properties can only be stored in variables (var). After
specifying the name of the property being declared and the return type of the closure expression
by adding a value without an assignment operator, a closure is indicated, as a result
those of which the property's return value should be generated.
In order for the property to return some value, the body of the closure must
The return statement must be present.
Let's rework the example from the previous listing using laziness
first computed property. Since the wholeName () method in the AboutMan class
serves solely to generate the value of the wholeName property ,
it makes sense to transfer this method as a closure expression
to the property. The result is a computed property (Listing 17.2).
Listing 17.2

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.

17 .2. Receiving control


and setting values

Computed property getter and setter

For any computed property, it is possible to implement


call two special functions:
❑ Getter ( get ) executes some code when requesting the value of you-
numerical property.
❑ The setter ( set ) executes some code when trying to install
computed property values.
All previously declared computed properties have implemented
getter only, so they were read-only properties
that is, attempting to change would cause an error. Thus, one can
to assert that a getter is required for the existence of a
numeric property, and the setter implementation can be omitted.

SYNTAX

var PropertyName1: ValueType {


get {
getter_body
}
set (passed_parameter) {
setter_body
}
}

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 }

13 var myNewCircle = Circle (coordinates: (0,0), radius: 10)


14 myNewCircle.perimeter // prints 62.8
15 myNewCircle.perimeter = 31.4
16 myNewCircle.radius // prints 5
When the perimeter property value is requested , the
execution
getter that generates a return value given the property
ARISING RADIUS . When setting the value of the perimeter property is triggered
a setter that computes and sets the value of the radius property .
The setter also allows you to use a shorthand syntax,
which does not specify the name of the input parameter. At the same time
inside
setter to access the set value, you must
to act on an automatically declared parameter named newValue .
Thus, the Circle class might look like Listing 17.4.
Listing 17.4
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 {
nine
self.radius = newValue / (2 * 3.14)
ten
}
eleven
}
12 }

Stored property observers


Getter and Setter let you execute code while installing and reading
computed property values. In other words, you have
mechanisms for controlling the process of changing and reading values.
Endowing
such useful mechanisms are computed properties, developers
Swift couldn't get around stored properties either. Especially for
these were implemented by observers.
Observers are special functions that are called either
immediately before or immediately after setting a new value
stored property.
There are two types of observers:
❑ The willSet observer is called before setting a new value.
cheniya.
❑ The didSet observer is called after setting a new value.
SYNTAX
var PropertyName1: ValueType {
willSet (parameter) {
getter_body
}
didSet (parameter) {
setter_body
}
}
Observers are declared using the keywords willSet and didSet, after which
The name of the input argument is indicated in parentheses. The observer willSet at the given
the argument is written to the value to be set, to the didSet observer - the old one,
already erased.
When declaring observers, you can use the shorthand
syntax that does not require input arguments
(just like the shorthand setter syntax). At the same time, no-
the new value in willSet is assigned to the newValue parameter , and the old
in didSet - the oldValue parameter .
Let's consider the use of observers using an example. Into the structure,
circumscribing a circle, we add a functional that, when
the radius of the circle displays the corresponding information
to the console (Listing 17.5).

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.

17 .3. Type properties


Earlier, we looked at properties that allow each
a separate copy to keep its own, independent of other copies
moat set of values. In other words, we can say that the properties
instance describe the characteristics of a specific instance
and belong to a specific instance.
In addition to properties of instances, you can declare your-
properties related to the data type. The values of these properties are the same for
all instances of the type.
Data type properties are very useful when there are
values that are universal for the entire type.
They can be either stored or calculated. Moreover, if
the value of the stored type property is variable and changes

in one copy, then the changed value becomes available in


all other instances of the type.
ATTENTION For stored type properties, it is mandatory to have
default values are specified. This is because the type itself does not have
an initializer that could have been triggered even during type definition and
update the required values for the properties.
Stored type properties are always lazy, nor do they need
in using the lazy keyword.
Type properties can be created for enums, structures, and classes
owls.
SYNTAX
struct SomeStructure {
static var storedTypeProperty = "Some value"
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumiration {
static var storedTypeProperty = "Some value"
static var computedTypeProperty: Int {
return 2
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 3
}
class var overrideableComputedTypeProperty: Int {
return 4
}
}

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.

18 .1. Assigning subscripts

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.

16 .2. Subscript syntax


In their implementation, subscripts are something in between
and computed properties. From the first they got the syntax
defining output parameters and return type,
from the second - the ability to create a getter and setter.

SYNTAX

subscript (input_parameters) -> return_value_type {


get {
// getter body
}
set (new_value) {
// setter body
}
}

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.

NOTE We encountered overloads when we declared several


functions with the same name or several initializers within one declaration
ect type. Each set of objects with the same name differed only in one
bore of input parameters.

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

values corresponding to numbers are written to this dictionary


rows on a chessboard. This is done in order to ensure error-free work when
placing a piece on a checkerboard.
Otherwise, when setting the shape, we would have to first find out the state of
the line (whether it exists in the dictionary), and only then write the figure to
specific coordinates.
The setChessman method does n't just set a reference to the shape in its
GUSTs desk , but also removes the old link. This is ensured by the fact that
the actual coordinates are always stored in the coordinates property of the ex-
an instance of the Chessman class .
The written class lacks the ability to request information
about an arbitrary cell. Let's implement it using a subscript
(Listing 18.2). The coordinates of the required
dim cell as two separate input arguments. If by
a figure exists at the specified coordinates, then it is returned,
otherwise, nil is returned .
Listing 18.2
1 class gameDesk {
2
var desk: [Int: [String: Chessman]] = [:]
3
init () {
4
for i in 1 ... 8 {
five
desk [i] = [:]
6
}
7
}
8
subscript (alpha: String, number: Int) -> Chessman? {
nine
get {
ten
if let chessman = self.desk [number]! [alpha] {
eleven
return chessman
12
}
thirteen
return nil
fourteen
}
fifteen
}
sixteen
func setChessman (chess: Chessman, coordinates: (String, Int)) {
17
if let oldCoordinates = chess.coordinates {
eighteen
desk [oldCoordinates.1]! [oldCoordinates.0] = nil
19
}
20
self.desk [coordinates.1]! [coordinates.0] = chess
21
chess.setCoordinates (char: coordinates.0, num:
coordinates.1)
22
}
23 }
24 var game = gameDesk ()
25 game.setChessman (QueenBlack, coordinates: ("A", 3))
26 game ["A", 3] ?. coordinates

The implemented subscript has only a getter, and in this


case, it was possible to use a short notation syntax (without
get keyword ).
Since the subscript returns an optional, before accessing the property
coordinates of the returned chess piece needs to be done
retrieving an optional value.
Now we have the ability to place pieces on a chessboard
using the setChessman () method and getting information about the individual
cell using a subscript.
We can extend the functionality of the subscript by adding a setter to it,
allowing you to set the figure to new coordinates (fox-
Thing 18.3).
Listing 18.3
1 class gameDesk {
2
var desk: [Int: [String: Chessman]] = [:]
3
init () {
4
for i in 1 ... 8 {
five
desk [i] = [:]
6
}
7
}
8
subscript (alpha: String, number: Int) -> Chessman? {
nine
get {
ten
if let chessman = self.desk [number]! [alpha] {
eleven
return chessman
12
}
thirteen
return nil
fourteen
}
fifteen
set {
sixteen
self.setChessman (newValue !, coordinates: (alpha,
number))
17
}
eighteen
}
19
func setChessman (chess: Chessman, coordinates: (String, Int)) {
20
if let oldCoordinates = chess.coordinates {
21
desk [oldCoordinates.1]! [oldCoordinates.0] = nil
22
}
23
self.desk [coordinates.1]! [coordinates.0] = chess
24
chess.setCoordinates (char: coordinates.0, num:
coordinates.1)
25
}
26 }
27 var game = gameDesk ()
28 game.setChessman (QueenBlack, coordinates: ("A", 3))
29 game ["C", 5] = QueenBlack
30 QueenBlack.coordinates

The datatype of the newValue variable in the subscript setter matches


the data type of the value returned by the subscript. For this reason-
it is not necessary to force retrieve the value before
place a piece on a checkerboard.
NOTE Remember that subscripts cannot be used as storage
shcha, that is, through them we organize only access to the value stores.
Subscripts really add a lot of fun to Swift. Co-
state that the entity "chessboard" is much more
more convenient through indexes than without them.

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).

19 .1. Inheritance syntax

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

class SomeSubClass: SomeSuperClass {


// subclass body
}
To create a derived class SomeSubClass for which the base is
SomeSuperClass, you must specify the superclass name separated by a colon after the name
subclass.
As a result, all properties and methods defined in the SomeSu class-
perClass becomeavailable in the SomeSubClass class without them being
a middle declaration in a derived type.
NOTE The values of inherited properties can change regardless of
values of the corresponding properties of the parent class.

Let's look at an example that creates a base Quadruped class


with a set of properties and methods (Listing 19.1). This class describes
It is the essence of "Four-legged animal". Additionally announced
Subclass Dog , describing the Dog entity. All characteristics
the Quadruped classes are applicable to the Dog class , so they can be
to follow.

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.

Accessing inherited characteristics


Accessing inherited elements of the parent class in production
n class is implemented in the same way as for its own elements of this
derived class, that is, using the self keyword .
As an example, add a method to the Dog class that prints to the console
the name of the dog. The nickname is stored in the name property , which is inherited
from the Quadruped class (Listing 19.2).

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.

19 .2. Overriding Inherited Elements


The subclass can create its own implementations of properties, methods
and subscripts inherited from the superclass. Such implementations are
are called overrides . To override the parameters su-
perclass in Swift, you must specify the override keyword before
element definition.

Overriding Methods

Quite often, the implementation of a method that "inherited"


from a superclass does not meet developer requirements. In such
case in the subclass, you need to rewrite this method, providing it with
access with the same name. Let's declare a new class NoisyDog , which
describes the entity Restless Dog. The Dog class is su-
perclass for NoisyDog . It is necessary to inject into the described class
own implementation of the bark () method , but since the same name
the method already exists in the parent Dog class , we will use
override mechanism (Listing 19.3).
Listing 19.3
1 class NoisyDog: Dog {
2
override func bark () {
3
print ("woof")
4
print ("woof")
five
print ("woof")
6
}
7}
8
9 var badDog = NoisyDog ()
10 badDog.bark () // prints woof woof woof
We use the override keyword to tell Swift that the method
bark () in the NoisyDog class has its own implementation.
NOTE A class can be a superclass regardless of whether it is
Whether he is a subclass or not.
The overridden method does not know the implementation details of the parent method.
telsky class. He only knows the name and the list of input parameters
parent method.

Accessing overridden superclass members


Although the override changes the implementation of the properties,
methods and subscripts, Swift allows access inside
derived class to the overridden members of the superclass. For
this in the name of the element instead of the prefix self use
the super keyword .
In the previous example, in the bark () method of the NoisyDog class ,
duplication of code. It uses the console output function
literal "woof" , although this functionality has already been implemented in one
named parent method. Let's rewrite the implementation of the bark () method
in such a way as to avoid code duplication (Listing 19.4).

Listing 19.4

1 class NoisyDog: Dog {


2
override func bark () {
3
for _ in 1 ... 3 {
4
super.bark ()
five
}
6
}
7}
8 var badDog = NoisyDog ()
9 badDog.bark () // Prints woof woof woof
The output to the console matches the output of the class implementation from the previous
example.

The overridden elements are accessed as follows -


the following rules:
❑ An overridden method named someMethod () can call
method of the superclass of the same name, using the super construction .
someMethod () inside its implementation (in the code overridden
method).
❑ Override Properties someProperty can access
to the superclass property with the same name using the construct
super.someProperty inside its getter or setter implementation.
❑ An overridden subscript someIndex can refer to a subscript
superclass script with the same index format using
super [someIndex] construct inside the subscript implementation.

Overriding initializers

Initializers are the same inherited elements as


as well as methods. If the subclass has a set of properties that require setting
values does not differ, then the inherited initializer can be
used to instantiate a subclass.
However, you can create your own implementation of the inherited
initializer. Remember that if you define an initializer
with a set of input arguments unique for the superclass and subclass
comrade, then you do not override the initializer, but declare a new one.
If the subclass has at least one native initializer, then
parent class initializers are not inherited.
WARNING For calling the superclass initializer inside the initializer
subclass, you must use the super .init () construct.
As an example, let's override the inherited from the superclass
Quadruped empty initializer. In the Dog class, the value inherits
My type property must always be "dog" . Concerning
Let's rewrite the implementation of the initializer so that in it
the value of this property was set (Listing 19.5).
Listing 19.5
1 class Dog: Quadruped {
2
override init () {
3
super.init ()
4
self.type = "dog"
five
}
6
func bark () {
7
print ("woof")
8
}
nine
func printName () {
ten
print (self.name)
eleven
}
12 }

Before accessing inherited properties in the overridden


lone initializer, you must call the parent initializer
la. It initializes all inherited properties.
If the subclass has its own properties that are not in the superclass-
ce, then their values in the initializer must be specified before calling
initializer of the parent class.

Overriding inherited properties


As noted earlier, you can override any inherited
elements. Inherited properties sometimes limit functionality.
subclass capabilities. In this case, you can rewrite
getter or setter of this property, or add if necessary
observer.
With the override mechanism, you can extend the inheritance
a read-only property before read-write by implementing
it contains both a getter and a setter. But the opposite is impossible: if the
inherited
properties are implemented and getter and setter, you cannot make from it
read-only property.
ATTENTION Stored properties cannot be overridden, because the called or
the inherited initializer of the parent class will try to set their value
reading, but will not find them.
The subclass does not know the implementation details of the inherited
property in the super-
class, it only knows the name and type of the inherited property. Therefore,
no-
you must always specify both the name and type of the property being
overridden.
19 .3. Final preventive modifier
Swift allows you to protect the implementation of a class as a whole or its
separate
elements. To do this, you must use preventive
final modifier , which is specified before the class declaration
or its individual elements:
❑ final class for classes;
❑ final var for properties;
❑ final func for methods;
❑ final subscript for subscripts.
While protecting the implementation of a class, its inheritance into other
classes
becomes impossible. For class elements, their inheritance
happens, but the override becomes unavailable.
19 .4. Replacing Class Instances

Inheritance, in addition to all the listed possibilities, allows


replace required instances of a specific class with instances
one of the subclasses.
Consider the example from Listing 19.6. In it we will declare an array of
elements
type Quadruped and add some elements to it.
Listing 19.6
1 var animalsArray: [Quadruped] = []
2 var someAnimal = Quadruped ()
3 var myDog = Dog ()
4 var dabDog = NoisyDog ()
5 animalsArray.append (someAnimal)
6 animalsArray.append (myDog)
7 animalsArray.append (badDog)
As a result , elements of Dog types are added to the animalsArray array .
and NoisyDog . This occurs despite the fact that as the type of mass
siva specified class is Quadruped .

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 .

By casting types, you can perform the following operations:


❑ check the type of a specific instance of a class for compliance with non-
which type or protocol;
❑ convert the type of a particular instance to another type of the same
class hierarchy.

Type checking

The type of an instance of a class is checked using the operator


Rathore is . This operator returns true if the type
the instance being checked is or inherits the specified after
operator class. For analysis, we take a certain and filled
previously the animalsArray array (Listing 19.7).

Listing 19.7

1 for item in animalsArray {


2
if item is Dog {
3
print ("Yap")
4
}
5}
6 // Yap is output 2 times

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.

20 .1. Alias Any


Thanks to the alias Any, you can create storages of undefined
local data type. Let's declare an array that can contain
elements of arbitrary types (Listing 20.1).
Listing 20.1
1 var things = [Any] ()
2 things.append (0)
3 things.append (0.0)
4 things.append (42)
5 things.append ("hello")
6 things.append ((3.0, 5.0))
7 things.append ({(name: String) -> String in "Hello, \ (name)"})
The things array contains values of types: Int , Double , String , (Double,
Double) and (String) -> String . That is, before you a whole set of different
data types.
When you request any of the array elements, you will get a value not
of the type of data that was assumed when installing a particular
values, but of type Any .

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

To analyze each element of the array, you must execute the


type maintenance. So you can get each element, transform
in its valid data type.
Consider an example in which we will analyze the previously declared mass
siv element by element and define the data type of each element (
Thing 20.2).

Listing 20.2

1 for thing in things {


2
switch thing {
3
case let someInt as Int:
4
print ("an integer value of \ (someInt)")
five
case let someDouble as Double where someDouble> 0:
6
print ("a positive double value of \ (someDouble)")
7
case let someString as String:
8
print ("a string value of \" \ (someString) \ "")
nine
case let (x, y) as (Double, Double):
ten
print ("an (x, y) point at \ (x), \ (y)")
eleven
case let stringConverter as String -> String:
12
print (stringConverter ("Troll"))
thirteen
default:
fourteen
print ("something else")
fifteen
}
16 }

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).

20 .2. Alias AnyObject

The AnyObject alias allows you to indicate what is in a given location


must or can be an instance of any class. In this case, you
you will come across arrays of this type quite often during development
programs using the Cocoa Foundation framework. The
the framework is written in Objective-C, and this language does not have
arrays
with explicitly specified data types.
Let's declare an array of instances using the AnyObject alias ( Lis-
Thing 20.3).
Listing 20.3

1 let someObjects: [AnyObject] = [Dog (), NoisyDog (), Dog ()]


When using the AnyObject alias, there is no restriction
on the use of
naming classes from only one hierarchical structure. In this
example, if you retrieve an arbitrary element of an array, then you get
you think of an instance of the AnyObject class that does not have properties
and methods for
interaction with him.

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

with a different set of parameters within one object


type. In this case, there must be at least one designated initial
an initializer that sets the values of all properties (if
they exist) and one of the designated initializers must
must be called when instantiated. Appointed ini-
an initializer cannot call another designated initializer,
that is, using the self.init () construct is prohibited.
NOTE Initializers inherit from superclass to subclass.
The only initializer that the designated one can call
an initializer is a derived class initializer that calls
parent initializer for setting values
inherited properties. We talked about this in some detail when
studied inheritance.
NOTE The initializer can set constant values.
Property values must be set inside the initializer
class or structure, so that by the end of its work all properties have
values (options can be nil ).

Convenient initializers

In addition to those assigned in Swift, there are auxiliary (conveni-


ence) initializers. They are secondary and support
with them. You can define a convenience initializer for
making settings and making a mandatory call to one of the designated
initializers. Convenience initializers are not
required for their implementation in the type. Create them if it is
provides the most rational way to solve the set
tasks.
The syntax for declaring helper initializers is not overkill.
com differs from the syntax assigned.
SYNTAX
convenience init (options) {
// initializer body
}
A helper initializer is declared with the conve- modifier.
nience followed by the init keyword. This initializer type is also
can take input arguments and set values for properties.
The body of the initializer must contain a call to one of the designated
initializers.
Let's go back to the hierarchy of the previously defined Quadruped , Dog
and NoisyDog . Let's rewrite the Dog class so that when
installation, it made it possible to output an arbitrary
text. To do this, create an auxiliary initializer, with
which takes as input the value for the inherited property type (optional
Thing 21.1).
Listing 21.1
1 class Dog: Quadruped {
2
override init () {
3
super.init ()
4
self.type = "dog"
five
}
6
convenience init (text: String) {
7
self.init ()
8
print (text)
nine
}
ten
func bark () {
eleven
print ("woof")
12
}
thirteen
func printName () {
fourteen
print (self.name)
fifteen
}
16 }
17 var myDog = Dog (text: "An instance of the Dog class has been created")
As a result, when creating a new instance of the Dog class, you will have
prompted to choose one of two initializers. Aid-
the body initializer calls the assigned one and outputs the text to
console.
NOTE A convenience initializer can call a designated
through another helper initializer.

Initializer inheritance

Initializer inheritance is different from normal inheritance


superclass methods. There are two important rules that must be
dimo remember:
❑ If the subclass has its own designated initializer, then
parent class initializers are not inherited.

❑ If a subclass overrides all designated initializers


superclass, then it inherits and all its auxiliary initials
lysers.

Relationships between initializers

When it comes to relationships between initializers, Swift follows


the following rules:
❑ The designated initializer of the subclass must call the designated initializer.
the first superclass initializer.
❑ The helper initializer should call the designated
an initializer of the same object type.
❑ A convenience initializer should ultimately call
designated initializer.
An illustration of all three rules is shown in Fig. 21.1.
Figure: 21.1. Relationships between initializers
Shown here is a superclass with one assigned and two auxiliaries.
powerful initializers. One of the auxiliary
Cializers calls another, which in turn calls
appointed. Also depicted is a subclass having two
designated initializer and one helper.

Any call to any of the initializers shown should end


end up calling the designated initializer of the superclass (left
upper block).

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

The initializer takes and checks the values of two parameters.


If at least one of them is not greater than zero, then nil is returned . About-
Note that before returning nil , the initializer sets
Lists the values of all stored properties.
WARNING In classes, a failing initializer can return nil only
to after setting the values of all stored properties. In the case of structures, this
there is no limitation.
A designated initializer in a subclass can override
a failing superclass initializer, and a failing
an initializer can call a designated initializer of that
same class.
Do not forget that in the case of using a failing initial
The initializer returns an optional. Therefore, before you work
with an instance, you need to extract the optional
values.
You can use a failing initializer to e-
bore a suitable enumeration member based on the values
input arguments. Consider the example from Listing 21.3. In this
The example declares an enumeration TemperatureUnit containing
three members. A failing initializer is used to
to return an enum member that matches the passed one
parameter, or nil if the parameter value is invalid.
Listing 21.3
1 enum TemperatureUnit {
2
case Kelvin, Celsius, Fahrenheit
3
init? (symbol: Character) {
4
switch symbol {
five
case "K":
6
self = .Kelvin
7
case "C":
8
self = .Celsius
nine
case "F":
ten
self = .Fahrenheit
eleven
default:
12
return nil
thirteen
}
fourteen
}
15 }
16 let fahrenheitUnit = TemperatureUnit (symbol: "F")
When instantiating an enumeration as an input pair-
meter symbol is passed the value. Based on the passed value
the corresponding member of the enumeration is returned.
Enumerations whose members have values have a built-in
failing init initializer ? (rawValue :) . It can be used
use without being defined in the code (Listing 21.4).
Listing 21.4
1 enum TemperatureUnit: Character {
2
case Kelvin = "K", Celsius = "C", Fahrenheit = "F"
3}
4 let fahrenheitUnit = TemperatureUnit (rawValue: "F")
5 fahrenheitUnit! .HasValue
Members of the TemperatureUnit enumeration are of type Character .
In this case, you can call the built-in failing ini-
a cializer that will return an enumeration member matching
the passed value.
An alternative to init initializer ? is the init operator ! ... Time-
the only thing about them is that the second one returns implicitly
the retrieved instance of the object type, since to work with it
you do not need to additionally retrieve the optional value. When
this may still return nil .
Mandatory initializers
A required initializer is an initializer that
ry must be defined in all subclasses of this
class.

SYNTAX

required init (options) {


// initializer body
}
To declare a mandatory initializer before the init keyword, specify
the required modifier is given.
In addition, the required modifier must be specified before each implementation.
subclassing this initializer so that subsequent subclasses will also
implemented this initializer.

When implementing an initializer in a subclass, the override keyword


not used.

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
}

To declare a deinitializer, use the deinit keyword, after which


the body of the deinitializer is indicated in curly braces.
Superclass deinitializer is inherited by subclass and is called
automatically at the end of subclass deinitializers. Deini-
the superclass cializer is always called, even if deinitialization is
the torus of the subclass is missing. Also, the instance of the class is not
deleted,
until the deinitializer finishes, so all values
instance properties remain available in the deinitializer body.
Let's look at an example of using a deinitializer (Listing 21.5).
Listing 21.5

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

As we noted in the previous chapter, just before the destruction


the deinitializer is called by the duplication of an instance of the class, while
the question of how the instance is deleted was left unanswered.
Any instance can be deleted in one of two ways:
❑ the developer destroys it independently;
❑ it is destroyed by Swift.

Area of visibility

Previously, we ourselves destroyed the created instance of the option


of an onal type SubClass? passing it nil as the value .
Now let's turn to the logic of Swift. For this, we will develop a class
myClass , which contains a single property, description . Dan-

this property is used to distinguish one instance of a class


from another.
Two instances must be created, one of which must have
limited scope (Listing 22.1).

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 :

Instance ONE created


Instance TWO created
Instance TWO destroyed
The myVar2 instance is scoped to the operator
rum if . Despite the fact that we did not perform a forced removal
the instance, the deinitializer was called for it, as a result
it was automatically deleted.
The reason for deleting the second instance lies in the scope
the variable that stores it. The first instance is initialized outside
of the if statement , which means it is global for the entire program.
The second instance is local to the conditional statement. how
only the execution of the statement body completes, scope
the variable declared in it ends and it, along with the stored
the instance in it is automatically destroyed.

Number of links per instance


Consider an example in which one instance is pointed to by several
to different links (Listing 22.2).

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 :

Instance ONE created


Instance TWO created
Instance ONE destroyed
The variable myVar1 initially stores a reference to the instance
class myClass . After writing this link to the variable myVar2
two references already point to the created instance. As a result
this instance is deleted only when the last one is deleted
link to it.
Keep in mind that Swift class instances are not copied.
and by reference.
NOTE An instance exists as long as it is pointed to though
would be one link.

22 .2. Memory leaks

A memory leak can lead to the most dire results, but


one of which may be user rejection of your program.
An example of a memory leak
Consider a situation in which a memory leak might occur.
We will develop a class that can describe a person and his relatives
good relationships with other people. To do this, as a property type
class, the type itself will be specified (Listing 22.3).

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.

Strong and weak links

Swift tries to prevent the program from creating situations,


leading to memory leaks. Imagine what happens if objects
occupying a large memory area will not be deleted, occupying
precious free space. In the end the application
"Will fall". Such situations will lead to the loss of application users.
zheniya.
To solve the described situation, when Swift does not know which of the
links
lock can be deleted, and which not, there is a special mechanism,

called strong and weak links . All created links


by default are strong. And when two
objects point to each other with strong links, then Swift cannot
decide which link to remove first. For
solving the problem, some links can be converted to weak links.
Weak links are defined using weak keywords
and onowned . The weak modifier indicates that stored in the password
the link can be automatically changed to
nil . Therefore, the weak modifier is only available for optionals.
But in addition to options, there are data types that oblige
a variable to store a value (all non-optional data types).
To create weak references to non-optionals, use the modifier
unowned .
Let's rewrite the example from the previous listing, converting to weak
references in the properties father and mother (Listing 22.4).
Listing 22.4
1 class Human {
2
let name: String
3
var child = [Human?] ()
4
weak var father: Human?
five
weak 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 }
Console :
Alexey - deleted
Olga - deleted
Kirill – deleted

As a result, all three objects will be deleted, since after deletion


no cross-referencing remains.
The specified keywords can only be used for storage
a specific instance of the class. For example, you cannot specify
a weak reference to an array of instances or to a tuple of
instances of the class.

22 .3. Automatic reference counting


Although the title of this chapter is abbreviated as ARC
(Automatic Reference Counting - automatic reference counting),
but in the course of the study, we have never addressed it. Actually
in all our actions with class instances, I always participated
automatic reference counting mechanism.

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.

23 .1. Accessing properties via

optional chains

But what if an instance is stored in an optional property,


which characteristics you want to access?
Consider an example in which two classes describe a certain person
and her place of residence (Listing 23.1).
Listing 23.1
1 class Person {
2
var residence: Residence?
3}
4 class Residence {
five
var numberOfRooms = 1
6}
Instances of the Person class have a single property referenced
coy on an instance of the Residence class , which also has only one
property.
If you create a new instance of the Person class , then the property value
residence is nil because it is optional.
If you try to access the numberOfRooms property , use
using forced checkout, you will get an error because you know

there is no such thing. This method will work correctly only


when a reference to an instance is stored in the residence property .
To solve this problem, optional binding is required.
(Listing 23.2).
Listing 23.2
1 var man = Person ()
2 if let manResidence = man.residence {
3
print ("There is a house with \ (manResidence.numberOfRooms) rooms")
4 } else {
five
print ("Not at home")
6}
7 // display information about the absence of home
The presented approach allows solving the problem, but will require
add extra code if nesting classes as optional
properties will turn out to be multilevel.
Let's create a new class describing the room and add a link to
an instance to the Residence class (Listing 23.3).
Listing 23.3
1 class Person {
2
var residence: Residence?
3}
4 class Residence {
five
var numberOfRooms = 1
6
var room: Room?
7}
8 class Room {
nine
var square: Int = 10
10 }
As you can see, accessing the square property requires building a nested
optional binding constructs.
An alternative way of access is to use the option
nal chains . They allow you to write the complete
path to the required element, while after each optional there is no
it is necessary to put a question symbol instead of an exclamation.
Let's demonstrate this with an example (Listing 23.4). It creates
three-level nesting structure in which you want to get
access to the property room . Moreover, both properties (both residence and room
) - optionals.

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.

23 .2. Setting values via

optional chains

The use of optional chaining is not limited to receiving


properties and calling subscripts and methods. They can also be used
also used to set the values of attached properties.
Let's go back to the human dwelling example (Listing 23.5). Let us
you need to change the value of the square property .

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.

23 .3. Accessing methods via

optional chains

As noted earlier, optional chaining can be used


called not only for accessing properties, but also for calling methods.
Add a new method to the Residence class , which should provide
print information about the number of rooms (Listing 23.6).
Listing 23.6
1 class Residence {
2
var numberOfRooms = 1
3
var room: Room?
4
func returnNumberOfRooms () {
five
return numberOfRooms
6
}
7}
To call this method, you need to use the optional
sequence (Listing 23.7).
Listing 23.7
1 man.residence? .PrintNumberOfRooms ()
2 // output the number of rooms
The principle of access to a method is exactly the same as to a property.

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).

NOTE For details on the purpose and operation of protocols, see


Further .
Let's list the possibilities of extensions.
❑ Add computed instance properties and computed
properties of type ( static ).
❑ Defining instance methods and type methods.
❑ Defining new initializers, subscripts and nested
types.
❑ Ensuring compliance of the existing type with the protocol.
Extensions can add new functionality to a type, but they cannot
modify an existing one. The essence of the extension is exclusive
but in building up opportunities, but not in changing them.
SYNTAX
extension ExpandableTypeName {
// description of new functionality for the SomeType type
}
To declare an extension, use the extension keyword, after which
The name of the extensible data type is specified. It is to the specified type that
all capabilities described in the extension body change.

The new functionality added by the extension becomes


available to all instances of the extensible object type outside of the
depending on where these instances are declared.

24 .1. Computed properties in extensions

Extensions can add computed instance properties


and computed type properties to a specific data type. Consider
trim an example of extending the functionality of the Double type by creating a
row in it
new computed properties (Listing 24.1) and providing a Double
the ability to work with units of length.

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.

NOTE Extensions can only add new computed properties.


state. When trying to add stored properties or property observers, the
there is an error.

24 .2. Extension Initializers

Extensions can add initializers to existing ones


type. This way you can extend existing types by
example, to handle instances of your own types as
as input arguments.
NOTE For classes, extensions can only add new auxiliary
gual initializers. Trying to add a designated initializer, or
deinitializer leads to error.
As an example, let's write an initializer for the Double type (fox-
Thing 24.2). This example creates a structure describing the line
on surface. It is necessary to implement an initializer that takes
as an input argument a line instance and setting
the value corresponding to the line length.
Listing 24.2
1 import UIKit
2 // entity "line"
3 struct Line {
4
var pointOne: (Double, Double)
five
var pointTwo: (Double, Double)
6}
7 // extensions for Double
8 extension Double {
nine
init (line: Line) {
ten
self = sqrt (pow ((line.pointTwo.0 - line.pointOne.0), 2) +
pow ((line.pointTwo.1 - line.pointOne.1), 2))
eleven
}
12 }
13 var myLine = Line (pointOne: (10,10), pointTwo: (14,10))
14 var lineLength = Double (line: myLine) // prints 4
The UIKit library provides access to math functions.
yam sqrt () and pow () (respectively square root and erection
to a power), which are required to calculate the length of the line on
plane.

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.

24 .3. Extension Methods


The next extension function to be considered is co-
building new methods in an extensible data type. Consider
example (Listing 24.3). In this example, by extending the Int type
we add a repetitions method that takes a closure as input
like () -> () . This method is designed to perform
passed closure as many times as its own indicates
integer 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 ()

24 .4. Subscripts in extensions

Besides properties, methods, and initializers, extensions allow


create new subscripts.
The Int extension we create in Listing 24.5 implements the new
subscript that allows you to get a certain number of
the natural value of the instance. The subscript indicates the number of the
the position of the number to be returned.
Listing 24.5
1 extension Int {
2
subscript (var digitIndex: Int) -> Int {
3
var decimalBase = 1
4
while digitIndex> 0 {
five
decimalBase * = 10
6
--digitIndex
7
}
8
return (self / decimalBase)% 10
nine
}
10 }
11 746381295 [0]
12 // returns 5
13 746381295 [1]
14 // returns 9
If the number does not have a digit with the requested index, return-
is 0, which does not violate the logic of work.

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:

class HostClassName: SuperClassName, Protocol1,


Protocol2 {
// class body
}
25 .1. Required properties

A protocol may require a corresponding implementation type.


an instance property or a property of type ( static ) that has a con-
specific name and data type. In this case, the protocol does not indicate the
form
properties (stored or calculated). Also can be specified
requirements for the accessibility and variability of the parameter.
If the property has an accessibility requirement and is mutable
st, then this property cannot be a constant or
read-only computed property. Accessibility requirement
denoted with the construction {get} , and the accessibility requirement
and mutability - using the construction {get set} .
Let's create a protocol containing a number of requirements for the receiving
its type (Listing 25.1).
Listing 25.1
1 protocol SomeProtocol {
2
var mustBeSettable: Int {get set}
3
var doesNotNeedToBeSettable: Int {get}
4}

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 .

25 .2. Required methods

The protocol may require the implementation of a specific method of instance


plyar or method of type. The entry form for this is similar to an indication
requirements for the implementation of properties.
To require the implementation of a type method, you must use
modifier static . Also, the protocol can describe changing
method. This is done using the mutating modifier .
NOTE If you specified the mutating keyword before the requirement
Toda, then you no longer need to specify it when implementing the method in the class. This
the keyword is only required in the implementation of structures.
When implementing a method in a data type, you must exactly match
observe all protocol requirements, in particular the name of the method, the
presence
or no input arguments, return type,
modifiers (Listing 25.4).
Listing 25.4
1 protocol RandomNumberGenerator {
2
func random () -> Double
3
static func getRandomString ()
4
mutating func changeValue (newValue: Double)
5}
This example implements the RandomNumberGenerator protocol , which
which contains requirements for the implementation of three methods.
Method ek-
the random () instance must return a Double . Method
getRandomString must be a method of type, with requirements
are not specified to its return value. The changeValue () method should
wives be mutating and take as an input parameter
a value of type Double .
This protocol makes no assumptions about
of how the random number will be calculated, it only matters the fact
fulfillment of requirements.

25 .3. Required initializers

Additionally, the protocol may impose requirements on the implementation


initializers. It is necessary to write initializers exactly
just like you write them in object type, omitting curly braces
and the body of the initializer.
Initializer requirements can be met according to
the class in the form of a designated or auxiliary
the initializer. Anyway, before declaring the initializer
the protocol must be specified modifier required . It's ha-
guarantees that you implement the specified initializer in all sub-
classes of this class.
NOTE There is no need to indicate the implementation of protocol initializers
the required modifier in classes that have the final modifier.
We implement a protocol containing requirements for the implementation of
the initial
congestion, and a class that fulfills the protocol requirements (Listing 25.5).
Listing 25.5
1 protocol Named {
2
init (name: String)
3}
4 class Person: Named {
5
var name: String
6
required init (name: String) {
7
self.name = name
8
}
9}

25 .4. Protocol as data type


The protocol itself does not carry any functional load,
it only contains requirements for the implementation of object types. However
less protocol is a full-fledged data type.
By using a protocol as a data type, you indicate that
that the value written to this storage must be of type
data that conforms to the specified protocol.
Since protocol is a data type, you can organize
verification of compliance with the protocol with the help of the operator is , who
we discussed when exploring the topic of typecasting. When checking
match returns true if the instance being checked is
lar corresponds to the protocol, and false otherwise.

25 .5. Extension and protocols


Extensions can interact not only with object types-
mi, but also with protocols.

Adding a type conformance to a protocol


You can use extensions to add requirements
according to the correspondence of some object type to the protocol. For this
in the extension after the name of the data type, separated by a colon, it is necessary
list the new protocols (Listing 25.6).
Listing 25.6
1 protocol TextRepresentable {
2
func asText () -> String
3}
4 extension Int: TextRepresentable {
5
func asText () -> String {
6
return String (self)
7
}
8}
9 5.asText ()

In this example, the TextRepresentable protocol requires that


The asText () method has been implemented on the base object type . With
the help
With the extension, we add the requirement for the type Int to match
to this protocol, while, since somewhere earlier it was implemented
the data type itself, the implementation is mandatory
of this method.

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

A protocol can inherit one or more other protocols.


At the same time, he can add new requirements on top of the inheritance
e, - then the type that has accepted the protocol for implementation will be
needs to fulfill the requirements of all protocols in the structure. When
protocol inheritance uses the same syntax as
class inheritance.
Working with legacy protocols is demonstrated in listing
ge 25.8.
Listing 25.8
1 protocol SuperProtocol {
2
var someValue: Int {get}
3}
4 protocol SubProtocol: SuperProtocol {
5
func someMethod ()
6}
7 struct SomeStruct: SubProtocol {
8
let someValue: Int = 10
9
func someMethod () {
10
// method body
11
}
12 }

Protocol SuperProtocol has requirements for the implementation of the property,


however, it is inherited by the SubProtocol protocol , which has
method implementation requirements. The structure takes for execution
the requirements of the SubProtocol protocol , which means that it must contain
both property and method are implemented.

25 .7. Class protocols

You can restrict the protocol so that it can


only accept classes (and not structures
and enumerations). To do this, the protocol in the list of inherited proto-
colov, you must specify the keyword class . This word is always
must be listed first in the inheritance list.
An example of how to create a protocol is shown in Listing 25.9. In it we are
change the SubProtocol protocol so that it can be accepted
exclusively class.
Listing 25.9
1 protocol SubProtocol: class, SuperProtocol {
2
func someMethod ()
3}

25 .8. Composition of protocols

Sometimes it is convenient to require that a type matches more than one,


but several protocols. In this case, of course, you can create
a new protocol, inherit several necessary protocols into it
stake and use the name of the newly created protocol. but
to solve this problem, it is better to use the composition
tokols , that is, combine several protocols.
SYNTAX
protocol <Protocol1, Protocol2 ...>
To compose protocols, you must use the protocol keyword,
followed by a list of protocols to be combined in angle brackets.
Listing 25.10 shows an example in which the two combo protocols
are formed into a single requirement.
Listing 25.10

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

1 var str: String

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.

Using Apple documentation you can find the answer practically


for any question related to Swift development or use
using Xcode.
The Conforms To section lists the protocols that the
data type String , among them you can find the one mentioned earlier
Hashable protocol . This suggests that values of this type have
are scalable and can be compared.
Let's move on to the example we met when studying the word
brews (Listing 26.2). Here, the keys and values properties are used to
to get a collection consisting of only keys or only
from the meanings of the given dictionary.
Listing 26.2

1 var countryDict = ["RUS": "Russian


["BEL": "Belarus",
Federation "," BEL ":" Belarus "," UKR ":" Ukraine "] " UKR ":" Ukraine ",
"RUS": "Russian
Federation"]
2 // all keys of the countryDict dictionary
3 var keysOfDict = countryDict.keys
LazyMapCollection
<Dictionary <String,
String>, String>
4 // all values of the countryDict dictionary
5 var valuesOfDict = countryDict.values
LazyMapCollection
<Dictionary <String,
String>, String>

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

1 var countryDict = ["RUS": "Russian


["BEL": "Belarus",
Federation "," BEL ":" Belarus ",
"UKR": "Ukraine",
"UKR": "Ukraine"]
"RUS": "Russian
Federation"]
2 var keys = Set (countryDict.keys)
{"BEL", "UKR", "RUS"}
3 var values = Array (countryDict.values)
["Belarus",
"Ukraine",
"Russian
Federation"]
The same principle can be applied to the result of the method
reverse () , which returns a reverse array. If you look
See the help for the example in Listing 26.4, you can see that the data type
the collection returned is ReverseRandomAccessCollection
<Array <Int>> . Moving on to the description of this type, we can conclude that
that it is an ordinary collection that returns the same
elements as the base collection, but by the type of base and return
collection is an Array <Int> (or <[Int]> ). This is nothing but
an array of integers.
Listing 26.4

1 var someArray = [1, 3, 5, 7, 9]


[1, 3, 5, 7, 9]
2 var reverseSomeArray = someArray.reverse ()
ReverseRandomAccessCollection <Array <Int>>
Thus, the result of the reverse () method can be omitted.
form into an array using the Array () function , as in the example
with properties keys and values , and assign directly to an array like
Array <Int> (Listing 26.5).
Listing 26.5
1 var someArray = [1, 3, 5, 7, 9]
[1, 3, 5, 7, 9]
2 let resArray: Array <Int> = someArray.reverse () [9, 7, 5, 3, 1]
The main purpose of this chapter is to help you understand
how important it is to use the documentation that the company
Apple, fortunately, did not cheat us. The documentation can be found from-
answers to most of the questions you have.
27 Universal
templates
Generics (generic) are one of the most powerful
Swift tools. Most of the libraries are written on their basis.
Even if you've never specifically used universal
templates, you actually interacted with them practically
in every written program.
Generic templates enable flexible designs
(functions, object types) without binding to a specific data type.
You are only describing the requirements and functionality,
and Swift independently determines which data types are available
developed functionality. An example would be datatype
Array (array). Array elements can be values
arbitrary data types, and for this developers do not need
create separate array types: Array <Int> , Array <String> , etc.
A universal template was used to implement the collection.
allowing you to specify the data type requirements, if necessary.
So, for example, in the implementation of the Dictonary type there is a
requirement,
so that the data type of the key conforms to the Hashable protocol (its
we discussed the purpose earlier).

27 .1. Versatile functions

We will develop a function with which you can change values


two integer variables (Listing 27.1).
Listing 27.1
1 func swapTwoInts (inout a: Int, inout b: Int) {
2
let temporaryA = a
3
a=b
4
b = temporaryA
5}
6 var firstInt = 4010
7 var secondInt = 13
8 swapTwoInts (& firstInt, & secondInt)
The swapTwoInts () function uses pass -through parameters to provide
provide access directly to the parameters passed to the function
tion, and not to their copies. As a result of the execution of the value in the
variable
GOVERNMENTAL firstInt and secondInt reversed.
This feature is extremely useful but very limited.
noah in their capabilities. To swap the values of two
variables of other types, you will have to write a separate function:
swapTwoStrings () , swapTwoDoubles () , etc. If you pay attention to
the fact that the bodies of all functions should be practically the same, then
we're just going to duplicate the code, although earlier in the book
it was repeatedly recommended to avoid this by all means.
To solve the problem, it would be much more convenient to use a uni-
versal function that allows you to pass as an argument
values of any type with only one requirement: the data types of both
arguments must be the same.
Generic functions are declared in exactly the same way as standard-
with one exception: after the function name in angle brackets
a placeholder for the type name is specified, that is, a literal that is further
in the function will indicate the data type of the passed argument.
We transform the swapTwoInts () function into a universal view (fox-
Thing 27.2).

Listing 27.2

1 func swapTwoValues <T> (inout a: T, inout b: T) {


2
let temporaryA = a
3
a=b
4
b = temporaryA
5}
6 var firstString = "one"
7 var secondString = "two"
8 swapTwoValues (& firstString, b: & secondString)
In a generic function, the type placeholder is the literal T ,
which allows you to set the data type in the list of input arguments
instead of a concrete type ( Int , String , etc.). In this case, it is determined
that a and b must be of the same data type.
Function swapTwoValues () can be called just as determined
the previously divided swapTwoInts () function .
The placeholder used is called the type parameter. Once
you defined it, you can use it to specify the type of any
parameter or value, including the return value of the function.
If necessary, you can set several type parameters by typing
them in angle brackets, separated by commas.

27 .2. Generic types

In addition to generic functions, generic templates


allow you to create generic data types. Towards universal
types include, for example, the previously mentioned arrays and dictionaries.
Let's create a universal collection Stack (stack). Developed
a stack is an ordered collection of elements, similar to an array, but
with a strict set of available operations:
❑ the push () method is used to add an element to the end of the collection;
❑ the pop () method is used to return an element from the end of the collection
removing it from there.
No other available operations to interact with your
elements the stack does not support.
First of all, let's create a non-generic version of the type (fox-
Thing 27.3).

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

1 struct Stack <T> {


2
var items = [T] ()
3
mutating func push (item: T) {
4
items.append (item)
5
}
6
mutating func pop () -> T {
7
return items.removeLast ()
8
}
9}

The universal version differs from the non-universal only in that


that instead of specifying a specific data type, a placeholder is specified
type name.
When creating a new collection of Stack type , in angle brackets you need
specify the data type, after which you can use the described methods
to modify the repository (Listing 27.5).
Listing 27.5

1 var stackOfStrings = Stack <String> ()


2 stackOfStrings.push ("uno")
3 stackOfStrings.push ("dos")
4 let fromTheTop = stackOfStrings.pop ()

Two items were added to a collection of type Stack <String> and


flax one.
We can modify the described data type in such a way that
when creating the store, there was no need to specify the element type
stack items (Listing 27.6). To implement this task, we describe the initial
a lizer that takes an array of values as input.
Listing 27.6

1 struct Stack <T> {


2
var items = [T] ()
3
init () {}
4
init (_ elements: T ...) {
5
self.items = elements
6
}
7
mutating func push (item: T) {
8
items.append (item)
9
}
10
mutating func pop () -> T {
11
return items.removeLast ()
12
}
13 }
14 var stackOfInt = Stack (1, 2, 4, 5)
15 var stackOfStrings = Stack <String> ()

Since we have declared our own initializer that takes


input parameter, to preserve functionality I had to
also describe an empty initializer.
Now we can avoid creating a stack without specifying the element type,
but simply pass the values as an input argument to the
lyzer type.

27 .3. Type constraints


Sometimes it is useful to indicate certain restrictions, overlays
data types of the generic template. As an example
we have already considered the Dictionary data type , where for the key there is
There is a requirement: the data type must conform to the protocol
Hashable .

Universal templates allow you to impose certain requirements


definitions and restrictions on the data type of the value. You can specify
a list of types that the value type must match. If
element of this list is the protocol (which is also
data type), then the correspondence of the value type to the given
protocol; if the type is a class, structure, or enumeration,
then it is checked whether the type of the value matches the given type.
To define restrictions, you must pass a list of names
types separated by a colon after the type name placeholder.
We implement a function that searches for an element in an array and
rotating its index (Listing 27.7).

NOTE To provide functionality for comparing two values in Swift


there is a special protocol Equatable. He obliges the supporter of him
data type to implement the functionality of comparing two values using the operator
moat of equality (==) and inequality (! =). In other words, if the data type is supported
If this protocol lives, then its values can be compared with each other.

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.

27 .4. Generic extensions

Swift allows you to extend the generic types described. Wherein


placeholder names used in the type description can indicate-
expanding as well.
Let's extend the previously described generic Stack type by adding
a computed property that returns the top element of the stack without it
deletions (Listing 27.8).
Listing 27.8

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 .

27 .5. Related types


When defining a protocol, it can be convenient to use related
types indicating some as yet unknown data type.

A bound type allows you to specify a data type placeholder that


will be used when filling out the protocol. Actually type
data is not specified until the protocol is accepted
any object type.
Let's define a Container protocol using the associated Item type-
Type (Listing 27.9).
Listing 27.9
1 protocol Container {
2
typealias ItemType
3
mutating func append (item: ItemType)
4
var count: Int {get}
5
subscript (i: Int) -> ItemType {get}
6}

The Container protocol can be used in various


collections, such as the Stack collection type described earlier .
In this case, the data type used in the properties and methods for
tocol, unknown in advance.
To solve the problem, the associated ItemType is used , which
ry is determined only when the protocol is accepted by the data type.
An example of a protocol to the execution of the data type Stack pre-
listed in Listing 27.10.

1 struct Stack <T>: Container {


2
typealias ItemType = T
3
var items = [T] ()
4
var count: Int {
5
return items.count
6
}
7
init () {}
8
init (_ elements: T ...) {
nine
self.items = elements
ten
}
eleven
subscript (i: Int) -> T {
12
return items [i]
thirteen
}
fourteen
mutating func push (item: T) {
fifteen
items.append (item)
sixteen
}
17
mutating func pop () -> T {
eighteen
return items.removeLast ()
19
}
20
mutating func append (item: T) {
21
items.append (item)
22
}
23 }
Since the Stack type now supports the Container protocol , it supports
there were three new elements: property, method and subscript. Key
the word typealias indicates which data type is associated -
nym in the given object type.
Since the name placeholder is used as the argument type
item of the append property and the return value of the subscript, Swift can
can independently determine that the placeholder T indicates a type
ItemType corresponding to the data type in the Container protocol . When
it is not necessary to specify the typealias keyword if you use it
delete, then the type will continue to work without errors.
28 Error handling
Error handling involves responding to emerging
during the execution of the error program. Some operations fail
can guarantee correct execution due to the
circumstances. In this case, it is very important to determine the cause
error and handle it correctly so as not to cause
the sudden end of the entire program.
An example is writing information to a file. When
an attempt to access the file may not exist or the user
you may not have permission to write to it.
The distinctive features of situations can help the program to self-
to solve emerging problems efficiently.

28 .1. Throwing errors

Swift uses enumeration to create a list of possible errors.


where each member of the enumeration corresponds to a separate error.
The enumeration itself must support the Error-
Type , which, although empty, indicates that the given
the object type contains error variants.
You shouldn't create one listing for all occasions. Group-
Consider possible errors by their meaning in the various enumerations.
The following example declares a data type that describes
bugs in the food vending machine (Listing 28.1).
Listing 28.1
1 enum VendingMachineError: ErrorType {
2
case InvalidSelection
3
case InsufficientFunds (coinsNeeded: Int)
4
case OutOfStock
5}

Each of the enumeration members indicates a different type of error:


❑ wrong choice;
❑ lack of funds;
❑ absence of the selected product.
The error allows you to show that some non-standard has occurred
situation and normal program execution cannot continue.
The process of generating an error is called error throwing . For
in order to throw an error, you must use the operator
torus throw . So, the following code when trying to make a purchase
throws a five coin shortage error (Listing 28.2).
Listing 28.2
1 throw VendingMachineError.InsufficientFunds (coinsNeeded: 5)

28 .2. Error processing

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.

Convert error to optional

To convert a thrown error to an optional value


the try statement is used , or rather, its form try? ... If in this
If an error is thrown, then the value of the expression is calculated
as nil .
Consider the example from Listing 28.7.
Listing 28.7
1 func someThrowingFunction () throws -> Int {
2
// ...
3}
4 let x = try? someThrowingFunction ()
If the someThrowingFunction () function throws an error, then in the end
Stante x will the value of nil .
Prohibiting error transmission
In some situations, you can be sure that a block of code in
runtime will not throw an error. In this case, it is necessary
use the try statement! , which indicates that the given
the block is guaranteed not to throw an error - this prohibits transmission
errors in general.
Consider the example from Listing 28.8.
Listing 28.8
1 let photo = try! loadImage ("./ Resources / John Appleseed.jpg")
The loadImage () function loads the local image,
and in case of its absence it throws an error. Since the specified in it
the image is part of a program you are developing and
located at the specified address, using the operator
try! it is advisable to disable the error transmission mode.
Be careful: if the block of code is
will throw it away, then your program will terminate urgently.

28 .3. Delayed cleanup actions

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

1 func processFile (filename: String) throws {


2
if exists (filename) {
3
let file = open (filename)
4
defer {
five
close (file)
6
}
7
while let line = try file.readline () {
8
// work with the file.
nine
}
ten
}
11 }
In this example, the defer statement simply provides a closure against
previously covered file.

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.

29 .1. Operator functions


With operator functions, you can provide interoperability
action of native object types through standard
Swift operators.
Suppose you have designed a structure to describe a vector
on a plane (Listing 29.1).
Listing 29.1
1 struct Vector2D {
2
var x = 0.0, y = 0.0
3}
The x and y properties show the coordinates of the end point of the vector.
The starting point is either at (0,0) or
at the end point of the previous vector.
If you are faced with the task of adding two vectors, then it is easier
just use the operator function and create your own
implement the addition operator ( + ), as shown in Listing 29.2.

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

Here the operator function is defined with a name corresponding to


addition operator. Since the addition operator is binary,
it should take two given values and return a result
addition.
Situation where multiple objects have the same name, in Swift
is named reboot . With this concept, we have repeatedly
got acquainted while reading the book.
Prefix and Postfix Operators
The addition operator is binary infix, that is, it becomes
between two operands. Besides infix operators
in Swift there are prefix (precede the operand) and post-
fixed (follow the operand) operators.
To reload a prefix or postfix operator before
the operator function declaration must specify the modifier
prefix or postfix respectively.
Implementing the unary minus prefix operator for the structure
Vector2D (Listing 29.3).
Listing 29.3
1 prefix func - (vector: Vector2D) -> Vector2D {
2
return Vector2D (x: -vector.x, y: -vector.y)
3}
4 let positive = Vector2D (x: 3.0, y: 4.0)
5 let negative = -positive
6 // negative - Vector2D instance with values (-3.0, -4.0)
By creating an operator function, we can use
unary minus in order to unfold the vector relative to
of the coordinates.

Compound assignment operator


In compound assignment operators, the assignment operator ( + )
combined with another operator. To reload composite
operators in an operator function, the first passed argument
must be made through ( inout ), since it is its value
will change during the execution of the function.
Listing 29.4 shows an example of a composite operator implementation.
addition assignments for instances of type Vector2D .
Listing 29.4
1 func + = (inout left: Vector2D, right: Vector2D) {
2
left = left + right
3}
4 var original = Vector2D (x: 1.0, y: 2.0)
5 let vectorToAdd = Vector2D (x: 3.0, y: 4.0)
6 original + = vectorToAdd
7 // original is now (4.0, 6.0)
Since the addition operator was previously declared, you don't need to
implement
call it in the body of this function. You can just add two
values of type Vector2D .
Note that the first input argument to the function is
through.
Equivalence operator
Custom object types do not contain inline implementation.
equivalence operator, therefore, to compare two
instance, you need to reload this statement with
operator function.
The following example shows the implementation of the operator equivalent
-
and the nonequivalence operator (Listing 29.5).
Listing 29.5
1 func == (left: Vector2D, right: Vector2D) -> Bool {
2
return (left.x == right.x) && (left.y == right.y)
3}
4 func! = (Left: Vector2D, right: Vector2D) -> Bool {
five
return! (left == right)
6}
7 let twoThree = Vector2D (x: 2.0, y: 3.0)
8 let anotherTwoThree = Vector2D (x: 2.0, y: 3.0)
9 if twoThree == anotherTwoThree {
ten
print ("These two vectors are equivalent.")
11 }
12 // prints "These two vectors are equivalent."
In operator function ==, we implement all the logic for comparing two
instances of type Vector2D . Since this function returns false
in case of inequality of operators, we can use it
inside its own implementation of the nonequivalence operator.

29 .2. Custom Operators

In addition to the standard Swift operators, you can define


share your own. Native operators are declared with
the operator keyword and the prefix , infix and postfix modifiers ,
and first it is necessary to declare a new operator, and only then
define its new implementation as an operator function.
The following example implements the new +++ operator , which is
gives an instance of the Vector2D type with itself (Listing 29.6).
Listing 29.6
1 prefix operator +++ {}
2 prefix func +++ (inout vector: Vector2D) -> Vector2D
3{
4
vector + = vector
five
return vector
6}
7 var toBeDoubled = Vector2D (x: 1.0, y: 4.0)
8 let afterDoubling = +++ toBeDoubled
9 // toBeDoubled is now (2.0, 8.0)
10 // afterDoubling also matters (2.0, 8.0)

You might also like