Александр Казанский
Санкт-Петербург
«БХВ-Петербург»
2022
УДК 004.4
ББК 32.973.26
К37
Казанский А. А.
К37 Разработка приложений на Swift и SwiftUI с нуля. — 2-е изд., перераб.
и доп. — СПб.: БХВ-Петербург, 2022. — 416 с.: ил. — (С нуля)
ISBN 978-5-9775-9681-7
Рассмотрены принципы протокольно-ориентированного и функционального
программирования на языке Swift 5.5 для операционных систем macOS, iOS и
iPadOS. Подробно описана среда разработки Xcode 13.1 и SwiftUI. Приведены
приемы проектирования и разработки программ для macOS с использованием
фреймворка Cocoa, мобильных приложений с помощью фреймворка Cocoa Touch,
описаны принципы создания проектов с интерфейсом Storyboard. Главы содержат
упражнения с решениями и примеры кода для начинающих программистов. Во
втором издании описано создание проектов в редакторе Interface Builder и подроб-
но рассмотрены новые возможности Swift 5.5.
Для программистов
УДК 004.4
ББК 32.973.26
Группа подготовки издания:
Руководитель проекта Павел Шалин
Зав. редакцией Людмила Гауль
Редактор Григорий Добин
Компьютерная верстка Ольги Сергиенко
Дизайн серии Карины Соловьевой
Оформление обложки Зои Канторович
Подписано в печать 02.06.22.
Формат 70×1001/16. Печать офсетная. Усл. печ. л. 33,54.
Тираж 1000 экз. Заказ №
"БХВ-Петербург", 191036, Санкт-Петербург, Гончарная ул., 20.
Отпечатано с готового оригинал-макета
ООО "Принт-М", 142300, М.О., г. Чехов, ул. Полиграфистов, д. 1
ISBN 978-5-9775-9681-7 © Казанский А. А., 2022
© Оформление. ООО "БХВ-Петербург", ООО "БХВ", 2022
<0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0>
ОГЛАВЛЕНИЕ
Введение ............................................................................................................................8
ЧАСТЬ I. ПРОГРАММИРОВАНИЕ НА SWIFT 5.5...............................................13
Глава 1. Среда разработки Xcode 13.1. Первые приложения на Swift 5.5
и SwiftUI ..........................................................................................................................14
1.1. macOS Big Sur, Xcode 13 и Swift 5.5 .....................................................................................14
1.2. Консольные приложения Playground для iOS ......................................................................18
1.3. Консольные приложения Playground для macOS.................................................................26
1.4. Консольные приложения в режиме project с шаблоном Command Line Tool ...................27
1.5. Написание кода на REPL Swift..............................................................................................32
1.6. Программирование на Swift 5.5 в режиме online .................................................................34
1.7. Программирование на языке С++ в среде Xcode.................................................................35
1.8. Создание проекта (project) для iOS c интерфейсом SwiftUI ...............................................39
1.9. Разработка на SwiftUI приложения для банковских операций...........................................44
Глава 2. Основы программирования на языке Swift .............................................49
2.1. Переменные и константы.......................................................................................................49
2.2. Стандартные числовые функции...........................................................................................55
2.3. Арифметические операции и операции сравнения..............................................................58
2.4. Построение арифметических выражений и преобразование числовых данных...............61
2.5. Упражнения на рассмотренный материал............................................................................64
2.6. Логические операции .............................................................................................................64
2.7. Упражнения на рассмотренный материал............................................................................65
Упражнение № 1..............................................................................................................65
Упражнение № 2..............................................................................................................66
Упражнение № 3..............................................................................................................66
2.8. Создание собственных пользовательских операций ...........................................................66
2.9. Работа с символьными константами и переменными String и Character ..........................68
2.10. Опциональные (Optional) типы данных и nil .....................................................................71
Глава 3. Функции языка Swift и их возможности...................................................74
3.1. Функции, не имеющие параметров и возвращаемого значения.........................................75
3.2. Функции без параметров и с одним возвращаемым значением .........................................76
3.3. Функции c несколькими входными параметрами и одним возвращаемым значением ......77
3.4. Локальные параметры функции ............................................................................................78
3.5. Метки и имена параметров функции ....................................................................................79
4 Оглавление
3.6. Функции c несколькими входными параметрами и несколькими возвращаемыми
значениями ..............................................................................................................................81
3.7. Функции с переменным числом параметров (Variadic function) ........................................82
3.8. Параметры типа in-out (входные-выходные) .......................................................................82
3.9. Упражнения на рассмотренный материал............................................................................83
Упражнение № 1..............................................................................................................83
Упражнение № 2..............................................................................................................83
Упражнение № 3..............................................................................................................84
Упражнение № 4..............................................................................................................84
3.10. Способы работы с параметрами in-out, не приводящие к побочным эффектам.............84
3.11. Перегрузка функций.............................................................................................................86
3.12. Вложенные функции ............................................................................................................87
3.13. Функции, возвращающие другие функции ........................................................................88
3.14. Замыкания (Closures)............................................................................................................90
3.15. Замыкающие выражения (Closure expressions) ..................................................................93
3.16. Захват значений замыкания из контекста и запрет их изменений ...................................95
3.17. Использование переменной в качестве функции...............................................................96
3.18. Каррирование функций (Currying functions) ......................................................................96
3.19. Универсальные функции (Generic functions)......................................................................98
Глава 4. Управление потоком (Control Flow). Перечисления.............................101
4.1. Условный оператор if ...........................................................................................................101
4.2. Конструкция условного оператора if...else .........................................................................103
4.3. Упражнения на рассмотренный материал..........................................................................105
Упражнение № 1............................................................................................................105
Упражнение № 2............................................................................................................105
Упражнение № 3............................................................................................................105
Упражнение № 4............................................................................................................105
Упражнение № 5............................................................................................................105
4.4. Условный оператор switch ...................................................................................................105
4.5. Упражнение на рассмотренный материал ..........................................................................108
4.6. Циклы ....................................................................................................................................108
4.6.1. Циклы for _ in.......................................................................................................109
4.6.2. Циклы forEach......................................................................................................112
4.6.3. Циклы while..........................................................................................................114
4.6.4. Циклы repeat...while.............................................................................................115
4.7. Кортежи (Tuples)...................................................................................................................116
4.8. Перечисления (Enumeration)................................................................................................118
4.9. Диапазоны (Range) ...............................................................................................................122
Глава 5. Коллекции.....................................................................................................125
5.1. Одномерные массивы...........................................................................................................125
5.2. Сортировка одномерных массивов .....................................................................................128
5.3. Двумерные массивы .............................................................................................................129
5.4. Словари (Dictionaries)...........................................................................................................134
5.5. Множества (Sets) ..................................................................................................................138
Глава 6. Классы и структуры ...................................................................................144
6.1. Синтаксис и семантика классов и структур .......................................................................146
6.2. Сохраняемые и ленивые сохраняемые свойства................................................................148
Оглавление 5
6.3. Вычисляемые свойства ........................................................................................................149
6.4. Наблюдатели свойств ...........................................................................................................151
6.5. Инициализаторы ...................................................................................................................153
6.6. Методы класса ......................................................................................................................156
6.7. Сабскрипты (Subscripts) .......................................................................................................160
6.8. Наследование (Inheritance)...................................................................................................164
6.9. Создание класса для решения матричной задачи ..............................................................173
6.10. Структуры (Structs).............................................................................................................177
6.11. Мутирующие методы структур .........................................................................................181
6.12. Расширения (Extensions) ....................................................................................................183
6.13. Контроль доступа ...............................................................................................................185
6.14. Типы значений и ссылочные типы....................................................................................186
6.15. Автоматический подсчет ссылок (ARC) ..........................................................................190
6.16. Сильные, слабые и не учитываемые (Unowned) ссылки.................................................191
6.17. Метод Copy-On-Write (COW) ............................................................................................194
Глава 7. Функциональное программирование на Swift.......................................197
7.1. Простые математические функции .....................................................................................203
7.2. Функции высшего порядка ..................................................................................................206
7.3. Рекурсии ................................................................................................................................207
7.4. Упражнения на рассмотренный материал..........................................................................215
Упражнение № 1............................................................................................................215
Упражнение № 2............................................................................................................215
7.5. Функциональные программы, использующие вместо циклов рекурсии.........................216
Пример 7.5.1. Поиск простых чисел ............................................................................216
Пример 7.5.2. Разложение положительного целого числа
на простые множители ..................................................................................................217
Пример 7.5.3. Построение таблиц ................................................................................217
Пример 7.5.4. Преобразование типа массива..............................................................218
7.6. Библиотечные функции для работы с одномерными массивами.....................................219
7.6.1. Метод map() .........................................................................................................220
7.6.2. Метод reduce() .....................................................................................................220
7.6.3. Метод filter().........................................................................................................221
7.6.4. Метод reversed()...................................................................................................222
7.6.5. Метод enumerated()..............................................................................................222
7.6.6. Метод zip()............................................................................................................222
7.6.7. Метод contains()...................................................................................................223
7.7. Упражнения на рассмотренный материал..........................................................................223
Упражнение № 1............................................................................................................223
Упражнение № 2............................................................................................................223
Упражнение № 3............................................................................................................223
7.8. Функциональные программы для вычисления векторных операций ..............................223
7.8.1. Векторные операции ...........................................................................................224
7.8.2. Примеры функциональных программ для работы с векторами ......................224
Сумма векторов ...................................................................................................224
Умножение вектора на скаляр............................................................................225
Скалярное произведение векторов ....................................................................226
Длина вектора ......................................................................................................226
7.9. Упражнения на рассмотренный материал..........................................................................226
Упражнение № 1............................................................................................................226
Упражнение № 2............................................................................................................226
6 Оглавление
Упражнение № 3............................................................................................................227
Упражнение № 4............................................................................................................227
7.10. Использование методов функционального программирования при решении задач
с массивами .........................................................................................................................227
7.11. Упражнения на рассмотренный материал........................................................................230
Упражнение № 1............................................................................................................230
Упражнение № 2............................................................................................................230
Упражнение № 3............................................................................................................230
7.12. Использование функционального программирования для работы
со сложными типами данных ............................................................................................231
Глава 8. Протокольно-ориентированное программирование на Swift.............233
8.1. Протоколы стандартной библиотеки Swift ........................................................................234
8.2. Требования, которые можно задавать в протоколе ...........................................................237
8.3. Объявление свойств в протоколе ........................................................................................238
8.4. Объявление методов в протоколе .......................................................................................242
8.5. Наследование протоколов....................................................................................................243
8.6. Композиция протоколов ......................................................................................................244
8.7. Делегирование (Delegation) .................................................................................................245
8.8. Протокол как тип ..................................................................................................................247
8.9. Расширение протокола.........................................................................................................248
8.10. Полиморфизм, основанный на протоколе ........................................................................249
8.11. Использование в протоколах ассоциированных типов ...................................................251
8.12. Разработка приложения с использованием протоколов..................................................253
Глава 9. Новые методы в версии Swift 5.5. Параллелизм ...................................263
9.1. Асинхронные функции async/await.....................................................................................265
9.2. Акторы...................................................................................................................................271
9.3. Изменение протокола Codable ............................................................................................277
9.4. Преобразование типов и ключевое слово lazy ...................................................................278
9.5. Асинхронное связывание свойств let (async binding let) ...................................................278
ЧАСТЬ II. ТЕХНОЛОГИИ РАЗРАБОТКИ ПРИЛОЖЕНИЙ
В СРЕДЕ XСODE 11.2 И 13.1 ....................................................................................283
Глава 10. Создание проектов в редакторе Interface Builder
на основе интерфейса Storyboard .............................................................................284
10.1. Краткая историческая справка ..........................................................................................284
10.2. Интерфейс Xcode 13.1 ........................................................................................................285
10.3. Навигатор проекта Xcode 13.1...........................................................................................290
10.4. Создание проекта для iOS с использованием Interface Builder.......................................291
10.5. Создание проекта для iOS без написания кода ................................................................298
10.6. Создание проекта обработки матрицы для iOS ...............................................................302
10.7. Создание игры для iOS: бросание игральной кости ........................................................305
10.8. Создание проекта обработки матрицы для macOS..........................................................307
10.9. Создание проекта определения цифр числа для macOS .................................................308
10.10. Создание проекта приведения матрицы к верхней треугольной для macOS ..............310
10.11. Создание проекта для macOS без написания кода ........................................................314
Оглавление 7
Глава 11. Технология SwiftUI....................................................................................316
11.1. Обновление системы macOS и Xcode...............................................................................317
11.2. Ключевые понятия SwiftUI................................................................................................320
11.3. Начало работы с использованием интерфейса SwiftUI...................................................322
11.4. Создание представлений строк (Row View). Добавление в проект новых файлов ..........338
11.5. Создание интерактивных приложений SwiftUI ...............................................................345
11.6. Использование формы, содержащей представления, кнопку и слайдер .......................353
11.7. Ввод данных с холста или из симулятора ........................................................................354
11.8. Настройка изображений для проекта SwiftUI ..................................................................357
11.9. Переключение между изображениями с помощью элемента Toggle.............................360
11.10. Использование вертикальных стеков при создании проектов для iOS,
содержащих изображения и текст...................................................................................362
11.11. Использование в SwiftUI стеков VStack, HStack и ZStack .............................................365
11.12. Проект с использованием географической карты (MapView), изображения
и текста ..............................................................................................................................368
11.13. Использование структуры ForEach для создания представлений ...............................375
11.14. Cкроллинг представлений ...............................................................................................378
11.15. Добавление и удаление представлений с помощью модификатора transition().
Библиотека модификаторов.............................................................................................380
11.16. Интерактивный переход между представлениями ........................................................383
11.17. Создание пользовательских модальных представлений...............................................384
11.18. Графические средства SwiftUI.........................................................................................386
11.19. Создание проекта SwiftUI для реализации анимации ...................................................389
11.20. Использование на SwiftUI декларативных функций .....................................................393
Приложение. Решения упражнений и ответы .......................................................400
Упражнения 2.5............................................................................................................................400
Упражнения 2.7............................................................................................................................401
Упражнения 3.9............................................................................................................................401
Упражнения 4.3............................................................................................................................403
Упражнение 4.5............................................................................................................................406
Упражнения 7.4............................................................................................................................407
Упражнения 7.7............................................................................................................................409
Упражнения 7.9............................................................................................................................410
Упражнения 7.11..........................................................................................................................412
Предметный указатель...............................................................................................413
<0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0>
ВВЕДЕНИЕ
Одна из причин, по которой я поклонник создания вещей
простыми, компонуемыми и расширяемыми способами,
заключается в том, что это позволяет использовать их
по-новому.
Крис Латтнер (запись в «Твиттере»)
Начиная изучать программирование для iOS и macOS c помощью Swift 5.5 и
SwiftUI, а также технику создания прикладных приложений, необходимо осозна-
вать некоторые важные моменты. Прежде всего надо понимать, что Swift — это
просто язык программирования. Новый, невероятно перспективный язык, но для
создания полезных и востребованных пользовательских приложений требуется
также освоить и инструменты интерфейса. В их роли выступают два фреймворка
(две платформы): UIKit и SwiftUI. Каждый из них позволяет разрабатывать краси-
вые приложения, но между ними есть и различия.
Фреймворк UIKit был представлен в январе 2007 года Стивом Джобсом, когда он
презентовал iPhone с операционной системой iOS. Этот фреймворк используется и
сейчас во многих разработках. UIKit позволяет писать код для создания мобильно-
го приложения на iOS. Макеты разрабатываемого приложения здесь создаются и
просматриваются при помощи разработчика интерфейса (Interface Builder) и так
называемых раскадровок (Storyboards), которые организуют работу экранов в при-
ложении. Процесс разработки при этом довольно трудный: надо перетаскивать
мышью элементы управления (кнопки, текстовые поля и т. п.) на контроллер
Interface Builder, писать для них код в редакторе кода, а затем еще и устанавливать
связь между кодом и элементом.
Когда в мае 2019 года появился фреймворк SwiftUI, ситуация с разработкой прило-
жений существенно улучшилась. В SwiftUI нет контроллеров, а вместо них исполь-
зуются представления (Views), и теперь не надо перетаскивать элементы из биб-
лиотеки элементов в приложение. Достаточно установить курсор мыши в то место
редактора кода, куда должен быть помещен код для обеспечения работы элемента,
щелкнуть мышью на нужном элементе в библиотеке, и появятся и заготовка кода,
и сам элемент как на холсте, так и в симуляторе. При работе приложения код и соз-
даваемый пользовательский интерфейс всегда синхронизированы, то есть разра-
ботчик имеет немедленную обратную связь, поскольку, изменяя код, он сразу
видит изменения в интерфейсе. Более того, теперь разработчик может использовать
декларативный синтаксис Swift.
Еще один важный момент состоит в понимании того, для каких целей создается
мобильное приложение. Существует множество жизненных ситуаций, где могут
Введение 9
помочь такие приложения, и хотя появилось немало фрилансеров и компаний, за-
нимающихся подобными разработками, они никогда не смогут решить все непре-
рывно возникающие проблемы текущей действительности. На эту тему есть инте-
ресный пример, рассмотренный автором весьма содержательных книг по iOS-
программированию из Гонконга Саймоном Энджи (Simon Ng). Он рассказал о том,
как один пилот из авиакомпании столкнулся с тем, что имеются десятки тысяч со-
кращенных авиационных терминов, разобраться в которых трудно даже опытному
пилоту со стажем более 20 лет. Чтобы понять каждое значение сокращения, необ-
ходимо копаться в словарях, и это очень затрудняет работу. Он создал мобильное
приложение для пилотов, которое позволяло быстро расшифровывать сокращения
технических терминов. Его приложение оказалось очень востребованным.
Разобраться в понимании как описанных, так и других важных моментов, вам по-
может эта книга. Здесь рассмотрен один из самых современных языков программи-
рования — Swift 5.5, в котором обеспечивается поддержка параллельного и асин-
хронного кода, дающая возможность существенным образом повысить производи-
тельность создаваемых приложений и упростить их разработку.
Первая версия Swift 1.0 была представлена в июне 2014 года на конференции
WWDC 2014 (Worldwide Developer Conference) в качестве быстрого, компактного и
безопасного языка, позволяющего создавать программы для устройств Apple. На
этой же конференции было распространено и бесплатное руководство по использо-
ванию Swift, содержащее 500 страниц. Создателем Swift является Крис Латтнер
(Chris Lattner), начавший работать над ним в 2010 году. Первоначально предпола-
галось назвать новый язык Shiny (солнечный), однако позже стали использовать
название Swift (стриж). Латтнер, хотя и работал в компании Apple, но разрабатывал
язык в одиночестве в свободное от работы время. Это обстоятельство показывает,
что, вопреки существующему мнению, будто современные серьезные разработки
по силам только большой команде профессионалов, тем не менее есть талантливые
люди, способные справляться с подобными вызовами безо всяких советов и под-
держки. Опираясь на уже существующие языки, Крис Латтнер использовал много
новых идей, позволивших Swift за очень короткое время встать в один ряд с такими
языками, как С++, Objective-C, С#, Python, и даже превзойти их. Сейчас считается,
что язык Swift изменяет программирование, поскольку предлагает простые средст-
ва для реализации больших возможностей. Латтнер впервые уделил серьезное вни-
мание интерактивным системам, понимая их важность при создании и отладке про-
грамм. На своей домашней странице в «Твиттере» он писал: «Возможности Xcode
Playgrounds и REPL были моей личной страстью, направленной на то, чтобы сде-
лать программирование более интерактивным и доступным. Команды Xcode и
LLDB проделали феноменальную работу, превратив сумасшедшие идеи во что-то
действительно великое». Спустя два года Крис Латтнер перешел в компанию Tesla
Autopilot Software, а разработкой Swift в Apple стал руководить Тед Кременек (Ted
Kremenek), и его вклад также вызывает большое уважение. Между тем и сам Латт-
нер продолжает свое участие в модернизации языка Swift.
Swift стал первым в мире языком программирования, созданным на основе прото-
колов. Помимо этого, Swift характеризуется открытым исходным кодом (Open
10 Введение
Source), в котором сочетаются средства объектно-ориентированного программиро-
вания (ООП) и протокольно-ориентированного программирования (POP) с мощны-
ми возможностями парадигм функционального программирования (FP). Swift не
создавался как полностью функциональный язык, подобно, например, Haskell или
Clojure, но он имеет инструменты, позволяющие создавать программы, используя
декларативный подход функционального программирования. Мало известно язы-
ков программирования (если они есть вообще), в которых забота о безопасности
работы программы в многопоточной системе начинается сразу же после введения
первых операторов программы. Если оператором var декларируется какая-то пере-
менная, и дальше в программе ей не присваивается значений, то компилятор выда-
ет сообщение, в котором указывается, что эту переменную надо сделать констан-
той, то есть объявить ее оператором let. Такая замена повысит безопасность рабо-
ты, поскольку константы не позволяют в многопоточной системе получать
побочные эффекты.
На Swift создаются приложения под macOS, iOS, tvOS и watchOS, его также можно
применять и для разработки веб-приложений в Ubuntu Linux. Не следует думать,
что Swift предназначен лишь для реализации игровых приложений, — его возмож-
ности намного шире: он используется при создании больших инженерных проектов
в области систем искусственного интеллекта, в сфере управления летательными
аппаратами, при организации сложных финансовых потоков и т. п.
Книг по программированию на Swift написано уже немало — достаточно упомя-
нуть 500-страничную бесплатную документацию по этому языку, представленную
компанией Apple (по адресу: https://swiftbook.ru/contents/doc/ имеется ее перевод
на русский язык), а также третье издание книги «Learning Swift» Джона Меннинга
(Jon Manning), Парижа Баттфилд-Аддисона (Paris Buttfield-Addison) и Тима Ньюд-
жента (Tim Nugent), выпущенное издательством O’Relly Media в 2018 году. Кроме
этого, в Интернете можно найти лекции курсов Пола Хэгерти (Paul Hegarty), про-
читанные им в Стэнфордском университете (имеется и их неавторизованный пере-
вод на русский язык).
По протокольно-ориентированному программированию тоже появилось немало
работ, среди которых следует отметить очень качественные книги Джона Хоффма-
на (John Hoffman).
Swift является частью Xcode — интегрированной cреды разработки (IDE) про-
граммного обеспечения для платформ macOS, iOS, watchOS и tvOS. Apple постоян-
но совершенствует операционную систему macOS и среду Xcode. Так, в конце
2019 года выпущены новая версия macOS Catalina (версия 10.15), крупный релиз
языка Swift (версия 5.1) и версия Xcode 11.1. Над этой версией работы велись более
четырех лет. Ее существенным достижением является инновационный фреймворк
SwiftUI, позволяющий унифицировать разработку приложений на iOS, macOS,
watchOS и tvOS. Важным преимуществом нового подхода стал декларативный син-
таксис, который используется при создании приложений. Один из разработчиков
Xcode — Мэтт Рикетсон (Matt Ricketson), выступая на конференции WWDC 19,
предложил для понимания декларативного подхода аналогию создания компьютер-
ного приложения с приготовлением тоста из авокадо.
Введение 11
Основываясь на подобной аналогии, можно обратиться к такому примеру. Пусть
некий заказчик собирается построить дом. Он хорошо разбирается в строительстве
и хочет нанять бригаду рабочих. Каждому рабочему он собирается объяснить,
что тот должен делать. Одному он детально расскажет, как ему вырыть котлован
под фундамент. Другому предоставит подробные инструкции по заливке бетона.
Третьему даст указания, как готовить доски для стен и т. д. В результате дом будет
построен. Это императивный подход к решению задачи.
А вот ориентирующийся на декларативный подход заказчик не станет давать ис-
полнителям подробные инструкции. Он найдет профессионального эксперта и объ-
яснит ему в общих чертах, какой он хочет иметь дом. Вся остальная работа будет
выполняться экспертом и его командой.
Apple непрерывно совершенствует свои разработки. Спустя пять лет после появле-
ния первой версии Swift была выпущена пятая, юбилейная, версия Swift и затем
еще пять версий:
Swift 5.0 — апрель 2019 года;
Swift 5.1 — сентябрь 2019 года;
Swift 5.2 — март 2020 года;
Swift 5.3 — сентябрь 2020 года;
Swift 5.4 — апрель 2021 года.
Наконец, в сентябре 2021 года Apple объявила о выпуске новой версии — Swift 5.5,
которая открывает уникальные возможности для разработчиков, использующих
язык Swift. При этом язык Swift 5.5 может работать только с последними версиями
операционной системы macOS: Bug Sur и Monterey.
Имеется и еще одно важнейшее отличие от предыдущих разработок Xcode: SwiftUI
позволяет не использовать Interface Builder и раскадровки, и более не требуются
детальные инструкции по разметке создаваемого интерфейса. Редактировать ин-
терфейс стало проще, поскольку он теперь создается в коде: разработчик пишет код
и моментально видит, что получается на холсте, поскольку код и холст синхрони-
зированы. Те пользователи, которые имеют опыт работы с Interface Builder и редак-
тировали раскадровки вручную, хорошо знают, как это сложно. Раскадровки со-
держат коды языка XML, который утомительно читать и редактировать. В них
трудно вносить исправления, и если создавалось несколько контроллеров пред-
ставления, то управление таким исходным кодом требовало серьезного внимания и
терпения. Interface Builder и программа на Swift не имеют надежной синхронной
связи, что приводит к таким, например, ситуациям: когда из Interface Builder пере-
таскивается какая-то связь для некоторого действия в коде, все работает нормально,
но если это действие удалить, то функция все равно будет вызываться, хотя ее уже
нет. И разработчику приходится самому устранять возникшие проблемы, посколь-
ку компилятор Swift этого не делает.
С появлением SwiftUI разработчики могут не беспокоиться от том, какой выбирать
дизайн: программный или основанный на раскадровках, поскольку теперь есть
одновременно и то и другое.
12 Введение
Крайне важным является и то обстоятельство, что SwiftUI разработан таким обра-
зом, что пользовательские приложения, созданные на нем, можно интегрировать
в уже существующие представления и модели.
Имеется немало видеороликов на YouTube, объясняющих принципы работы
с Xcode 11, Xcode 13, Swift 5 и Swift 5.5. Они на английском языке, но если про-
сматривать их не в полноэкранном режиме, то можно найти под правой частью
окна видео, правее ссылки СОХРАНИТЬ, три точки, после щелчка на которых вы-
падает меню, в котором надо выбрать опцию Посмотреть расшифровку видео
(рис. В.1). После этого в правой части экрана станет появляться текст. Он на
английском языке, но его можно копировать и переводить.
Рис. В.1. Выпадающее меню окна видео YouTube
В качестве примеров таких видеороликов можно привести серию Пола Хадсона
(Paul Hudson) по адресу: https://www.youtube.com/watch?v=nc-n5Gc8wN0&list.
Или, например, следующие:
https://www.youtube.com/watch?v=vC2BRWrCUlI;
https://www.youtube.com/watch?v=2VGbCaWhlmc.
По проблемам Swift и SwiftUI написано множество статей и книг, а также прово-
дится большое количество конференций, причем не только в США и Европе, но и
по всему миру: в Сингапуре, Гонконге, Индонезии, Японии, Австралии и в других
странах.
Разработчики также могут пользоваться шаблоном Swift Playgrounds для SwiftUI.
К сожалению, в настоящее время наблюдается повальное увлечение мобильными
разработками для iPhone, и большая часть этих материалов посвящена iOS — в свя-
зи с очень большим коммерческим спросом на мобильные разработки. Однако раз-
работки для macOS и watchOS имеют много общего c iOS, и этими материалами
также можно пользоваться при создании приложений для macOS и watchOS.
<0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0>
ГЛАВА 2
ОСНОВЫ ПРОГРАММИРОВАНИЯ
НА ЯЗЫКЕ SWIFT
Язык Swift — это совершенно новый язык программирования. Конечно, он допус-
кает использование технологий объектно-ориентированного программирования, на
которых основан, например, такой язык, как С++, но Swift — не С++. В корпорации
Apple заявляют, что Swift ориентирован не на классы, а на структуры. На конфе-
ренции WWDC 15 было объявлено, что Swift является первым в мире протокольно-
ориентированным языком, а это предполагает совершенно иной стиль программи-
рования (как это работает, рассмотрено в главе 8). Кроме этого, на языке Swift реа-
лизованы возможности декларативного стиля программирования, что особенно
актуально при многопоточном и многоядерном механизме обработки данных, по-
скольку позволяет избежать таких побочных явлений, как гонки или взаимные бло-
кировки (эти вопросы рассмотрены в главе 7). Инновационные технологии SwiftUI,
объявленные на конференции WWDC 19, также были созданы на основе использо-
вания декларативного стиля программирования (описанию технологии SwiftUI по-
священа глава 10).
2.1. Переменные и константы
Переменная (как и константа) — это место в оперативной памяти компьютера, где
хранится некоторое значение. Значение переменной можно изменять в процессе
выполнения программы, но оно теряется при выключении компьютера. Каждая
переменная имеет имя, являющееся адресом ячейки (или ячеек) памяти, где она
хранится, значение и тип.
Значение переменная получает при помощи специального оператора var, называе-
мого оператором присваивания, который имеет вид:
var имя переменной = значение переменной
В этом выражении символ (=) — это не знак равенства, а знак пересылки (присваи-
вания) значения, или, как часто говорят, копирования значения:
var a = 5
print("значение a =",a)
print("тип a =",type(of: a)). // Печать для проверки типа
Поскольку число 5 не имеет здесь дробной части, то компилятор рассматривает его
как целочисленное с типом Int и, хотя целочисленных типов несколько (о типах
50 Часть I. Программирование на Swift 5.5
переменных рассказано далее), но при неявном задании числу всегда назначается
именно тип Int, и на экран будет выдано:
значение a = 5
тип a = Int
Существуют такие переменные, изменение значений которых в программе недо-
пустимо, и если все же такое изменение происходит, то это может вызвать в этой
программе ошибки. Чтобы обезопасить программиста от необходимости отслежи-
вания подобных переменных, используется специальный тип переменной, назы-
ваемый константой. Константе присваивается значение один раз при ее объявле-
нии, и больше ее изменить не удастся. Константа на Swift играет гораздо большую
роль, чем переменная, что связано с обеспечением безопасности выполнения про-
грамм, которой Apple придает очень большое значение.
Для объявления константы используется ключевое слово let. В одном операторе let
(и, соответственно, var) можно объявлять любое число констант (переменных),
разделяя их запятыми:
при неявном объявлении:
let x = 0, z = 7.5, str = "символьная строка"
и при явном:
let t: Int = 0, r: Float = 7, y: String = "string"
Можно создать и любую символьную константу — например, language:
let language = "Swift"
Swift создан как типобезопасный (type-safe) язык, и это означает, что после того,
как константа, переменная или выражение были определены, их тип уже нельзя
изменить. Это очень важно, поскольку позволяет обнаружить несоответствие типов
уже во время компиляции программы. На Swift поддерживается технология выво-
димости (inference) типов, которая обеспечивает получение типов объявляемых
констант, переменных или выражений автоматически, по тому значению, которое
указывается в объявлении, однако это возможно не для всех типов, а только для
четырех следующих:
Int;
Double;
String;
Bool.
Вот примеры объявления таких типов:
let i = 5 // Выводится целый тип Int (нет десятичной точки)
let x = 5.24 // Выводится вещественный тип Double
// (точка после целой части числа)
var y = 25.0 // Выводится вещественный тип Double
let str = "любой текст" // Выводится символьный тип String
let l = true // Выводится логический тип Bool
Глава 2. Основы программирования на языке Swif 51
Подобное объявление весьма удобно для очень большого рода задач, но если тре-
буется какой-то другой тип, то его надо при объявлении указывать явно. Такое объ-
явление называется аннотацией (annotation) типов. Например:
let constant1: Int8 = 25
let constant2: Int16 = 127
let constant: Float = 5.18
var variable: Int64 = 2245
var st: Character = "a"
var name: String
name = "alex"
Кстати, хотя язык Swift и содержит константу pi, но обращаться к ней надо так:
Double.pi. Можно создать эту константу со своим именем pi. Для этого надо напи-
сать:
let pi = Double.pi
print ("pi=", pi)
На экран будет выдано:
pi= 3.141592653589793
Тип переменной нужен для того, чтобы:
выделить размер памяти, который применяется для этого типа;
определить операции, которые могут использоваться с этой переменной.
Swift является сильно типизированным (strong typing) языком со статической ти-
пизацией, в отличие, например, от такого языка, как Python, который имеет сильную
динамическую типизацию. Подобные определения несколько условны, но они озна-
чают, что фактически тип переменной на Swift устанавливается при объявлении
переменной и, как уже было отмечено ранее, не может быть изменен в процессе
выполнения программы. На Python же тип переменной в процессе работы програм-
мы может изменяться. Дело в том, что Python использует переменные ссылочного
типа, и когда ссылка устанавливается на другое значение, тип которого отличается
от предыдущего, то соответственно изменится и тип переменной. Например, на
рис. 2.1 показана программа на Python, где переменная a сначала принимает значе-
Рис. 2.1. Программа на Python: тип переменной a изменяется по ходу программы
52 Часть I. Программирование на Swift 5.5
ние целого типа (int), а затем эта же переменная получает символьное значение
(str), и программа продолжает нормально работать. На Swift такое изменение типа
недопустимо, что и показано на рис. 2.2. Здесь сначала переменная a получает зна-
чение целого типа, а потом ей пытаются присвоить символьное значение. Компиля-
тор Swift в этой строке выдает ошибку: Cannot assign value of type 'String' to type
'Int' (Невозможно присвоить значение типа 'String' типу 'Int').
Рис. 2.2. Программа на Swift: тип переменной a изменить невозможно
При явном объявлении переменной надо указать и ее начальное значение, и ее тип.
Но сначала надо придумать переменной имя. Для математических задач иногда ис-
пользуются имена из одной буквы — например x, y, z и т. п. Однако в большинстве
приложений лучше задействовать имена, помогающие понять назначение перемен-
ной, — например, windowHeight или numberOfLetters. При таком способе несколько
слов соединяются вместе, и каждое очередное слово (кроме первого) начинается
с заглавной буквы, — этот способ называется CamelCase (верблюжий вид). Надо
также заметить, что Swift является чувствительным к регистру (case sensitivity),
и поэтому на нем переменные case и Case будут разными. Кроме имени, в операто-
ре var надо указать тип переменной и задать начальное значение — например, так:
var value:Int = 0
Это явное объявление переменной, но ее можно объявить и неявно, указав только
начальное значение, — например:
var value = 0
Неявный способ объявления переменной очень распространен, поскольку в этом
случае компилятор сам назначит ей тип, исходя из представленного значения. Что-
бы узнать, какой тип был переменной назначен, его можно вывести на печать (на
экран), используя стандартную функцию Swift с именем type:
let value = 7
print (type (of: value))
В приведенном примере на экран будет выведен тип: Int.
Недостаток неявного объявления заключается в том, что разработчик не может за-
давать тот тип, который хочет, поскольку если указать целое число (как в преды-
дущем примере), то всегда будет устанавливаться тип Int (а не какой-либо другой
целочисленный тип), а если вещественное, как, например, здесь:
Глава 2. Основы программирования на языке Swif 53
let real = 2.45
print (type (of: real))
то будет установлен тип Double (а не Float).
Заметим, что в вещественных числах Double и Float целая часть числа отделяется
от дробной части точкой.
Если присвоить переменной символьные значения, то ей всегда назначается тип
String, даже если задан всего один символ, — например:
let str = "s"
print (type(of: str))
Здесь переменной будет назначен тип String (а не Character).
Количество байтов, которое отводится для переменной определенного типа (но не
экземпляра класса), можно узнать, используя оператор:
let t = MemoryLayout<T>.size
где T — тип переменной. Например, чтобы узнать количество байтов, отведенных
для типа Double, надо выполнить операторы:
let t = MemoryLayout<Double>.size
print("t=", t)
На экран будет выведено значение:
t=8
Можно узнать также и размер конкретной переменной — например, x:
var x: Int8 = 5
let s = MemoryLayout.size(ofValue: x)
print("количество байтов переменной x =",s)
В этом примере на экран будет выведено количество байтов переменной:
x=1
Если оператор присваивания имеет вид:
variableX = variableY
то в этом случае значение переменной variableY пересылается (копируется) в пере-
менную variableX, но надо помнить, что это же самое значение остается и в пере-
менной variableY.
Типы переменных и их размеры приведены в табл. 2.1.
Таблица 2.1. Типы переменных и их размеры
Тип Описание Примеры
Int Целое число, размер которого 4 байта, значение 56
между 2147483648 и 2147483647. На 32-разрядной платформе
-72
этот тип эквивалентен Int32
<0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0>
ГЛАВА 7
ФУНКЦИОНАЛЬНОЕ
ПРОГРАММИРОВАНИЕ НА SWIFT
Основные характеристики языка программирования определяются архитектурой
компьютера, для которой создан этот язык. В прошедшие десятилетия общеприня-
той считалась неймановская архитектура, названная так в честь ее автора Джона
фон Неймана. В компьютере, построенном на основе этой архитектуры, команды
программы и данные хранятся в одной и той же оперативной памяти (RAM).
В нем также имеется центральный процессор (CPU), который забирает данные из
памяти, выполняет команды для обработки этих данных и возвращает результат
снова в оперативную память. Языки программирования для таких компьютеров на-
зываются императивными.
Основными элементами императивных языков являются переменные, определяе-
мые ячейками памяти, операторы присваивания, основанные на операции пересыл-
ки данных, а также циклы, реализующие итеративное повторение операций, что,
собственно, и обеспечивает наибольшую эффективность таких языков.
Усложнение задач приводило к быстрой эволюции императивных языков, и в нача-
ле 1980-х была разработана методология объектно-ориентированного программи-
рования (ООП). Она базировалась на таких понятиях, как абстракция данных, по-
зволяющая инкапсулировать в один объект данные и программы для их обработки,
а также наследование и динамическое связывание данных. Техника наследования
давала возможность многократно использовать существующие объекты, что серь-
езно повышало эффективность создания программ и способствовало популярности
ООП. Для упрощения создания программ их стали разбивать на более мелкие час-
ти — такие, как классы, интерфейсы и методы. Однако при этом одновременно
возникли и трудности, поскольку собирать отдельные части программ для их со-
вместной работы оказалось крайне сложной задачей.
И все же наибольшей проблемой использования императивных языков стало значи-
тельное снижение надежности работы программ, созданных на этих языках. Связа-
но это с появлением многоядерных процессоров. Поскольку был достигнут практи-
ческий предел роста тактовой частоты процессоров (скорости выполнения опера-
ций), то для дальнейшего увеличения их производительности специалисты по
аппаратному обеспечению начали добавлять в процессоры все больше и больше
ядер, что потребовало создания очень сложного многопоточного механизма много-
ядерной обработки данных. Но для императивных языков многопоточность вызы-
вает возникновение побочных эффектов, и надежность разработанных на этих язы-
198 Часть I. Программирование на Swift 5.5
ках программ существенным образом падает. Одним из выходов из тупика много-
ядерности стало использование функционального подхода, базирующегося на идеях
декларативного программирования, что и легло в основу языка Swift.
Декларации легко отследить, и они не так многословны, как императивные про-
граммы. При этом Swift предлагает строить приложения, используя типы, — такие,
как структуры, перечисления, протоколы, функции и замыкания. Да, замыкания
также могут быть типами и применяться в этом качестве. Swift — в отличие от
многих других языков программирования (таких, например, как Ruby) — обеспе-
чивает для типов надежную безопасность. Swift также способен всякий раз, когда
это возможно, отслеживать определение неизменяемых констант вместо перемен-
ных. Неизменяемость — это одно из основных понятий функционального про-
граммирования. Очень важным является и то, что ключевое слово (оператор) let
можно использовать не только для констант, но и для всех пользовательских типов,
таких как коллекции, структуры и перечисления.
Swift предоставляет мощные средства, которые позволяют задействовать различ-
ные технологии функционального программирования. Как известно, функциональ-
ное программирование — это не язык, а методология программирования, при кото-
рой сложные задачи разбиваются на более простые, и достигается это при помощи
функций, которые наделены в Swift очень большими возможностями. В последние
годы Apple уделяет много внимания многопоточным задачам, в которых два или
более потока могут одновременно обращаться к одной и той же переменной и из-
менять ее не так, как предполагалось при написании программы. Это приводит
к плохо устраняемым побочным эффектам, называемым условиями гонок (race
conditions), или взаимным блокировкам (deadlocks) — иногда их называют также
мертвыми точками. Гонки известны давно и не только из программирования. Ко-
гда в электронике начали создаваться первые микросхемы, то там обнаружился по-
бочный эффект, при котором два сигнала могли поступать на один и тот же эле-
мент схемы, причем сначала должен был поступить сигнал A и заставить этот эле-
мент работать, а спустя время прийти сигнал B, который отключал этот элемент,
и схема нормально функционировала. Однако в ряде случаев сигнал B на какие-то
доли секунды приходил раньше, и работа микросхемы нарушалась. Происходило
это либо вследствие некачественного материала микросхемы, либо из-за программ-
ной ошибки, которую разработчики не заметили. Для борьбы с гонками стали рас-
сматривать схему как граф, и чтобы устранить гонки, этот граф следовало проекти-
ровать в качестве подграфа многомерного куба. В программировании имеет место
очень похожее явление, приводящее к изменению локальных переменных функции,
однако функции на Swift оснащены механизмом, позволяющим запретить нежела-
тельное изменение переменных. Позже этот механизм будет рассмотрен.
В функциональном подходе ключевую роль играет понятие математической
функции. Одним из первых функциональных языков стал язык LISP, созданный
в 1959 году Джоном Мак-Карти (John McCarthy) во время его работы в проекте,
посвященном искусственному интеллекту. Программа на функциональном языке
отличается от программы на языке императивном. Вычисления в такой программе
производятся путем применения функций к аргументам, при этом не требуются ни
Глава 7. Функциональное программирование на Swift 199
изменяемые переменные, ни операторы присваивания, ни циклы, поскольку итера-
ции осуществляются при помощи рекурсивных вызовов функций. Язык LISP преоб-
ладал в системах разработки искусственного интеллекта более 20 лет, однако позже
стали появляться его диалекты, содержащие элементы императивного стиля про-
граммирования.
Другим популярным чисто функциональным языком является язык Haskell
(Thompson, 1996). Этот язык также не имеет переменных, операторов присваивания
и никаких других императивных свойств. Интересной особенностью языка Haskell
стало использование так называемых ленивых вычислений, суть которых состоит
в том, что ни одно выражение не вычисляется до тех пор, пока не потребуется ис-
пользование его значения (ленивые вычисления называются также отложенными
вычислениями). Язык Haskell не имеет никаких побочных эффектов, связанных
с многопоточностью (concurrency).
Резюмируя сказанное, отметим, что проблемы, сопутствующие параллельно рабо-
тающим задачам, возникают из-за того, что некоторые ресурсы оказываются дос-
тупны для задач, которым они не предназначены. Например, некоторая переменная
должна изменяться задачей А, но оказывается, что она была изменена задачей B
раньше, чем это сделала задача А. Возникает ошибка, которую трудно обнаружить
и которая приводит к неверной работе программы. Обычно выделяются три таких
случая:
гонки (race condition) — эта ошибка вызывается ошибкой разработки приложе-
ния, при которой доступ к некоторому ресурсу разные задачи должны получать
в определенном порядке, но этот порядок нарушается;
инверсия приоритетов (priority inversion) — возникает в системе, когда задачи
имеют разные приоритеты и задача с более высоким приоритетом блокирует за-
дачи с менее высоким приоритетом;
взаимная блокировка (deadlock) — возникновение состояния многопоточной
системы, при котором несколько различных задач ожидают доступа к ресурсам,
которые эти же задачи и заняли.
Несмотря на то что эти проблемы давно известны, их устранение очень трудно,
а возникают они также и потому, что им до сих пор уделяется недостаточно внима-
ния.
Таким образом, при функциональном программировании (ФП), реализующем дек-
ларативный стиль программирования:
не допускается использование переменных, объявленных оператором var (мож-
но использовать только константы, декларированные оператором let);
не допускается использование оператора присваивания;
все вычисления должны выполняться за счет применения функций высших по-
рядков (т. е. функций, аргументами которых могут быть другие функции);
не допускается применение циклов, вместо которых используются итеративные
процессы, определяемые с помощью рекурсивных вызовов функций.
200 Часть I. Программирование на Swift 5.5
Язык Swift поддерживает три вида функций для реализации парадигмы функцио-
нального программирования:
функции первого порядка (first-class functions) — это такие функции, которые
можно использовать как обычные значения, т. е. их можно присваивать пере-
менным и константам, передавать и возвращать;
функции высшего порядка (higher-order functions) — это функции, которые могут
получать другие функции в качестве своих параметров;
замыкания (closure) — это анонимные функции, которые создаются в том месте,
где они используются, и берут константы и переменные из того контекста, где
они созданы.
Разработчик может, соблюдая все правила декларативного стиля, создавать и свои
собственные функции первого и высшего порядков, что позволяет значительно
расширять возможности ФП.
Чтобы понять, как создаются и используются такие функции, рассмотрим три воз-
можных случая:
1. Функцию можно использовать как переменную.
2. Параметрами функции могут быть другие функции.
3. Возвращаемое значение функции также может быть функцией.
Это можно показать на простом примере. Пусть известны два числа, определяющие
длины сторон прямоугольного треугольника, и требуется, используя теорему Пи-
фагора, найти длину третьей стороны. Далее представлены варианты кода для всех
трех случаев:
// Создание функции pythagoras с двумя параметрами
func pythagoras(a: Double,b: Double) -> Double{
return a * a + b * b}
// 1. Определение переменной side, которая может хранить функцию с двумя
параметрами
var side: (Double, Double) -> Double
side = pythagoras // Использование функции pythagoras
print("Длина третьей стороны треугольника=",sqrt(side(2,4)))
//-----------------------------------------
// 2. Использование других функций в качестве параметров
func sq(x: Double) -> Double{
return sqrt(x)
}
// Параметром функции side является функция pythagoras
func side(a: Double,b: Double,pythagoras: (Double)-> Double) -> Double{
return pythagoras( a * a + b * b)
}
print("Длина третьей стороны треугольника=",side(a: 2.0,b: 4.0,pythagoras: sq))
//---------------------------------------------------------------------
// 3. Возвращаемое значение – другая функция
print("Возвращаемым значением функции outside является функция pythagoras")
Глава 7. Функциональное программирование на Swift 201
func outside(a: Double) ->(Double) -> Double{
func pythagoras(b: Double) ->Double{
}
return pythagoras
}
var side1 = outside(a: 2)
print("Длина третьей стороны треугольника=",side1(4))
В каждом из этих трех случаев на экран будет выдано:
Длина третьей стороны треугольника= 4.47213595499958
Конечно, в библиотеке Swift имеется очень много функций, и конкурировать с ни-
ми едва ли возможно, поэтому для достижения серьезных результатов в функцио-
нальном программировании эти функции надо освоить и использовать, а также
встраивать их в свои собственные задачи.
Полностью декларативный стиль пока еще не реализован, поскольку остаются
задачи, где требуются инструменты императивного программирования. К таким
задачам относятся задачи записи данных в файл, в базу данных и отправки данных
по почте. К сожалению, эти задачи часто служат ресурсами для организации не-
санкционированного доступа.
В качестве примера, позволяющего увидеть возможности функционального под-
хода, рассмотрим библиотечную универсальную функцию sequence(first:next:)
с двумя параметрами: first и next. Параметр first не изменяется и служит началь-
ным значением для возвращаемой последовательности. Второй параметр — next —
является замыканием, которое выполняет ленивые (lazy) вычисления для рекурсив-
ного образования последующих элементов. Воспользуемся сначала функцией
sequence для создания массива, применив при этом метод prefix(n), который по-
зволяет создавать только первые n элементов массива, а также метод map с замыка-
нием для отображения элементов массива. Поскольку функция sequence имеет уни-
версальный тип, то мы можем создать массивы разных типов:
let d1 = sequence(first: 1, next: {$0+1})
.prefix(5).map{$0}
print("Массив целых чисел=",d1)
На экран будет выдано:
Массив целых чисел= [1, 2, 3, 4, 5]
let d2 = sequence(first: sin(1.0), next: {sin($0*$0)})
.prefix(5).map{$0}
print("Массив вещественных чисел",d2)
На экран будет выдано:
Массив вещественных чисел: [0.8414709848078965, 0.6503715160821458,
0.4104824784759861, 0.16769970583556823, 0.02811948431348801]
let d3 = sequence(first: "ab", next: {$0 + "c"})
.prefix(5).map{$0}
print("Символьный массив",d3)
202 Часть I. Программирование на Swift 5.5
На экран будет выдано:
Символьный массив= ["ab", "abc", "abcc", "abccc", "abcccc"]
Рассмотрим теперь, как с помощью функции sequence можно найти наибольший
общий делитель для двух положительных целых чисел с помощью алгоритма Евк-
лида.
Пусть a и b — два положительных целых числа, и допустим, что a > b. Наибольший
общий делитель для них можно найти прямым перебором. Примем, например, что
a = 276 и b = 84. Найдем все делители этих чисел и выберем из них наибольший
общий делитель:
a = 276, делители: 1, 2, 3, 4, 6, 12, 23, 46, 69, 92, 138, 276
b = 84, делители: 1, 2, 3, 4, 6, 7, 12, 14, 21, 28, 42, 84
Отсюда следует, что наибольший общий делитель 12.
Применим теперь алгоритм Евклида: cначала разделим a на b и получим первый
остаток от деления: r1 ( r1 < b). Затем разделим b на r1 и получим второй остаток:
r2 ( r2 < r1 ). Далее делим r1 на r2 и получаем: r3 ( r3 < r2 ). Продолжаем деление до
тех пор, пока не получим остаток rk , равный нулю, а он обязательно будет полу-
чен, поскольку остатки убывают. Тогда остаток rk −1 и будет наибольшим общим
делителем.
Итак, для нашего примера:
1. Делим a = 276 на b = 84 и получаем остаток r1 = 24 (т. к. 276 = 84 · 3 + 24).
2. Делим b = 84 на r1 = 24 и получаем остаток r2 = 12 (т. к. 84 = 24 · 3 + 12).
3. Делим r1 = 24 на r2 = 12 и получаем остаток r3 = 0 (т. к. 24 = 12 · 2 + 0).
4. Поэтому r2 = 12 и будет искомым наибольшим общим делителем.
Теперь напишем функциональную программу для алгоритма Евклида — она состо-
ит из одной функции sequence и использует метод prefix — для указания количест-
ва рекурсий и метод filter — чтобы из всех получаемых кортежей выбрать только
тот, у которого первый элемент не равен нулю, а второй — равен. Для нашего при-
мера будут порождаться следующие кортежи из двух элементов:
[(276.0, 84.0), (84.0, 24.0), (24.0, 12.0), (12.0, 0.0)]
В четвертом кортеже (12.0, 0.0) второй элемент — ноль, и поэтому первый эле-
мент и определяет наибольший общий делитель.
В качестве аргументов функции sequence мы будем брать не одиночные элементы,
а пары элементов, т. е. кортежи из двух элементов. Кортеж для первого аргумента
(first) первым элементом имеет a, а в вторым — b, т. е. (a, b). Кортеж для второго
аргумента (next) своим первым аргументом берет второй элемент из предыдущего
кортежа, а его второй элемент представляет собой остаток от деления первого эле-
мента на второй (оба берутся из предыдущего кортежа). Поскольку для определе-
Глава 7. Функциональное программирование на Swift 203
ния элементов next используется замыкание, то для них можно использовать ано-
нимные обозначения $0 и $1, где $0 — это первый элемент кортежа, а $1 — его вто-
рой элемент. Здесь надо отметить, что функция sequence имеет аргументы универ-
сального типа, поэтому использовать операцию $0%$1 для определения
остатка нельзя, т. к. она применима только для целых чисел (а функция sequence —
поскольку она является Generic Function — должна работать с любым типом), по-
этому для определения остатка необходимо использовать метод truncatingRemainder.
В результате функциональная программа имеет вид:
let gcd = sequence (first: (276, 84), next:
{($1,$0.truncatingRemainder(dividingBy: $1))} )
.prefix(10).map{$0}
.filter {$0 != 0.0 && $1 == 0.0}
print("Наибольший общий делитель =",gcd[0].0)
и на экран будет выдано:
Наибольший общий делитель = 12.0
7.1. Простые математические функции
Математическая функция представляет собой отображение (mapping) одного
множества, называемого областью определения (domain), в другое множество, на-
зываемое множеством значений (codomain). Другими словами, каждому элементу
из множества А назначается уникальный элемент из множества В, и семейство всех
таких назначений называется функцией из А в В. Символически это обозначается
следующим образом:
f : A → B,
где для каждого a ∈ A назначается единственный элемент из В, который обознача-
ется f(a) и называется образом a по f.
Функцию можно задать либо формулой, либо таблицей, либо графически. Напри-
мер, рассмотрим отображение каждого вещественного числа в число, меньшее дан-
ного на единицу. Это можно записать в виде формул:
f(x) = x – 1
или
y = x – 1.
В первой записи x называется переменной, а символ f обозначает функцию. Во вто-
рой записи x называется независимой переменной, а символ y — зависимой пере-
менной, поскольку величина y зависит от величины x.
На языке Swift функции являются функциями первого класса, а это означает, что их
можно использовать как переменные, т. е. передавать их в качестве аргументов
в другие функции или возвращать как результаты других функций.
Для понимания разницы между императивным и декларативным подходами обыч-
но рассматриваются простые примеры, связанные с обработкой массивов. Пусть,
204 Часть I. Программирование на Swift 5.5
например, имеется массив с именем massiv из четырех элементов, которые нужно
уменьшить на 1.
При императивном подходе нам необходимо создать изменяемый массив
modifiedMassiv (при помощи оператора var), после чего выполнить команды цикла
для вычитания единицы из каждого элемента этого массива:
let massiv = [5, 7, 1, 18]
// Императивный подход
var modifiedMassiv: [Int] = []
for number in massiv {
modifiedMassiv.append(number - 1)
}
print("Измененный массив=",modifiedMassiv)
На экран будет выдано:
Измененный массив= [4, 6, 0, 17]
При декларативном подходе мы создадим неизменяемый массив otherModifiedMassiv
(оператором let), после чего используем функцию map и замыкание, — т. е. объяв-
ляем, как должны отображаться числа массива, из которых вычитается единица:
// Декларативный подход
let otherModifiedMassiv = massiv.map({number in number - 1})
print("Другой массив=",otherModifiedMassiv)
На экран будет выдано:
Другой массив= [4, 6, 0, 17]
Разумеется, то же самое можно сделать и при использовании структуры:
struct Mas{ // Определение структуры
let massiv = [5, 7, 1, 18]
func MasMod() {
let otherModifiedMassiv = massiv.map({number in number - 1})
print("Другой массив=",otherModifiedMassiv)
}
}
let mas = Mas() // Создание экземпляра структуры
mas.MasMod()
На экран будет выдано:
Другой массив= [4, 6, 0, 17]
Оба эти подхода дают один и тот же результат — разница лишь в том, что в первом
случае наша программа уязвима для побочных эффектов (таких, как, например,
гонки), а во втором она надежно защищена технологией функционального подхода.
При функциональном подходе требуется использовать чистые (pure) функции, ко-
торые не зависят от данных вне своего тела и сами не изменяют никаких данных за
своими пределами. Это свойство называется ссылочной прозрачностью (referential
Глава 7. Функциональное программирование на Swift 205
trancparency) и служит основой применения формальных методов проверки. При
программировании на ООП значение блока зависит от контекста или его состояния,
что не позволяет ни переставлять блоки, ни заменять их. При функциональном про-
граммировании (ФП) можно заменять одни блоки кода другими, не заботясь об их
порядке или состоянии. Поэтому чистые функции позволяют не использовать клас-
сы и объекты (как в ООП), а создавать приложения с функциями.
ФП позволяет успешно применять ленивые вычисления. При этом для создания
ленивой версии коллекции служит ключевое слово lazy. Технология ленивых вы-
числений декларирована в стандартной библиотеке Swift и может быть использова-
на для таких функций, как map и filter. Например, для массива из предыдущего
примера можно получить только один последний элемент, а остальные элементы
отображаться не будут:
let massiv = [5, 7, 1, 18]
let lastN= massiv.lazy.map({$0 - 1}).last!
print("Последнее число массива=",lastN)
Последнее число массива= 17
Подобным же образом мы можем отобразить наибольший элемент этого массива:
let massiv = [5, 7, 21, 18]
let lastN = massiv.lazy.map({$0 - 1}).max()!
print("Наибольший элемент=",lastN)
Наибольший элемент= 20
На языке Swift также допускается использование безымянных функций. Метод
определения безымянной функции основан на лямбда-исчислении, разработанном
Алонзо Черчем (Alonzo Church) в 1941 году. Параметр и отображение, соответст-
вующее функции, задается лямбда-выражением (lambda expression). Фактически
лямбда-выражение и является функцией.
Например, пусть имеется выражение:
(x)x*x
До его вычисления параметр x представляет собой любой элемент множества опре-
деления функции, однако во время вычисления он связывается с конкретным
элементом этого множества. При вычислении лямбда-выражения для заданного
параметра считается, что выражение применяется к этому параметру. Пример ис-
пользования лямбда-выражения для выражения: (x)x*x показано в следующем
примере:
(x)x*x(5)
Результатом этого выражения будет 25.
В общем случае лямбда-выражения могут иметь и несколько параметров.
Глава 10. Создание проектов в редакторе Interface Builder на основе интерфейса Storyboard 289
Навигатор Контроллер Инспектор
проекта представления атрибутов
Стрелка указывает а
на начальный контроллер
б
Рис. 10.3. а — окно редактора Interface Builder; б — выбор поддерживаемого размера iPhone и iPad
Рис. 10.4. Линейка с кнопками управления проектом
290 Часть II. Технологии разработки приложений в среде Xcode 11.2 и 13.1
Рассмотрим некоторые элементы iOS, применяемые в процессе создания раскад-
ровки:
Label (UILabel) — метка, предназначенная для вывода текста из программы на
раскадровку (storybord);
Button (UIBatton) — командная кнопка, предназначенная для создания и запуска
метода программного кода;
Segmented Control — отображает горизонтальную группу элементов;
Text Field — текстовое поле, содержащее редактируемый текст, служит для вво-
да текста в программный код;
Slider — горизонтальная полоса, позволяющая получать величины из заданного
диапазона;
Switch — позволяет переключаться между включенным и выключенным состоя-
ниями;
Activity Indicator View — представление, используемое для обозначения работы
задачи при неизвестном проценте выполнения;
Progress View — показывает, что задача выполняется, и указывает процент ее
выполнения;
Pages View — указывает число открытых в приложении страниц;
Date Picker — элемент для визуального отображения и редактирования экземп-
ляра UIDatePicker;
Image View — позволяет отобразить одно изображение или серию изображений
как анимацию;
Map Kit View — определяет встраиваемый интерфейс географической карты.
Элементы управления фактически представляют собой разнообразные виджеты
(widgets) на панели, с которой должен взаимодействовать пользователь.
10.3. Навигатор проекта Xcode 13.1
После создания нового проекта Xcode для iOS на основе технологии Storyboard,
в панели навигатора отображается корневой файл проекта, помеченный синим
значком (на рис. 10.5 — это верхняя надпись: iOSMyProject). Левая область
панели навигатора представляет собой собственно навигатор проекта (Project
Navigator) в составе всех его файлов, которые можно просматривать или редакти-
ровать в области редактора кода, а правая его область демонстрирует свойства про-
екта. Навигатор также позволяет добавлять, удалять или группировать файлы про-
екта.
Щелчок в панели навигатора на файле ViewController вызовет появление контрол-
лера представления (View Controller), на котором должны размещаться элементы
управления, а если щелкнуть на ссылке Main, появится заготовка класса
ViewController, куда можно будет вписывать код создаваемого проекта.
Глава 10. Создание проектов в редакторе Interface Builder на основе интерфейса Storyboard 291
ВНИМАНИЕ!
Иногда по каким-либо причинам требуется изменить имя проекта, однако при этом на-
до понимать, что изменение имени проекта приведет к нарушению его правильной ра-
боты. Поскольку проект состоит из нескольких файлов, придется редактировать и их
имена, и если этого не сделать, проект станет непригодным для дальнейшего исполь-
зования.
Для добавления к проекту новой функциональности вы можете создать папку New
Group, куда можно будет копировать нужные файлы. Щелкните для этого правой
кнопкой мыши в поле навигатора на ссылке Assets и из появившегося контекстного
меню выберите опцию New Group (рис. 10.6). Когда в этой папке пропадет надоб-
ность, ее можно будет удалить, выбрав в ее контекстном меню опцию Delete.
Рис. 10.5. Левая область панели навигатора Рис. 10.6. Создание новой папки New Group
Впрочем, вы можете не создавать и не использовать такую папку, а добавлять фай-
лы непосредственно в навигатор проектов (если, конечно, их немного). Щелкните
для этого правой кнопкой мыши в панели навигатора проектов и в открывшемся
окне выберите опцию New File либо Add File to "Имя проекта". Пусть, например,
нам в проекте требуется какое-то изображение — выберите тогда опцию Add File
to "iOSMyProject", найдите на компьютере нужный файл, выделите его имя и
щелкните на кнопке Add — выбранный файл будет добавлен в навигатор проектов.
10.4. Создание проекта для iOS
с использованием Interface Builder
Некоторые проекты можно создать и без написания кода, однако для большей час-
ти проектов код, как правило, необходим. Рассмотрим, как создать проект, когда
для его реализации требуется код. В этом случае компоновку окна редактора
Interface Builder надо изменить, поскольку на экране после щелчка на ссылке Main
появляется только контроллер представления (см. рис. 10.3, а). Однако мы должны
видеть там одновременно и контроллер представления, и редактор кода, поскольку
нам понадобится перетаскивать связи от элементов на контроллере представления
292 Часть II. Технологии разработки приложений в среде Xcode 11.2 и 13.1
к их программной реализации в редакторе кода. Возможностей взаимного распо-
ложения контроллера представления и редактора кода имеется несколько, но наи-
более удобным, вероятно, является расположение, когда контроллер представления
(View Controller) находится слева, а редактор кода — справа. Чтобы этого добиться,
надо в правой верхней части окна Interface Builder (см. рис. 10.3, а) щелкнуть на
кнопке и в открывшемся меню выбрать опцию Assistant — справа от контрол-
лера представления появится панель редактора кода, и вам останется только немно-
го раздвинуть поля, чтобы получить вид окна, показанный на рис. 10.7.
Рис. 10.7. Справа от контроллера представления появился редактор кода
В редакторе кода вы увидите автоматически созданный класс ViewController и
метод ViewDidLoad() (поскольку этот метод нам не требуется, его можно удалить):
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Создадим сейчас простой проект: установим на нашу раскадровку (storyboard) два
элемента Label и одну кнопку Button. Делается это следующим образом: сначала
щелкните в любом месте на контроллере представления, а затем на кнопке
(см. рис. 10.4) — откроется окно с элементами библиотеки (рис. 10.8).
Щелкните в этом окне левой кнопкой мыши на имени Label и, не отпуская кнопки
мыши, перетяните метку на панель контроллера представления. Немного увеличьте
Глава 10. Создание проектов в редакторе Interface Builder на основе интерфейса Storyboard 293
Рис. 10.8. Окно с элементами библиотеки элементов управления
с помощью мыши размеры метки, а затем на панели инспектора атрибутов, рас-
положенной справа от панели редактора кода (рис. 10.9), установите цвет текста
метки — для чего в поле Color выберите White Color, в поле Alignment задайте
выравнивание текста по центру, а в поле Background назначьте цвет заднего фона
метки — System Blue Color (сине-зеленый).
Настроив первую метку Label, таким же образом перетащите на панель контролле-
ра представления еще одну метку Label и кнопку Button и настройте так же и их.
Теперь необходимо связать элементы раскадровки с кодом. Тут надо отметить, что
если по технологии SwiftUI (см. главу 11) все это делается автоматически, то здесь
эту связь должен устанавливать сам разработчик. Так что нажмите на клавиатуре
клавишу <Control> и, не отпуская ее, щелкните на элементе Label, размещенном на
панели контроллера представления, — появится синяя линия со стрелкой, которую
надо протянуть в код программы в редакторе кода, направив ее ниже строки, где
294 Часть II. Технологии разработки приложений в среде Xcode 11.2 и 13.1
Рис. 10.9. Настройка вида меток и кнопок с помощью инспектора атрибутов
объявлен class ViewController (рис. 10.10, а), после чего отпустите и клавишу
<Control>, и кнопку мыши — откроется панель, показанная на рис. 10.10, б.
В этой панели в поле Connection выберите спецификатор Outlet. Он используется
для описания свойств класса, когда не требуется выполнять какие-либо действия.
Затем в поле Name впишите имя элемента, под которым он будет фигурировать
в коде, — его надо просто придумать (в нашем примере это имя label). Осталось
нажать на кнопку Connect, и свойство появится в программе. Так же устанавлива-
ется и вторая метка.
Теперь надо установить связь для элемента Button. Снова нажмите клавишу
<Control>, щелкните на элементе Button в панели контроллера представления и, не
отпуская клавишу <Control> и кнопку мыши, протяните синюю линию в код про-
граммы, направив ее ниже уже имеющихся там объявлений меток, после чего от-
пустите и клавишу <Control>, и кнопку мыши — откроется панель, показанная на
рис. 10.11.
Глава 10. Создание проектов в редакторе Interface Builder на основе интерфейса Storyboard 295
а
б
Рис. 10.10. Связывание элемента раскадровки Label с его кодом: а — протягивание синей стрелки
в код программы в редакторе кода; б — панель параметров такого связывания
296 Часть II. Технологии разработки приложений в среде Xcode 11.2 и 13.1
Рис. 10.11. Панель параметров связывания элемента раскадровки Button с его кодом
В этой панели в поле Connection теперь выберите спецификатор Action, поскольку
кнопка предназначена для реализации метода, и с ней связывается код, выполняе-
мый по ее нажатии. Затем в поле Name впишите имя элемента, под которым он бу-
дет фигурировать в коде, — в нашем примере это имя button. Нажмите на кнопку
Connect, после чего напишите код для метода button. В этом коде мы создадим
свойство k, которое будет запоминать число нажатий на кнопку, и флажок f, кото-
рому первоначально присвоим значение true, а после пяти нажатий — значение
false, что позволит прекратить дальнейший подсчет количества нажатий, даже
если на кнопку продолжать нажимать (рис. 10.12):
import UIKit
class ViewController: UIViewController {
var f = true
var k = 0
@IBOutlet weak var label: UILabel!
@IBOutlet weak var label1: UILabel!
@IBAction func button(_ sender: Any) {
if f == true { k += 1
label.text = "Кнопка нажата " + String(k) + " раз"
}
if k == 5 {
label1.text = "Поскольку кнопка была нажата 5 раз, то
вычисление количества нажатий было завершено"
f = false
}
}
}
Далее надо установить тип симулятора, который предполагается использовать
в проекте. Для этого выполните в меню Xcode команду Product | Destination и вы-
берите требуемую модель симулятора. Затем щелкните на кнопке в левой верх-
ней части окна Xcode над панелью навигатора проекта (можно также нажать ком-
бинацию клавиш <Command>+<R>), и симулятор будет запущен (рис. 10.13).
Глава 10. Создание проектов в редакторе Interface Builder на основе интерфейса Storyboard 297
Рис. 10.12. Завершенный проект с двумя метками label и кнопкой button в Xcode 13.1
Рис. 10.13. Завершенный проект
с двумя метками label
и кнопкой button отображен
в симуляторе
298 Часть II. Технологии разработки приложений в среде Xcode 11.2 и 13.1
10.5. Создание проекта для iOS
без написания кода
Далеко не каждый проект можно реализовать, не используя кодирование, однако
такие задачи имеются. Пусть, например, требуется просмотреть на экране какие-
либо документы или изображения и, просмотрев их все, вернуться в начало для
повторного просмотра.
Поскольку Xcode 11.2 еще используется многими разработчиками, а создание про-
екта на его основе принципиально мало отличается от использования Xcode 13.1,
то некоторые проекты, в том числе из этого и следующих разделов, мы рассмотрим
на основе Xcode 11.2 (причем для разнообразия в его темном режиме).
ПРИМЕЧАНИЕ
Особо необходимо отметить, что все программы каждой главы этой книги будут рабо-
тать и в Xcode 13.1, и в Xcode 11.2, — достаточно их набрать и вставить в нужное ме-
сто редактора кода, При этом если у вас имеется электронная версия книги, то про-
граммы оттуда можно просто копировать и вставлять в редактор кода.
В навигаторе проекта щелкните на файле каталога активов Assets.xcassets, и в пра-
вой части панели навигатора откроется каталог ресурсов. Затем создайте какой-
нибудь файл с изображениями или откройте приложение Фото с предварительно
записанными изображениями. Разместите окно приложения Фото правее окна
Interface Builder и просто перетащите в него нужные файлы, поместив их в область
Applcon (рис. 10.14). Если файлы имеют громоздкие имена, замените их на легко
Рис. 10.14. Изображения перемещены в каталог ресурсов Interface Builder
Глава 10. Создание проектов в редакторе Interface Builder на основе интерфейса Storyboard 299
читаемые — щелкните для этого на старом имени файла двойным щелчком, сотри-
те это имя и впишите вместо него новое. Переведите изображения в один из трех
подходящих форматов (1x, 2x или 3x).
Щелкните в навигаторе проекта на ссылке Main.storyboard и в открывшемся окне
уменьшите размер контроллера представления (View Controller), щелкнув один или
два раза на расположенной под ним кнопке . Затем щелкните на кнопке в пра-
вой верхней части окна Xcode и перетащите из открывшейся библиотеки к имею-
щемуся контроллеру представления еще три. На каждый из них затем перетащите
по элементу Image View, Label и Button (рис. 10.15). Впишите в элементы Label на-
звания изображений, которые будут на них отображаться, а в элементы Button —
надписи о переходах на следующие экраны: в элемент Button первого контроллера
представления — надпись Переход на второй экран, в элемент Button второго
контроллера представления — надпись Переход на третий экран, в элемент Button
третьего контроллера — надпись Переход на четвертый экран, а в элемент Button
четвертого — надпись Возврат на стартовый экран.
Рис. 10.15. Четыре контроллера представления
с размещенными на них элементами Image View, Label и Button
Чтобы связать изображение с элементом Image View, щелкните на нем, откройте
инспектор атрибутов (если он не открыт) и в верхней его части установите флажок
в поле Image — там появятся все имена файлов изображений, которые вы перета-
щили в каталог ресурсов. Выделите нужный файл и щелкните на нем, чтобы его
имя появилось в области Image. Проделайте то же самое с каждым файлом изобра-
жения из предназначенных для размещения в контроллерах представления. Затем
перейдите в каталог ресурсов и перетащите каждое изображение в свой контроллер.
300 Часть II. Технологии разработки приложений в среде Xcode 11.2 и 13.1
Теперь вернитесь в Main.storyboard для задания необходимых связей. Установите
курсор на первой кнопке Button (на ней должна быть надпись Переход на второй
экран), нажмите клавишу <Control>, протяните мышью появившуюся линию на
второй контроллер представления, а когда вы отпустите и кнопку мыши, и клавишу
<Control>, откроется меню, в котором надо выбрать опцию Show (рис. 10.16),
и связь будет установлена.
Рис. 10.16. Устанавливаем связь первой кнопки Button со вторым контроллером представления
Затем таким же образом установите связи и для остальных контроллеров представ-
ления. Связи можно посмотреть и проверить — на рис. 10.17 вид Xcode был пере-
ключен в светлый режим (Light), поскольку в нем переходы (segue) между кон-
троллерами видны более отчетливо.
Рис. 10.17. Переходы между контроллерами представления
368 Часть II. Технологии разработки приложений в среде Xcode 11.2 и 13.1
Рис. 11.47. Результат работы проекта, использующего стеки VStack, HStack и ZStack
11.12. Проект с использованием
географической карты (MapView),
изображения и текста
Проекты, в которых задействованы географические карты, в мобильных приложе-
ниях весьма востребованы. Для создания карты требуется фреймворк MapKit, по-
зволяющий встраивать карту в представление разрабатываемого проекта. При ини-
циализации карты можно указать координаты того региона, который должен
отображаться в центральной ее части. Регион задается центральной точкой и верти-
кальным и горизонтальным расстояниями от нее — диапазонами (span). Диапазон
позволяет определить, какая часть карты должна быть видимой, а также установить
масштаб ее отображения. Чем меньше диапазон, тем меньшая область карты будет
отображаться и тем более подробно будет выведено ее представление. Класс
MKMapView позволяет также производить прокрутку карты с помощью мыши на хол-
сте или с помощью жестов (gestures) на устройстве.
В рассматриваемом здесь проекте использованы координаты южного района Моск-
вы и изображение некоторой местности (ландшафта) этого района. Для вывода
изображения, заключенного в круг, применен элемент CircleImage.
Глава 11. Технология SwiftUI 369
Итак, давайте создадим проект с именем iOSTextCircleMap, который должен пока-
зывать карту местности заданного района и выводить ниже ее на холсте изображе-
ние ландшафта этого района с поясняющими надписями. Для этой цели к стандарт-
ным файлам проекта (включая и файл ContentView) надо добавить еще два: файл
MapVies — для отображения карты заданного района и файл CircleImage — для
отображения ландшафтного вида этого района со всеми необходимыми поясне-
ниями.
Для их создания щелкните правой кнопкой мыши в панели навигатора, выполните
для каждого из них команду New File | Add Files to "Имя проекта" и добавьте
файлы в проект (на рис. 11.48 эти файлы уже добавлены). Кроме этого, надо от-
крыть каталог Assets и добавить туда изображение ландшафта, которое сначала
должно быть либо найдено в Finder, либо предварительно записано в какую-либо
папку, находящуюся, например, на рабочем столе. Изображению надо дать какое-то
простое имя и перетащить его в область Applicon — тогда оно появится в одном из
прямоугольников справа, задающих формат. Если изображение не появляется при
запуске программы, то надо проверить модификаторы панели Image, поскольку от
них зависит качество изображения.
Рис. 11.48. Состав проекта iOSTextCircleMap
Создадим сначала файл MapView, отображающий карту местности. Этот файл
определяет структуру MapView, которая должна соответствовать протоколу UIKit,
что позволит нашему пользовательскому типу MapView настраивать типы UIKit, а
SwiftUI будет обновлять их каждый раз, когда это потребуется:
import SwiftUI
import MapKit
struct MapView: UIViewRepresentable {
func makeUIView(context:
UIViewRepresentableContext<MapView>) -> MKMapView {
MKMapView()
}
370 Часть II. Технологии разработки приложений в среде Xcode 11.2 и 13.1
func updateUIView(_ uiView:
MKMapView, context: Context){
// Код для обновления карты
let coordinate = CLLocationCoordinate2D (
// Широта и долгота (здесь значения для Москвы)
latitude: 55.75396, longitude: 37.620393)
// Масштаб карты
let span = MKCoordinateSpan(latitudeDelta: 0.0, longitudeDelta: 1.0)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.setRegion(region, animated: true)
}
}
struct MapView_Previews: PreviewProvider {
static var previews: some View {
MapView()
//.previewInterfaceOrientation(.landscapeLeft)
}
}
При запуске этого файла будет выдаваться карта (рис. 11.49).
Рис. 11.49. Карта местности, выдаваемая при запуске файла MapView
Глава 11. Технология SwiftUI 371
Создадим теперь файл CircleImage, отображающий ландшафт местности:
import SwiftUI
struct CircleImage: View {
var body: some View {
Image("Image")
.resizable(resizingMode: .stretch)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white,lineWidth: 2))
.shadow(radius: 30)
}
}
struct CircleImage_Previews: PreviewProvider {
static var previews: some View {
CircleImage()
}
}
При запуске этого файла будет выдаваться изображение (рис. 11.50), которое надо
не забыть, как отмечалось ранее, предварительно добавить в каталог ресурсов про-
екта.
Рис. 11.50. Изображение на холсте и в симуляторе,
выдаваемое при запуске файла CircleImage
Глава 11. Технология SwiftUI 397
Рис. 11.70. Панель навигатора с добавленным файлом функции prime
Рис. 11.71. Реализация функции разложения
натурального числа n на простые множители
в симуляторе
Рассмотрим еще один пример использования декларативной функции, которую
также разместим в отдельном файле, как и в предыдущем примере. Пусть имеется
лотерейный билет, который определяется его шестизначным номером. Если сумма
первых трех цифр этого билета равна сумме трех последних цифр, то билет счита-
ется выигрышным, в противном случае он не выигрывает ничего.
398 Часть II. Технологии разработки приложений в среде Xcode 11.2 и 13.1
Сначала создадим проект с именем iOSLotteryTicket, в котором номер билета
будет вводиться пользователем при помощи текстового поля TextField, а все необ-
ходимые вычисления выполним в замыкании элемента Button, которое расположе-
но после оператора action. За пределами замыкания необходимо поместить текст,
щелчок на котором будет приводить к выдаче результата:
import SwiftUI
struct ContentView: View {
@State private var ticket = ""
@State private var st = ""
var body: some View {
Form {
TextField("Введите номер билета:", text: $ticket).padding(.all)
Text("Введен номер = \(ticket)")
Button(action: {
if sumOfDigits(n: Int(self.ticket)!/1000 ) ==
sumOfDigits(n: Int(self.ticket)!%1000) {
self.st = "Билет выиграл "}
else {self.st = "Билет не выиграл "}
}) {
Text("Нажмите для получения результата:").font(.headline) }
Text("\(self.st)").foregroundColor(.red).font(.largeTitle)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
В качестве декларативной возьмем созданную в главе 7 функцию с именем
sumOfDigits, которая определяет сумму цифр любого натурального числа. Необхо-
димо добавить эту функцию в проект, поместив ее в файл sumOfDigit. Сделать это
надо так же, как было сделано в предыдущем примере, — щелкнув правой кнопкой
мыши в навигаторе проекта и выбрав опцию New File. Функция имеет следующий
вид (рис. 11.72):
import SwiftUI
func sumOfDigits(n: Int) -> Int {
if n == 0 {return 0}
return sumOfDigits(n: n/10) + n%10
}
Результат работы этого проекта показан на рис. 11.73.
Глава 11. Технология SwiftUI 399
Рис. 11.72. Реализация проекта с лотерейным билетом
а б
Рис. 11.73. Результат работы проекта с лотерейным билетом:
а — выигрышный вариант; б — проигрышный вариант
<0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0>
ПРИЛОЖЕНИЕ
РЕШЕНИЯ УПРАЖНЕНИЙ
И ОТВЕТЫ
Упражнения 2.5
№1
import UIKit
var x = 3.6
var y = 1.5
var z = ((sin(pow(x,3)) - pow(cos(x),5) + tan(x - 2 * y) )/(log(x) +
log10(pow(x,4)+12)))*sqrt(x + 5*y)
print("рeзультат=", z)
рeзультат= 1.6159126593248798
№2
import UIKit
var x = 0.78
var y = -2.3
var z = x*pow(y,5) + pow(log(x + pow(y,4) - exp(x)),1/3)+
sin(pow(x + 3*log(pow(x,y) + exp(x*y) + 6),1/4))
print("рeзультат=", z)
рeзультат= -47.719182756513064
№3
import UIKit
var x = 0.27
var y = -14.68
var z = pow(x*cos(abs(y)) + pow(sin(x - 4*y),3),1/5) +
pow((abs(tan(x))+7*x),1/5)/(cos(x)*sin(x))
print("рeзультат=", z)
рeзультат= 5.20364103532659
Решения упражнений и ответы 401
Упражнения 2.7
№1
Программа с данными для получения первой строки таблицы истинности:
var logicalX = false
var logicalY = false
var implication = !logicalX || logicalY
print("implication =",implication)
implication = true
№2
Программа с данными для получения первой строки таблицы истинности:
var logicalX = false
var logicalY = false
var equivalence = logicalX && logicalY || !logicalX && !logicalY
print("equivalence=",equivalence)
equivalence= true
№3
Программа с данными для получения первой строки таблицы истинности:
var logicalX = false
var logicalY = false
var moduloTwo = logicalX && !logicalY || !logicalX && logicalY
print("moduloTwo",moduloTwo)
equivalence= false
Упражнения 3.9
№1
// Определение min и max для трех положительных чисел
func minMax(valueX: Int,valueY: Int,valueZ: Int) -> (min: Int,max: Int) {
var min = (valueX + valueY - abs(valueX - valueY))/2
var max = (valueX + valueY + abs(valueX - valueY))/2
max = (max + valueZ + abs(max - valueZ))/2
min = (min + valueZ - abs(min - valueZ))/2
return(min, max)
}
<0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0> <0>
ПРЕДМЕТНЫЙ УКАЗАТЕЛЬ
C В
CamelCase 52 Вектор 223
Взаимная блокировка (deadlock) 199
*** Взаимные блокировки 49, 198
Виджеты (widgets) 290
А Визуальная среда разработки 15
Вложенные функции 87
Абстракция данных 145 Вложенный цикл 111
Автозавершение 56, 57 Вычисляемые свойства 149
Автоматический подсчет ссылок (ARC) 180, 190
Актор 264, 276 Г
Акторы (Actors) 271
Анимация 389 Генератор случайных чисел 305
Аннотация типов 51 Географические карты 368
Анонимная функция 90, 93 Глобальные переменные 55
Анонимное замыкание 228 Глобальные функции 74
Анонимные параметры 92 Гонки (race condition) 49, 199, 227
Аргументы функции 74 Гонки данных (Data Races) 271
Арифметические операции 58 Градиент 388
Асинхронное выполнение 266 Графические приложения (project) 14
Асинхронные
◊ методы 266 Д
◊ функции 265, 268
Асинхронный код 264 Двумерные массивы 130
Ассистивная клавиатура 355 Декларативное программирование 198
Ассоциированный тип 251–253 Декларативные
Ассоциированный тип (associated type) 251 ◊ программы 224
◊ функции 393
Б Декларативный
◊ подход 11, 203, 204
Базовый класс 164 ◊ стиль 199, 201, 208, 212
Безымянные функции 205
Библиотека программирования 199
◊ Cocoa 18 Делегат 245, 246
◊ Cocoa Touch 18 Делегирование 245, 246
◊ Swift 201, 205, 206 Диаграммы Венна 139, 140
◊ модификаторов 381 Диапазон 122
Библиотечные функции 219 Динамическая
Бинарная операция 67 ◊ замена (Dynamic replacement) 316
Бинарный оператор 66 ◊ типизация 51
Блокировки (dead lock) 227 Динамические представления 335
Блок-схема 105 Диспетчеризация (dispatch) 274
Диспетчерские очереди (dispatch queues) 274
Дополнение множества 139