Перейти к основному содержимому

Основы языка Swift

Разработчику Архитектору

Основы языка Swift

Что такое Swift?

Swift — это язык программирования со следующими особенностями:

  • Типизация — статическая, сильная; вывод типов есть (let x = 42Int); null-safety через опционалы (T?, Optional<T>).
  • Парадигма — мультипарадигменный — протокол-ориентированный, объектно-ориентированный (class), императивный, функциональный (замыкания, generics, enum с associated values); value types (struct, enum) как основа модели данных.
  • Уровень — высокоуровневый (с доступом к низкоуровневым конструкциям через UnsafePointer, Unmanaged<T>).
  • Выполнение — компилируемый (swiftc → LLVM IR → машинный код); не интерпретируемый напрямую.
  • Память — ARC (автоматический подсчёт ссылок) для reference types; value types на стеке без GC; weak/unowned против циклических ссылок; ручное управление через Unsafe* при необходимости.
  • Платформа — кроссплатформенный (iOS, iPadOS, macOS, watchOS, tvOS, visionOS, Linux, Windows, WebAssembly); на Apple — нативная интероперабельность с Objective-C и C через runtime; open-source с swift.org (с 2015).
  • Формат разработки — обычно требует структуры проекта (Xcode, Swift Package Manager); скриптовый режим через swift script и shebang #!/usr/bin/swift; один .swift-файл можно запустить из терминала.
  • Направление — универсальный; сильнее всего — нативные приложения Apple, серверная разработка (Vapor, Hummingbird), CLI, embedded, WebAssembly.
  • REPL — есть: команда swift в терминале (macOS, Linux); Playgrounds в Xcode для интерактивных фрагментов; на Linux и Windows классического Playground Xcode нет — REPL и SPM.
  • Поколение — современный (с 2014 года); активно развивается (Swift 5.x / 6.x, релизы примерно два раза в год).
  • Параллелизм и асинхронность — нативно — async/await, Task, TaskGroup, actor, @MainActor (с Swift 5.5); GCD (DispatchQueue) через Foundation; Swift 6 усилил проверку data race на этапе компиляции.
  • Безопасность — относительно "безопасный" — опционалы, статическая типизация, проверка границ массивов, обязательная инициализация; не memory-safe как Rust (try!, Unsafe* — escape hatches); в strict concurrency mode Swift 6 ловит гонки данных статически.

Если какой-то пункт из списка непонятен — подробные определения и примеры в Язык программирования.

Помимо iOS, iPadOS, macOS, watchOS, tvOS и visionOS, Swift используют для серверов (Vapor, Hummingbird), CLI-скриптов и сборок под Linux и Windows. Компилятор на LLVM может таргетировать x86, ARM, WebAssembly и другие архитектуры. На платформах Apple Swift работает с Cocoa / Cocoa Touch и при необходимости с рантаймом Objective-C — в одном проекте смешивают Swift, Objective-C и C. Подробная хронология — в Истории языка Swift.

Минимальный синтаксис (как в учебных примерах Swift) — вывод типов, let для констант, подчёркивания в числах и интерполяция строк:

Код ITЗагрузка примера кода…

Разбор:

  • Без : Type компилятор выводит тип (70Int, 70.0Double).
  • let фиксирует значение; var допускает изменение.
  • 299_792_458 — разделитель разрядов для читаемости.
  • "\(...)"string interpolation; в цикле for (name, age) in — сопоставление пар ключ–значение словаря.

Связь с Objective-C

До Swift основным языком экосистемы был Objective-C (надмножество C + ООП в стиле Smalltalk). Swift задуман как эволюция, а не обрыв: в одном проекте можно смешивать оба языка, вызывать Cocoa API и постепенно мигрировать модули.

Ключевые ограничения Objective-C, из-за которых понадобился Swift (синтаксис, неявные nil, слабая статическая проверка, динамическая диспетчеризация) разобраны в истории языка. В новых проектах под Apple сегодня почти всегда выбирают Swift.


Архитектура языка

Swift — это компилируемый язык высокого уровня, сочетающий черты статически типизированных, императивных, объектно-ориентированных и функциональных языков. Его архитектура состоит из нескольких ключевых компонентов, взаимодействующих в процессе разработки и выполнения.


Компилятор (swiftc) и LLVM

Компиляция Swift осуществляется через swiftc — драйвер компилятора, являющийся частью инструментария Swift. На начальных этапах Swift использовал собственный фронтенд, но генерировал промежуточный код LLVM (LLVM IR), который затем оптимизировался и транслировался в машинный код целевой архитектуры (x86_64, ARM64, и др.). Сегодня компилятор Swift тесно интегрирован в инфраструктуру LLVM: анализ типов, проверки безопасности и генерация кода выполняются в рамках единого конвейера.

Это даёт два важных преимущества:

  • Высокая производительность: LLVM — один из самых продвинутых современных бэкендов компиляции. Он умеет выполнять глубокую оптимизацию, включая встраивание функций, развёртывание циклов, анализ потока данных.
  • Целевая гибкость — благодаря LLVM, Swift может компилироваться практически под любую архитектуру, для которой существует бэкенд LLVM — от микроконтроллеров до серверов.

Система типов

Swift обладает строгой статической типизацией с выводом типов. Это означает, что все выражения имеют тип, который проверяется на этапе компиляции, но при этом явное указание типа часто не требуется — компилятор выводит его из контекста. Например, в выражении let x = 42 компилятор определяет, что x имеет тип Int, без необходимости писать let x: Int = 42.

Типы в Swift делятся на значимые (value types) и ссылочные (reference types):

  • Значимые типы — это структуры (struct) и перечисления (enum). Их копирование выполняется по значению: при присваивании создаётся независимая копия данных. Это обеспечивает предсказуемость и потокобезопасность.
  • Ссылочные типы — это классы (class). Их переменные хранят ссылку на объект в куче; присваивание копирует только ссылку, а не сам объект. Управление временем жизни таких объектов осуществляется через автоматический подсчёт ссылок (ARC).

Кроме того, Swift предоставляет богатую систему обобщений (generics), ассоциированных типов, протоколов (протоколы как интерфейсы и "типы-требования") и составных типов (tuples). Все они строго проверяются на этапе компиляции и не имеют накладных расходов времени выполнения.

Код ITЗагрузка примера кода…

Разбор:

  • struct Config копируется по значению: две независимые переменные с одинаковым начальным содержимым.
  • class AppState копирует ссылку: state и copy указывают на один объект в куче.
  • Изменение поля через любую ссылку на класс видно всем остальным ссылкам на тот же экземпляр.
  • Для моделей данных и настроек экрана чаще выбирают struct; для общего состояния приложения — class или actor.
  • Понимание value vs reference помогает избежать "неожиданных" обновлений UI из другого экрана.

Безопасность как фундаментальный принцип

Безопасность в Swift не является опциональной "фичей" — она заложена в саму структуру языка. Ключевые механизмы:

  • Обязательная инициализация: каждая переменная должна быть проинициализирована перед использованием. Компилятор проверяет это статически.
  • Работа с отсутствующими значениями через опционалы (Optional): вместо null (как в Objective-C или Java) Swift использует тип-оболочку Optional<T>, который чётко разделяет "есть значение" и "значения нет". Распаковка опционала требует явных действий (if let, guard let, ??), что исключает случайные разыменования нулевых указателей.
  • Проверка выхода за границы массивов: все операции с коллекциями в безопасном режиме включают проверку индексов. Попытка чтения по некорректному индексу вызывает ошибку выполнения, а не неопределённое поведение.
  • Иммутабельность по умолчанию: константы (let) предпочтительнее переменных (var). Неизменяемость помогает избежать побочных эффектов и упрощает рассуждение о коде.
  • ARC без циклических ссылок "из коробки": управление памятью автоматизировано, но для предотвращения утечек в замыканиях и делегатах предусмотрены ключевые слова weak и unowned.

Эти механизмы работают совместно — опционалы защищают от ошибок времени выполнения, ARC — от утечек памяти, иммутабельность — от трудноуловимых изменений состояния. В результате Swift-код, прошедший компиляцию без предупреждений, обладает высокой степенью корректности.

func loadUser(id: Int?) {
guard let userId = id, userId > 0 else {
print("Некорректный id")
return
}
// дальше userId — обычный Int, не опционал
print("Загрузка пользователя \(userId)")
}

let items = ["a", "b", "c"]
// print(items[10]) // runtime trap: индекс вне диапазона

Разбор:

  • guard let проверяет опционал и при неудаче выходит из функции — без глубокой вложенности if.
  • После успешного guard компилятор считает userId неопциональным Int в оставшемся теле функции.
  • Доступ к массиву по индексу проверяется в runtime: выход за 0..<count завершает программу с понятной ошибкой.
  • let items не запрещает читать элементы, но фиксирует ссылку на массив; для изменения элементов нужен var.
  • Связка let + guard + опционалы — типичный каркас безопасного кода на границе с сетью и UI.

Компоненты языка

Swift состоит не только из синтаксиса и компилятора. Это целостная система, включающая:

  • Ядро языка — базовые конструкции — объявление переменных и констант, типы, управляющие структуры, функции, замыкания, ошибки.
  • Типы данных и коллекции — встроенные типы (Int, String, Bool, Double), структуры, классы, перечисления, кортежи, массивы, множества, словари.
  • Система протоколов — основа полиморфизма и абстракции в Swift. Протоколы определяют требования к поведению (методы, свойства), но не реализацию. Расширения протоколов позволяют добавлять реализации по умолчанию.
  • Расширения (extension) — механизм добавления функциональности к существующим типам без наследования и без доступа к исходному коду. Это мощный инструмент модульности и повторного использования.
  • Обобщённое программирование — параметризация типов, функций и протоколов. Позволяет писать универсальный, переиспользуемый код без потери производительности.
  • Управление ошибками — механизм throw/try/catch на основе перечислений, соответствующих протоколу Error. Все потенциально выбрасывающие функции помечаются явно, что делает обработку ошибок прозрачной и обязательной.
  • Асинхронное программирование — начиная с Swift 5.5, в язык встроена поддержка async/await, а также акторов (actor) для безопасного распараллеливания.

Каждый из этих компонентов взаимосвязан — например, протоколы могут иметь ассоциированные типы, расширения могут реализовать протоколы для обобщённых типов, акторы используют ARC с дополнительными гарантиями изоляции. Это создаёт консистентную и мощную модель программирования.


Экосистема Swift

Экосистема Swift формируется на нескольких уровнях — от ядра языка до высокоуровневых платформенных фреймворков. Важно отделить язык от среды выполнения и библиотек, поскольку Swift сам по себе не содержит встроенных возможностей для работы с камерой, сетью или интерфейсом: всё это обеспечивается внешними компонентами, с которыми Swift интегрируется.


Стандартная библиотека Swift (Swift Standard Library)

Это фундаментальный набор типов и функций, который доступен в любом Swift-проекте без дополнительных импортов. Стандартная библиотека включает:

  • Базовые типы — Int, UInt, Float, Double, Bool, Character, String.
  • Коллекции — Array, Set, Dictionary, а также протоколы Collection, Sequence, MutableCollection.
  • Утилиты — Optional, Result, Never, Void, Equatable, Comparable, Hashable, CustomStringConvertible.
  • Управление памятью — ARC, Unmanaged<T>, UnsafePointer<T> и производные типы (включая UnsafeMutableBufferPointer и т.д.).
  • Типы для работы с ошибками: Error, LocalizedError.
  • Поддержка конкурентности — async/await, Task, TaskGroup, Actor.

Стандартная библиотека написана на самом Swift и C++ (части, связанные с низкоуровневой оптимизацией) и компилируется вместе с приложением. Она не зависит от операционной системы и может использоваться везде, где скомпилирован Swift — в том числе на Linux и в bare-metal средах (например, с Swift for TensorFlow или Swift on Raspberry Pi).


Foundation и другие базовые фреймворки Apple

Хотя стандартная библиотека предоставляет универсальные абстракции, для реальной разработки требуются платформенные возможности. Их обеспечивает Foundation — фреймворк, изначально разработанный для Objective-C, но полностью совместимый со Swift. Foundation включает:

  • Работу со временем (Date, Calendar, TimeZone).
  • Форматирование (NumberFormatter, DateFormatter, MeasurementFormatter).
  • Работу с файловой системой (FileManager, URL, Data).
  • Архивацию и сериализацию (Codable, JSONEncoder, PropertyListEncoder, NSKeyedArchiver).
  • Многопоточность (DispatchQueue, DispatchGroup, DispatchSemaphore — через GCD).
  • Регулярные выражения (NSRegularExpression, а с Swift 5.7 — нативные регулярки в стиле /pattern/).
  • Локализацию (Bundle, LocalizedStringKey, NSLocalizedString).

Foundation не является частью Swift как языка — это отдельный фреймворк, но он настолько прочно интегрирован, что считается "полустандартным". На платформах Apple он поставляется вместе с ОС; на Linux он доступен через open-source реализацию swift-corelibs-foundation.

Помимо Foundation, ключевыми фреймворками являются:

  • UIKit (iOS, tvOS) и AppKit (macOS) — для построения классических пользовательских интерфейсов с контролами, делегатами, responder chain.
  • SwiftUI — декларативный фреймворк для UI, полностью на Swift, с поддержкой реактивных паттернов (через @State, @Binding, Observable).
  • Combine — фреймворк реактивного программирования, предоставляющий Publisher, Subscriber, Subject, Operator. Используется для работы с асинхронными потоками данных (события, сетевые ответы, изменения состояния).
  • Core Data — ORM-подобная система для локального хранения объектов с поддержкой миграций, отношений, запросов (на NSPredicate или @FetchRequest в SwiftUI).
  • CloudKit, Firebase SDK, Network, URLSession, AVFoundation, CoreLocation, CoreMotion, ARKit, RealityKit, SpriteKit, Metal — все они — не часть Swift, но официально поддерживаются Apple и имеют Swift-совместимые API.

Swift выступает здесь как лингва франка — он не диктует, как должен выглядеть интерфейс, но обеспечивает строгую типизацию, безопасность и выразительность при вызове этих внешних библиотек.


Инструменты разработки

Xcode — основная среда разработки

Полный обзор IDE — в статье Xcode — IDE для экосистемы Apple. Кратко — Xcode — официальная IDE Apple и единственная среда с полным циклом разработки под Apple-платформы — от редактирования кода до отладки, тестирования, профилирования и отправки в App Store.

В Xcode интегрированы:

  • Редактор кода с автодополнением SourceKit — обеспечивает семантический анализ в реальном времени (не только синтаксический), быструю навигацию, рефакторинги.
  • Interface Builder — визуальный редактор UI (для UIKit/AppKit), хотя в эпоху SwiftUI его роль сокращается.
  • Swift Package Manager (SPM) — встроенная поддержка управления зависимостями через Package.swift. SPM поддерживает локальные, удалённые (по URL), версионированные и веточные зависимости.
  • LLDB — отладчик с поддержкой выражений на Swift (можно выполнять po myObject.description или даже expr myVar = 42 во время отладки).
  • Instruments — набор инструментов профилирования — Allocations (утечки памяти), Time Profiler (горячие точки CPU), Energy Log, Network, Core Animation и др.
  • Simulator — эмулятор устройств iOS/tvOS/watchOS с поддержкой различных моделей, ориентаций, локализаций, условий сети и геолокации.

Важно: Xcode не обязателен для написания Swift-кода. На macOS, Linux или Windows (через WSL) можно использовать VS Code с расширением SourceKit-LSP или JetBrains Toolbox (AppCode). Однако для сборки под Apple-платформы Xcode (точнее, его компоненты: SDK и signing tools) необходим — без него нельзя сгенерировать подписанный .ipa или .app.

Xcode и Swift — ключевые вехи

Xcode — IDE Apple для macOS, iOS, iPadOS, tvOS, watchOS и visionOS. Первая версия вышла в 2003; стабильные сборки бесплатны в Mac App Store, бета — на Apple Developer. Среда включает документацию Apple, Interface Builder для UIKit/AppKit и профилировщик Instruments (изначально Xray — интеграция DTrace из OpenSolaris, 2006).

Версия XcodeСвязь со Swift и инструментами
6 (2014)Первый публичный Swift, Playgrounds, SDK iOS 8 / OS X 10.10, тысячи новых API (Metal, HomeKit, HealthKit)
7 (2015)Swift 2, watchOS 2
8 (2016)Swift 3, расширенный LLDB
9 (2017)Swift 4, беспроводная отладка на устройстве, ARKit, Core ML
10 (2018)Ускоренный Interface Builder, Core ML 2, ARKit 2
11 (2019)SPM в IDE, SwiftUI, RealityKit, Mac Catalyst
12 (2020)Сборка под Apple Silicon
14 (2022)Metal 3
16 (2024)Swift 6, Swift Testing
26 (2025)Интеграция ИИ-ассистентов (ChatGPT и др.), Metal 4, MCP для внешних агентов

Toolchain Swift встроен в Xcode начиная с версии 6. Начиная с Xcode 9, компилятор не поддерживает Swift 2 и старше — миграция на Swift 3+ обязательна для актуальных проектов.


Swift Package Manager (SPM)

SPM — официальный менеджер пакетов, встроенный в компилятор Swift. Он решает три задачи:

  1. Описание зависимостей — в файле Package.swift указываются URL репозиториев, версии (через semantic versioning или ветки), а также целевые платформы.
  2. Сборка проекта — SPM умеет компилировать библиотеки (library), исполняемые файлы (executable), тесты (test) и плагины (например, для SwiftLint или Sourcery).
  3. Публикация пакетов — любой публичный Git-репозиторий с Package.swift может быть подключён как зависимость.

Пример Package.swift:

Код ITЗагрузка примера кода…

Разбор:

  • Что делает код — фрагмент показывает завершённый шаг решения задачи, где данные последовательно проходят через объявление, обработку и получение результата.
  • Как устроен поток выполнения — сначала задаются исходные сущности и их типы, затем выполняются операции и вызовы, после чего формируется итоговое состояние программы.
  • Отправная точка примера: выражение // swift-tools-version:5.9 задаёт контекст, от которого разворачивается логика этого фрагмента.
  • Ключевые слова и конструкции в этом примере: let, каждая из них отвечает за отдельную часть поведения программы.
  • Что важно при чтении: код выполняется сверху вниз, а проверки типов и корректности вызовов происходят ещё до запуска на этапе компиляции.
  • Практический смысл: такой паттерн удобно переносить в рабочий проект, выделяя повторяемые части в отдельные функции и упрощая тестирование.

SPM поддерживает транзитивные зависимости, условную компиляцию (#if canImport(UIKit)), ресурсы (изображения, локализации), плагины сборки — и всё это без внешних утилит вроде CocoaPods или Carthage.


Синтаксис и основы языка

Swift стремится к балансу между краткостью и читаемостью. Его синтаксис заимствует лучшие практики из множества языков (Haskell, Python, Rust, C#), но формирует собственную идентичность.


Объявление и управление состоянием

Переменные (var) и константы (let) — основа управления состоянием. Разделение на изменяемые и неизменяемые сущности не формальное — компилятор строго следит за тем, чтобы let не модифицировались, а var использовались осознанно. Иммутабельность в Swift влияет на оптимизации — компилятор может избежать копирования структур, если знает, что они не изменятся.

Типы выводятся автоматически, но явная аннотация разрешена и часто рекомендуется в публичных API:

let name = "Timur" // тип String выведен
let count: Int = 42 // явная аннотация
let point = (x: 10, y: 20) // кортеж с именованными элементами

Разбор:

  • Что делает код — фрагмент показывает завершённый шаг решения задачи, где данные последовательно проходят через объявление, обработку и получение результата.
  • Как устроен поток выполнения — сначала задаются исходные сущности и их типы, затем выполняются операции и вызовы, после чего формируется итоговое состояние программы.
  • Отправная точка примера: выражение let name = "Timur" // тип String выведен задаёт контекст, от которого разворачивается логика этого фрагмента.
  • Ключевые слова и конструкции в этом примере: let, каждая из них отвечает за отдельную часть поведения программы.
  • Что важно при чтении: код выполняется сверху вниз, а проверки типов и корректности вызовов происходят ещё до запуска на этапе компиляции.
  • Практический смысл: такой паттерн удобно переносить в рабочий проект, выделяя повторяемые части в отдельные функции и упрощая тестирование.

Опционалы (Optional<T>, синтаксический сахар T?) — полноценный перечисляемый тип с двумя случаями: .some(wrappedValue) и .none. Это позволяет использовать сопоставление с образцом (if case let .some(x) = value), цепочки вызовов (value?.method()), и заставляет явно обрабатывать отсутствие значения.


Управляющие конструкции

Swift не имеет неявного приведения типов в условиях. Выражение в if, while, guard должно иметь тип Bool — нельзя написать if number { … }, если number — целое. Это исключает классические ошибки вроде if (ptr = getValue()) вместо if (ptr == getValue()).

Конструкция guard — один из ключевых инструментов раннего выхода. Она требует, чтобы все условия в блоке else завершали область видимости (return, throw, break, continue). Это гарантирует, что за guard-блоком все необходимые предусловия выполнены, и дальнейший код может рассчитывать на корректность данных.

switch в Swift — полноценный инструмент сопоставления с образцом — он поддерживает диапазоны, перечисления с ассоциированными значениями, условия where, деструктуризацию кортежей и даже выражения. При этом он исчерпывающий: компилятор требует покрытия всех возможных случаев (или default), что предотвращает необработанные ветки.

Интерактивное демо — вызов функции и стек на примере JavaScript. В Swift объявление другое, но вызов, локальные переменные и возврат устроены так же. Обобщённо: функции в коде.

Play ITЗагрузка интерактивного демо…


Функции и замыкания

Функции в Swift — это именованные блоки кода с параметрами, возвращаемым типом и телом. Они могут быть вложенными, перегружены, иметь значения по умолчанию, метки аргументов и даже асинхронными (async/await). Замыкания — это функции без имени, захватывающие контекст. Их синтаксис оптимизирован для частых случаев:

  • Полная форма: { (a: Int, b: Int) -> Int in return a + b }
  • Сокращённая: { $0 + — }
  • Автоматический вывод типов и возврата упрощает передачу в API: array.sorted { $0.count > $1.count }

Swift поддерживает входные-выходные параметры (inout), но только с явной передачей адреса (&variable). Это делает побочные эффекты явными и контролируемыми.


Структуры, классы, перечисления

Swift предоставляет три первичных типа для моделирования:

  • Структуры (struct) — значимые типы, копируются при присваивании. Используются для большинства моделей данных, DTO, конфигураций. Поддерживают методы, вычисляемые свойства, инициализаторы.
  • Классы (class) — ссылочные типы, управляются через ARC. Используются там, где требуется совместное владение (делегаты, контроллеры, наблюдатели) или наследование.
  • Перечисления (enum) — типы с конечным набором случаев (cases). Могут быть простыми (case .on, .off), иметь ассоциированные значения (case .success(Data), .failure(Error)), сырцовые значения (enum HTTPStatus — Int { case ok = 200 }), соответствовать CaseIterable, Equatable, Codable.

Все три типа могут реализовывать протоколы, иметь расширения, участвовать в обобщённых контекстах. Выбор между ними — вопрос семантики: нужно ли совместное состояние? Если нет — предпочтение структурам.


Сфера применения Swift

Swift изначально позиционировался как язык для приложений Apple, но его потенциал шире.


Native-разработка под Apple-платформы

Это основная и самая развитая область. Swift позволяет писать приложения, полностью интегрированные в систему:

  • Доступ к аппаратным возможностям — камера (AVCaptureSession), микрофон (AVAudioEngine), геолокация (CoreLocation), биометрия (LocalAuthentication), сенсоры (CoreMotion), Apple Pencil (PencilKit), Touch Bar (macOS).
  • Построение сложных UI — от простых форм до анимированных переходов (withAnimation), кастомных жестов (Gesture), модальных представлений (sheet, fullScreenCover), динамических типов (DynamicTypeSize), тем (@Environment(\.colorScheme)).
  • Работа с сетьюURLSession для REST/GraphQL/WebSocket, Network framework (нижеуровневый, для фоновых задач), Combine для реактивной обработки потоков, интеграция с Firebase, CloudKit, AWS Amplify.
  • Медиа и графика — AVFoundation (видео/аудио), CoreGraphics (2D-рисование), CoreImage (фильтры), Metal (высокопроизводительная 3D-графика и compute shaders), ARKit/RealityKit (дополненная реальность).
  • Игры — SpriteKit (2D), SceneKit (3D), GameplayKit (ИИ, состояние, физика), а также интеграция с Unity/Unreal через нативные плагины.

SwiftUI, представленный в 2019 году, изменил парадигму: UI описывается как функция состояния. Вместо "создать кнопку → добавить в иерархию → установить действие → обновить при изменении" — пишется:

Button("Count: \(count)") { count += 1 }
.font(.title)
.foregroundColor(count.isMultiple(of: 2) ? .green : .blue)

Разбор:

  • Что делает код — фрагмент показывает завершённый шаг решения задачи, где данные последовательно проходят через объявление, обработку и получение результата.
  • Как устроен поток выполнения — сначала задаются исходные сущности и их типы, затем выполняются операции и вызовы, после чего формируется итоговое состояние программы.
  • Отправная точка примера: выражение Button("Count: \(count)") { count += 1 } задаёт контекст, от которого разворачивается логика этого фрагмента.
  • Что важно при чтении: код выполняется сверху вниз, а проверки типов и корректности вызовов происходят ещё до запуска на этапе компиляции.
  • Практический смысл: такой паттерн удобно переносить в рабочий проект, выделяя повторяемые части в отдельные функции и упрощая тестирование.

Система сама следит за изменениями @State var count и перерисовывает только затронутые части.


Серверная разработка

Swift используется для backend-сервисов через фреймворки:

  • Vapor — полноценный веб-фреймворк с маршрутизацией, middleware, ORM (Fluent), WebSocket, очередями.
  • Hummingbird — лёгкий, высокопроизводительный фреймворк от Apple, ориентированный на async/await.
  • Kitura (устаревает) — IBM-инициатива, больше не поддерживается.

Преимущества — производительность (ближе к Go/Rust, чем к Python/JS), типобезопасность, общая кодовая база с клиентом (например, общие модели данных через Codable), лёгкое развёртывание (один бинарник, без runtime).


Скрипты и автоматизация

Swift — скриптовый язык. Достаточно написать файл с расширением .swift и первой строкой:

#!/usr/bin/swift sh

import Foundation

print("Hello from Swift script!")

Разбор:

  • Что делает код — фрагмент показывает завершённый шаг решения задачи, где данные последовательно проходят через объявление, обработку и получение результата.
  • Как устроен поток выполнения — сначала задаются исходные сущности и их типы, затем выполняются операции и вызовы, после чего формируется итоговое состояние программы.
  • Отправная точка примера: выражение #!/usr/bin/swift sh задаёт контекст, от которого разворачивается логика этого фрагмента.
  • Что важно при чтении: код выполняется сверху вниз, а проверки типов и корректности вызовов происходят ещё до запуска на этапе компиляции.
  • Практический смысл: такой паттерн удобно переносить в рабочий проект, выделяя повторяемые части в отдельные функции и упрощая тестирование.

…и сделать его исполняемым: chmod +x script.swift && ./script.swift. Это позволяет использовать Swift для CI-скриптов, генерации кода, обработки логов — везде, где нужна надёжность и читаемость.

chmod +x script.swift && ./script.swift

Разбор:

  • Что делает код — фрагмент показывает завершённый шаг решения задачи, где данные последовательно проходят через объявление, обработку и получение результата.
  • Как устроен поток выполнения — сначала задаются исходные сущности и их типы, затем выполняются операции и вызовы, после чего формируется итоговое состояние программы.
  • Отправная точка примера: выражение chmod +x script.swift && ./script.swift задаёт контекст, от которого разворачивается логика этого фрагмента.
  • Что важно при чтении: код выполняется сверху вниз, а проверки типов и корректности вызовов происходят ещё до запуска на этапе компиляции.
  • Практический смысл: такой паттерн удобно переносить в рабочий проект, выделяя повторяемые части в отдельные функции и упрощая тестирование.

Образовательные и исследовательские проекты

Благодаря простоте синтаксиса и REPL (swift repl), Swift подходит для обучения программированию — от детей (через Swift Playgrounds на iPad) до университетских курсов. Открытость и кроссплатформенность позволяют использовать его в научных вычислениях (через Swift for TensorFlow, хотя проект заморожен, идеи живы), визуализации данных, даже в робототехнике.


Как Swift обеспечивает native-разработку с полной интеграцией в систему

Native-приложение на Swift — это системный процесс, который взаимодействует с операционной средой через строго определённые точки сопряжения. Swift сам по себе не знает, что такое "камера" или "уведомление" — эти понятия существуют в фреймворках (AVFoundation, UserNotifications, CoreLocation и др.), но именно за счёт строгой типизации, безопасной работы с ресурсами и глубокой интеграции с Objective-C runtime Swift становится идеальным проводником между высокоуровневой логикой и низкоуровневыми сервисами ОС.

Apple-платформы (iOS, macOS, watchOS, tvOS) построены на принципе sandboxing: приложение изолировано от других процессов и имеет доступ только к тем ресурсам, на которые получено разрешение. Swift помогает соблюдать эти ограничения на этапе компиляции: например, попытка вызвать CLLocationManager.requestWhenInUseAuthorization() без объявления ключа NSLocationWhenInUseUsageDescription в Info.plist не приведёт к ошибке компиляции, но вызовет крах при выполнении — однако Swift-экосистема (включая Xcode) предупреждает об этом ещё на этапе статического анализа, а начиная с iOS 15 — через runtime-валидацию entitlements.

Более того, Swift поддерживает capability-based security model: многие API требуют объявления в Info.plist и активации Capabilities в Xcode (например, Push Notifications, App Groups, Keychain Sharing). Это конфигурационные параметры, которые компилируются в entitlements-файл и проверяются при установке приложения. Строгая система типов делает невозможным использование, например, UNUserNotificationCenter без подключения фреймворка UserNotifications и без реализации протокола UNUserNotificationCenterDelegate, что исключает "забытые" шаги интеграции.

Таким образом, полная интеграция достигается за счёт ко-дизайна — Swift и фреймворки Apple развиваются параллельно, с учётом синтаксических, семантических и инструментальных требований друг друга. Например, появление async/await в Swift 5.5 совпало с обновлением URLSession.data(for:), CLLocationManager.requestLocation(), PHPhotoLibrary.requestAuthorization() — все они стали async, что позволило писать асинхронный код без замыканий и вложенных обработчиков.


SwiftUI (в двух словах)

SwiftUI (2019) — декларативный UI — вы описываете интерфейс как функцию от состояния (@State, @Binding, @Observable), система сама обновляет представление. Под капотом — property wrappers, some View и diffing дерева вью.

UIKit/AppKit по-прежнему нужны для сложной кастомизации и легаси; на практике часто комбинируют SwiftUI с UIViewRepresentable. Подробнее: раздел SwiftUI в статье про экосистему и сравнение SwiftUI и UIKit.


Безопасность типов

Строгая система типов в Swift — это не просто "нельзя присвоить строку числу". Это целостная стратегия верификации корректности, охватывающая время компиляции, время выполнения и даже этап проектирования.


Опционалы как замена null

Вместо nil (как в Objective-C) или null (как в Java/JS), Swift вводит Optional<T> — перечислимый тип с двумя случаями: .some(wrappedValue) и .none. Это означает:

  • Значение либо есть, либо его точно нет — третьего не дано.
  • Нельзя вызвать метод на опционале без распаковки (value?.method() — безопасный вызов, value!.method() — принудительный, с риском краха).
  • Компилятор требует обработки всех веток: в switch по Optional обязательно покрывать .none, в if let — блок else не обязателен, но логика за пределами if не имеет доступа к распакованному значению.

Опционалы распространяются рекурсивно: [[String?]?] — это опциональный массив опциональных массивов опциональных строк. Это кажется громоздким, но на практике такие структуры редки — и если они возникают, это сигнал о неясности семантики данных. Swift заставляет явно решить: может ли отсутствовать весь список? Может ли отсутствовать элемент списка? Может ли элемент быть пустым?


Контроль времени жизни объектов через ARC и weak/unowned

Swift использует автоматический подсчёт ссылок (ARC) — механизм, при котором объект удаляется, когда количество сильных ссылок на него падает до нуля. В отличие от сборщика мусора, ARC не вносит пауз, предсказуем по времени и совместим с C++.

Однако ARC не решает проблему циклических ссылок. Swift предоставляет два инструмента:

  • weak — ссылка, не увеличивающая счётчик. Автоматически обнуляется (nil), когда объект уничтожен. Тип всегда опциональный: weak var delegate: Protocol?.
  • unowned — ссылка, также не увеличивающая счётчик, но не обнуляемая. Если объект уничтожен, а unowned-ссылка используется — происходит крах. Применяется, когда жизненный цикл владельца строго короче, чем у цели (например, замыкание внутри объекта ссылается на самого объекта).

Выбор между weak и unowned — вопрос гарантий: если есть хоть малейший шанс, что цель может исчезнуть раньше — только weak.


Проверки на этапе компиляции

Swift выполняет множество проверок до запуска:

  • Все переменные инициализированы перед использованием (даже в сложных ветвлениях).
  • Все пути switch покрыты (или есть default).
  • Все потенциально выбрасывающие функции помечены throws, и вызовы обёрнуты в try.
  • Все inout-параметры передаются с &, и не могут быть константами.
  • Все mutating-методы вызываются только на изменяемых экземплярах (var, не let).

Эти проверки исключают целые классы ошибок — uninitialized variable, non-exhaustive switch, unhandled exception, mutating immutable value — всё это становится невозможным в корректном Swift-коде.


Реализация бизнес-логики

Swift особенно силён в реализации чистой бизнес-логики — той части приложения, которая не зависит от UI, сети или устройств. Это достигается за счёт:

  • Value types по умолчанию
    Структуры и перечисления копируются при передаче, что исключает неожиданные побочные эффекты. Например, модель User как struct гарантирует, что изменение user.name в одном месте не повлияет на другие ссылки.

  • Codable для сериализации
    Протокол Codable (объединяет Encodable и Decodable) позволяет автоматически генерировать код преобразования в JSON, Property List, а с библиотеками — в XML, YAML, Protocol Buffers. Компилятор генерирует CodingKeys, encode(to:), init(from:) на этапе компиляции, без рантайм-рефлексии.

  • Result<T, Error> для обработки асинхронных операций
    Вместо передачи замыканий с (value — T?, error: Error?), Swift поощряет использование Result:

func fetchUser(id: Int) async -> Result<User, NetworkError>

Разбор:

  • Что делает код — фрагмент показывает завершённый шаг решения задачи, где данные последовательно проходят через объявление, обработку и получение результата.

  • Как устроен поток выполнения — сначала задаются исходные сущности и их типы, затем выполняются операции и вызовы, после чего формируется итоговое состояние программы.

  • Отправная точка примера — выражение func fetchUser(id — Int) async -> Result<User, NetworkError> задаёт контекст, от которого разворачивается логика этого фрагмента.

  • Ключевые слова и конструкции в этом примере: func, каждая из них отвечает за отдельную часть поведения программы.

  • Что важно при чтении: код выполняется сверху вниз, а проверки типов и корректности вызовов происходят ещё до запуска на этапе компиляции.

  • Практический смысл: такой паттерн удобно переносить в рабочий проект, выделяя повторяемые части в отдельные функции и упрощая тестирование.

    Это позволяет применять функциональные операторы (map, flatMap, tryMap) и избегать pyramid of doom.

  • Вычисляемые свойства и наблюдаемые изменения
    Свойства с get/set, willSet/didSet позволяют инкапсулировать логику вычислений и реакций. Например, var fullName: String { "\(firstName) \(lastName)" } — всегда актуально, без ручного обновления.

  • Pattern matching и guard для валидации
    Сложные условия проверки (например, валидация формы) выражаются через guard, switch, where, что делает код линейным и легко тестируемым.

Такой подход позволяет вынести логику в отдельные модули (Core, Domain), независимо от платформы — и использовать их в iOS-приложении, сервере на Vapor и даже в Swift Playgrounds для обучения.


Работа с устройством

Полный доступ к возможностям устройства в Swift реализуется через фреймворки с типобезопасными обёртками над C-интерфейсами. Например:

  • AVCaptureSession (камера) — объектная обёртка над Core Media и IOKit.
  • CoreLocation — над Core Location Services.
  • LocalAuthentication — над Secure Enclave и biometric subsystem.

Swift не прячет сложность, но структурирует её:

  • Все вызовы, требующие авторизации, асинхронны и возвращают Result или используют completion handler с Error?.
  • Все параметры строго типизированы: AVCaptureDevice.Position.back, а не .backCamera как строка.
  • Все перечисления соответствуют CaseIterable, Equatable, RawRepresentable, что упрощает перебор и сериализацию.

Особое внимание — приватности. Начиная с iOS 14, Apple ввела transparency requirements — при первом доступе к микрофону, камере, фотоальбому появляется системный индикатор. Swift помогает соблюдать эти требования:

  • Методы вроде PHPhotoLibrary.requestAuthorization() требуют явного вызова.
  • Xcode проверяет наличие UsageDescription-ключей в Info.plist.
  • В тестах можно использовать XCTestExpectation и моки для проверки запросов разрешений.

Таким образом, Swift не "даёт доступ к камере" — он обязывает разработчика явно запросить, обработать отказ, предусмотреть fallback, и делает это через интерфейсы, которые невозможно использовать неправильно без сознательного нарушения типовой безопасности.


Интеграция с внешними системами

Swift не содержит встроенного HTTP-клиента, но обеспечивает строгую основу для построения надёжных сетевых слоёв поверх URLSession, Network framework или сторонних библиотек. Ключевой принцип — типобезопасная сериализация и обработка ошибок.


REST, GraphQL и WebSocket

Начиная с Swift 5.5, все современные сетевые API Apple поддерживают async/await. Например, URLSession.data(for:) возвращает (data, URLResponse), а не принимает completion handler. Это позволяет писать:

do {
let (data, _) = try await URLSession.shared.data(from: url)
let user = try JSONDecoder().decode(User.self, from: Data)
// обработка user
} catch {
// обработка ошибки сети или десериализации
}

Разбор:

  • Что делает код — фрагмент показывает завершённый шаг решения задачи, где данные последовательно проходят через объявление, обработку и получение результата.
  • Как устроен поток выполнения — сначала задаются исходные сущности и их типы, затем выполняются операции и вызовы, после чего формируется итоговое состояние программы.
  • Отправная точка примера: выражение do { задаёт контекст, от которого разворачивается логика этого фрагмента.
  • Ключевые слова и конструкции в этом примере — try, await, let, каждая из них отвечает за отдельную часть поведения программы.
  • Что важно при чтении: код выполняется сверху вниз, а проверки типов и корректности вызовов происходят ещё до запуска на этапе компиляции.
  • Практический смысл: такой паттерн удобно переносить в рабочий проект, выделяя повторяемые части в отдельные функции и упрощая тестирование.

Обратите внимание — ошибка может быть как сетевой (таймаут, недоступность), так и семантической (невалидный JSON). Swift не разделяет их на уровне типа — обе реализуют Error, и обрабатываются в одном catch. Но при необходимости можно использовать Result в связке с async:

func fetchUser(id: Int) async -> Result<User, NetworkError> {
do {
let (data, _) = try await URLSession.shared.data(from: userURL(id))
let user = try JSONDecoder().decode(User.self, from: Data)
return .success(user)
} catch let error as URLError {
return .failure(.network(error))
} catch DecodingError.dataCorrupted {
return .failure(.invalidResponse)
} catch {
return .failure(.unknown)
}
}

Разбор:

  • Что делает код — фрагмент показывает завершённый шаг решения задачи, где данные последовательно проходят через объявление, обработку и получение результата.
  • Как устроен поток выполнения — сначала задаются исходные сущности и их типы, затем выполняются операции и вызовы, после чего формируется итоговое состояние программы.
  • Отправная точка примера — выражение func fetchUser(id — Int) async -> Result<User, NetworkError> { задаёт контекст, от которого разворачивается логика этого фрагмента.
  • Ключевые слова и конструкции в этом примере — func, return, try, await, let, каждая из них отвечает за отдельную часть поведения программы.
  • Что важно при чтении: код выполняется сверху вниз, а проверки типов и корректности вызовов происходят ещё до запуска на этапе компиляции.
  • Практический смысл: такой паттерн удобно переносить в рабочий проект, выделяя повторяемые части в отдельные функции и упрощая тестирование.

Такой подход изолирует сетевую логику от UI: SwiftUI-вью может вызывать Task { await viewModel.load() }, а viewModel — возвращать @Published var state: LoadingState<User>, где LoadingState — перечисление .idle, .loading, .success(User), .failure(Error). Это обеспечивает единый поток данных и предсказуемое поведение при обновлениях.

GraphQL (например, через Apollo iOS) и WebSocket (через URLSession.webSocketTask) интегрируются аналогично:

  • Для GraphQL генерируются типобезопасные запросы и модели на основе схемы — компилятор проверяет соответствие полей.
  • Для WebSocket Swift использует async-итераторы: for try await message in webSocketTask.messages {}, что исключает потерю сообщений и упрощает управление жизненным циклом соединения.

Облачные сервисы

Интеграция с облачными платформами в Swift строится на тех же принципах:

  • Firebase SDK предоставляет DocumentReference.getDocuments() как async throws, а addSnapshotListener — через Combine (Publisher), либо через замыкания с Result.
  • CloudKit использует CKContainer, CKDatabase, CKQuery, где все операции — асинхронные и возвращают Result<CKRecord, Error>. Swift делает невозможным игнорировать ошибки: если не обработать Result, компилятор выдаст предупреждение.
  • AWS Amplify генерирует клиентский код по GraphQL-схеме, обеспечивая строгую типизацию запросов, мутаций и подписок.

Важно: все эти SDK не требуют Objective-C bridging headers. Они написаны на Swift или имеют Swift-compatible API, что гарантирует нулевую стоимость абстракции и полную интеграцию с системой типов.


Работа с медиа

Swift не обрабатывает медиа напрямую — этим занимаются фреймворки (AVFoundation, CoreImage, CoreGraphics, Metal). Но Swift обеспечивает безопасную композицию этих инструментов.


AVFoundation

AVCaptureSession, AVAssetReader, AVPlayer — все они требуют явного управления состоянием:

  • Сессия камеры должна быть startRunning() и stopRunning(), иначе ресурсы не освобождаются.
  • Чтение аудио/видео — через AVAssetReader, который выбрасывает ошибку, если файл повреждён.
  • Воспроизведение — через AVPlayer, который уведомляет о состоянии через Combine (publisher(for: \.status)).

Swift помогает избежать утечек:

  • AVCaptureSession — ссылочный тип, но его следует хранить как weak в делегатах.
  • AVPlayerLayer не владеет AVPlayer — при уничтожении слоя плеер продолжает работать, если на него есть сильная ссылка. Swift заставляет явно продумать владение.

Core Image и Metal

CIImage — неизменяемый тип, представляющий описание изображения (цепочку фильтров), а не пиксели. Это позволяет строить сложные пайплайны без промежуточных буферов:

let filter = CIFilter(name: "CISepiaTone")!
filter.setValue(inputImage, forKey: kCIInputImageKey)
filter.setValue(0.8, forKey: kCIInputIntensityKey)
let outputImage = filter.outputImage

Разбор:

  • Что делает код — фрагмент показывает завершённый шаг решения задачи, где данные последовательно проходят через объявление, обработку и получение результата.
  • Как устроен поток выполнения — сначала задаются исходные сущности и их типы, затем выполняются операции и вызовы, после чего формируется итоговое состояние программы.
  • Отправная точка примера: выражение let filter = CIFilter(name: "CISepiaTone")! задаёт контекст, от которого разворачивается логика этого фрагмента.
  • Ключевые слова и конструкции в этом примере: let, каждая из них отвечает за отдельную часть поведения программы.
  • Что важно при чтении: код выполняется сверху вниз, а проверки типов и корректности вызовов происходят ещё до запуска на этапе компиляции.
  • Практический смысл: такой паттерн удобно переносить в рабочий проект, выделяя повторяемые части в отдельные функции и упрощая тестирование.

Metal — низкоуровневый API для GPU-вычислений. В Swift он интегрируется через:

  • MTLDevice, MTLCommandQueue, MTLComputePipelineState — все типы строго проверяются.
  • Шейдеры компилируются в metallib на этапе сборки, и ошибка синтаксиса шейдера вызывает ошибку компиляции приложения — ещё до запуска.

Фреймворки вроде Accelerate (vDSP, vImage) предоставляют высокоуровневые функции с async-интерфейсами, а SwiftUI позволяет встраивать MetalView напрямую в декларативный UI.


AR и VR

ARKit и RealityKit — это системы пространственного моделирования, где Swift обеспечивает строгую привязку к физике и геометрии.


ARKit

ARSession управляет потоком данных с камеры, акселерометра, гироскопа. Swift гарантирует:

  • Все делегаты (ARSessionDelegate) требуют реализации методов с конкретными типами: session(_:didUpdate:) получает ARFrame, а не Any.
  • ARAnchor — базовый тип для привязки объектов к миру; его подтипы (ARImageAnchor, ARFaceAnchor, ARObjectAnchor) строго разделены.
  • Ошибки трекинга (ARSessionInterruptionReason, ARSessionRunOption) — перечисления, а не строки, что исключает опечатки.

RealityKit

RealityKit строит сцены из Entity (геометрия), Component (поведение), System (логика). Это entity-component-Система (ECS), и Swift делает его типобезопасным:

  • ModelEntity содержит MeshResource, Material, CollisionShape.
  • PhysicsBodyComponent, Transform, InputComponent — структуры, добавляемые через addComponent(_:).
  • Системы (System) обрабатывают только те сущности, у которых есть нужные компоненты — проверка на этапе компиляции.

Например, чтобы добавить реакцию на касание:

let entity = ModelEntity(mesh: .generateBox(), materials: [material])
entity.components.set(InputComponent())
arView.installGestures(.tap, for: entity)

Разбор:

  • Что делает код — фрагмент показывает завершённый шаг решения задачи, где данные последовательно проходят через объявление, обработку и получение результата.
  • Как устроен поток выполнения — сначала задаются исходные сущности и их типы, затем выполняются операции и вызовы, после чего формируется итоговое состояние программы.
  • Отправная точка примера: выражение let entity = ModelEntity(mesh: .generateBox(), materials: [material]) задаёт контекст, от которого разворачивается логика этого фрагмента.
  • Ключевые слова и конструкции в этом примере — for, let, каждая из них отвечает за отдельную часть поведения программы.
  • Что важно при чтении: код выполняется сверху вниз, а проверки типов и корректности вызовов происходят ещё до запуска на этапе компиляции.
  • Практический смысл: такой паттерн удобно переносить в рабочий проект, выделяя повторяемые части в отдельные функции и упрощая тестирование.

Здесь installGestures принимает только Entity, у которого есть InputComponent — попытка передать обычный Entity вызовет ошибку компиляции. Это design-by-contract на уровне языка.


Игры и графические приложения — SpriteKit, Metal, Unity

Swift не является игровым языком "из коробки", но его характеристики делают его привлекательным для разработки игр — особенно 2D и прототипов.


SpriteKit — декларативная анимация и физика

SKScene, SKNode, SKSpriteNode, SKAction — объектная модель, где:

  • Анимации строятся через композицию SKActionsequence([move, rotate, fade]).
  • Физика — через physicsBody, с коллизиями по категориям (collisionBitMask, categoryBitMask), типизированным как UInt32, но часто обёрнутым в OptionSet.

Swift усиливает безопасность:

  • SKAction.customAction(withDuration:actionBlock:) принимает замыкание с currentTime: TimeInterval, но не позволяет модифицировать сцену вне главного потока — Xcode выдаст runtime-предупреждение.
  • SKShader использует Metal Shading Language, и ошибки шейдера обнаруживаются при загрузке, а не при рендеринге.

Интеграция с Unity и Unreal Engine

Swift может быть использован как нативный плагин:

  • В Unity — через DllImport("__Internal") и C-интерфейс (обёртка на Swift с @_cdecl).
  • В Unreal — через UCLASS, UFUNCTION, с генерацией bindings.

Ключевое — межъязыковая ABI-совместимость. Swift гарантирует, что struct с @frozen layout имеет тот же бинарный формат, что и в C. Это позволяет передавать данные без копирования: например, UnsafeBufferPointer<GLfloat> напрямую в OpenGL/Metal.


Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.

Содержание