# Swift: Полный справочник для разработчика с опытом Java/Kotlin > **Целевая аудитория:** разработчики, знакомые с Java/Kotlin/Python, планирующие разрабатывать приложения для macOS, iOS и watchOS. > **Версия:** Swift 6.x (актуально на 2026 год) --- ## Содержание 1. [Общее введение](#общее-введение) 2. [Синтаксические основы](#синтаксические-основы) 3. [Типы данных и коллекции](#типы-данных-и-коллекции) 4. [Управляющие конструкции](#управляющие-конструкции) 5. [Функции и замыкания](#функции-и-замыкания) 6. [Optionals — нулевая безопасность](#optionals--нулевая-безопасность) 7. [Перечисления (Enum)](#перечисления-enum) 8. [Структуры и классы](#структуры-и-классы) 9. [Протоколы и расширения](#протоколы-и-расширения) 10. [Обобщённые типы (Generics)](#обобщённые-типы-generics) 11. [Управление памятью — ARC](#управление-памятью--arc) 12. [Обработка ошибок](#обработка-ошибок) 13. [Конкурентность — async/await и Actors](#конкурентность--asyncawait-и-actors) 14. [Свойства: computed, observers, wrappers](#свойства-computed-observers-wrappers) 15. [Паттерн-матчинг](#паттерн-матчинг) 16. [Макросы (Swift 6)](#макросы-swift-6) 17. [SwiftUI, UIKit, AppKit, WatchKit](#swiftui-uikit-appkit-watchkit) 18. [Экосистема и инструменты](#экосистема-и-инструменты) 19. [Таблица сравнений Swift vs Kotlin](#таблица-сравнений-swift-vs-kotlin) --- ## Общее введение Swift — статически типизированный, компилируемый язык общего назначения от Apple (2014), разработанный как замена Objective-C. Он открыт с 2015 года и доступен на [swift.org](https://swift.org). Swift работает поверх LLVM и компилируется в нативный машинный код. ### Ключевые особенности - **Статическая типизация + вывод типов** — компилятор сам выводит типы из контекста. - **Value Semantics** — `struct` и `enum` копируются при передаче, что снижает количество багов с общим состоянием. - **ARC (Automatic Reference Counting)** — нет GC, нет пауз (в отличие от JVM). - **Нулевая безопасность** — `nil` возможен только в `Optional` (аналог `T?` в Kotlin, но реализован иначе). - **Protocol-Oriented Programming** — протоколы мощнее интерфейсов Kotlin: поддерживают дефолтные реализации через `extension`, associated types, `where`-ограничения. - **Structured Concurrency** — `async/await` и `actor` встроены в язык (Swift 5.5+, расширено в Swift 6). - **Swift 6 (2024)** — строгая проверка конкурентности на уровне компилятора, Typed Throws, Embedded Swift, расширенная интероп с C++. --- ## Синтаксические основы ### Переменные и константы ```swift let name: String = "Alice" // константа (аналог val в Kotlin) var age = 30 // переменная, тип Int выведен автоматически var score: Double = 9.5 // Несколько присваиваний var x = 0, y = 0, z = 0 ``` > **vs Kotlin:** `val`/`var` → `let`/`var`. Отличие: в Swift `let` на struct запрещает изменение любых свойств, а `let` на class — только перепривязывание ссылки (как в Kotlin `val`). ### Типы данных | Swift | Kotlin/Java | Описание | |-------------|-----------------|---------------------------| | `Int` | `Int`/`Long` | 64-bit на 64-bit платформах | | `Double` | `Double` | 64-bit floating point | | `Float` | `Float` | 32-bit floating point | | `Bool` | `Boolean` | `true` / `false` | | `String` | `String` | Unicode, value type | | `Character` | `Char` | Один символ Unicode | | `Void` | `Unit` | Отсутствие возвращаемого значения | | `Never` | `Nothing` | Функция никогда не вернётся | | `Any` | `Any` | Любой тип | | `AnyObject` | нет прямого аналога | Любой ссылочный тип | ### Строки ```swift let greeting = "Hello" let name = "World" // Интерполяция строк — \() вместо ${} в Kotlin let msg = "Hello, \(name)! 2 + 2 = \(2 + 2)" // Многострочная строка let multiline = """ Первая строка Вторая строка """ // String — value type (struct), не Reference let upper = name.uppercased() let count = name.count let hasPrefix = name.hasPrefix("Wo") ``` ### Комментарии ```swift // Однострочный комментарий /* Многострочный комментарий */ /// Документация (аналог /** */ в Kotlin/Java) /// - Parameter x: описание параметра /// - Returns: описание результата func square(_ x: Int) -> Int { x * x } ``` ### Точка с запятой Точка с запятой не нужна в конце строки (опциональна). Обязательна только для нескольких операторов на одной строке: ```swift let a = 1; let b = 2 // допустимо, но не рекомендуется ``` --- ## Типы данных и коллекции ### Array ```swift // Создание var numbers: [Int] = [1, 2, 3] var empty = [String]() // пустой массив var repeated = Array(repeating: 0, count: 5) // [0,0,0,0,0] // Операции numbers.append(4) numbers.insert(0, at: 0) numbers.remove(at: 2) let first = numbers.first // Optional let count = numbers.count // Итерация for n in numbers { print(n) } for (index, value) in numbers.enumerated() { print("\(index): \(value)") } // Функции высшего порядка (аналог Kotlin) let doubled = numbers.map { $0 * 2 } let evens = numbers.filter { $0 % 2 == 0 } let sum = numbers.reduce(0, +) let sorted = numbers.sorted() ``` ### Dictionary ```swift var dict: [String: Int] = ["a": 1, "b": 2] dict["c"] = 3 dict["a"] = nil // удаление ключа let val = dict["a"] // Optional — ключ может отсутствовать let safe = dict["z"] ?? 0 // значение по умолчанию через nil-coalescing // Итерация for (key, value) in dict { print("\(key) → \(value)") } ``` ### Set ```swift var fruits: Set = ["apple", "banana", "cherry"] fruits.insert("mango") fruits.remove("apple") let contains = fruits.contains("banana") // true // Теоретико-множественные операции let a: Set = [1, 2, 3] let b: Set = [2, 3, 4] let union = a.union(b) // {1,2,3,4} let intersection = a.intersection(b) // {2,3} let difference = a.subtracting(b) // {1} ``` ### Tuple Кортежи — лёгкий способ вернуть несколько значений. **В Kotlin нет прямого аналога** (только `Pair`/`Triple`). ```swift let point = (3.0, 4.0) let (x, y) = point // destructuring // Именованные поля let http = (statusCode: 200, description: "OK") print(http.statusCode) print(http.description) // Возврат нескольких значений из функции func minMax(_ arr: [Int]) -> (min: Int, max: Int) { return (arr.min()!, arr.max()!) } let result = minMax([1, 5, 3]) print(result.min, result.max) ``` ### Range ```swift let closed = 1...5 // 1, 2, 3, 4, 5 (аналог 1..5 в Kotlin) let half = 1..<5 // 1, 2, 3, 4 (аналог 1 until 5 в Kotlin) let from = 1... // от 1 до бесконечности (PartialRangeFrom) let upTo = ..<5 // до 4 включительно for i in 1...5 { print(i) } let slice = [10, 20, 30, 40, 50][1...3] // [20, 30, 40] ``` --- ## Управляющие конструкции ### if / else ```swift let temp = 25 if temp > 30 { print("Жарко") } else if temp > 20 { print("Тепло") } else { print("Прохладно") } // В Swift if НЕ является выражением (в отличие от Kotlin) // Используй тернарный оператор: let label = temp > 30 ? "Жарко" : "Нормально" ``` > **Swift 5.9+:** Появился `if`/`switch` как выражение: > ```swift > let label = if temp > 30 { "Жарко" } else { "Нормально" } > ``` ### switch В Swift `switch` гораздо мощнее Java/Kotlin — поддерживает паттерн-матчинг, ranges, tuples, where-условия. **Не нужен `break` — нет fall-through по умолчанию.** ```swift let score = 85 switch score { case 90...100: print("Отлично") case 75..<90: print("Хорошо") case 60..<75: print("Удовлетворительно") default: print("Неудовлетворительно") } // Паттерн-матчинг с where let point = (1, -1) switch point { case let (x, y) where x == y: print("На диагонали") case let (x, y) where x == -y: print("На антидиагонали") case (let x, let y): print("(\(x), \(y))") } ``` ### guard `guard` — уникальная для Swift конструкция раннего выхода. Аналог `require()` в Kotlin, но встроен в язык. ```swift func processAge(_ age: Int?) { guard let age = age else { print("Возраст не указан") return // обязательный ранний выход } // age доступен здесь как Non-Optional Int guard age >= 0 && age < 150 else { print("Некорректный возраст") return } print("Возраст: \(age)") } ``` > `guard let` предпочтительнее `if let`, когда нужно избежать вложенности (Pyramid of Doom). ### for-in, while, repeat-while ```swift // for-in for i in 0..<5 { print(i) } for char in "Hello" { print(char) } // stride — аналог Kotlin step for i in stride(from: 0, to: 10, by: 2) { print(i) } // 0,2,4,6,8 for i in stride(from: 10, through: 0, by: -2) { print(i) } // while var n = 1 while n < 100 { n *= 2 } // repeat-while — аналог do-while в Java/Kotlin repeat { n /= 2 } while n > 1 ``` --- ## Функции и замыкания ### Объявление функций ```swift // Базовый синтаксис: func имя(метка параметра: тип) -> ТипВозврата func greet(person name: String, from city: String) -> String { return "Привет, \(name) из \(city)!" } // Вызов — именованные аргументы обязательны (отличие от Kotlin) greet(person: "Иван", from: "Москва") // _ — убирает внешнюю метку параметра func add(_ a: Int, _ b: Int) -> Int { a + b } add(3, 5) // без меток // Параметры по умолчанию func connect(host: String, port: Int = 8080) { } connect(host: "localhost") // Variadic параметры func sum(_ numbers: Int...) -> Int { numbers.reduce(0, +) } sum(1, 2, 3, 4, 5) // inout — передача по ссылке (аналог ref в C#) func doubleInPlace(_ value: inout Int) { value *= 2 } var x = 5 doubleInPlace(&x) // x == 10 ``` > **vs Kotlin:** В Swift функции объявляются через `func`, Kotlin через `fun`. Тип возврата: Swift использует `->`, Kotlin `:`. > Значительное отличие — метки аргументов (argument labels): внешняя метка для читаемости API. ### Функции как типы первого класса ```swift // Функция как тип var operation: (Int, Int) -> Int = { $0 + $1 } operation(3, 4) // 7 // Функция высшего порядка func apply(_ op: (Int, Int) -> Int, to a: Int, and b: Int) -> Int { op(a, b) } apply(+, to: 5, and: 3) // 8 apply(*, to: 5, and: 3) // 15 ``` ### Замыкания (Closures) Замыкания — анонимные функции, захватывающие контекст. Аналог лямбд в Kotlin. ```swift // Полный синтаксис let multiply: (Int, Int) -> Int = { (a: Int, b: Int) -> Int in return a * b } // Сокращённый синтаксис (trailing closure) let numbers = [3, 1, 4, 1, 5, 9] let sorted = numbers.sorted { $0 < $1 } // $0, $1 — shorthand arguments let sorted2 = numbers.sorted(by: <) // ещё короче // Trailing closure синтаксис — если последний аргумент — замыкание numbers.forEach { print($0) } // Несколько trailing closures (Swift 5.3+) UIView.animate(withDuration: 0.3) { // анимация } completion: { finished in // по завершению } ``` ### Захват значений (Capture List) ```swift var counter = 0 let increment = { counter += 1 } increment() // counter == 1 // Захват по значению через capture list var value = 10 let closure = { [value] in // захватывает копию value на момент создания print(value) } value = 20 closure() // выведет 10, не 20 // [weak self] — слабая ссылка, предотвращает retain cycle class MyClass { var name = "Test" func doWork() { DispatchQueue.global().async { [weak self] in guard let self = self else { return } print(self.name) } } } ``` > **Важно:** Без `[weak self]` в замыкании, захватывающем `self`, возникает retain cycle — `self` держит замыкание, замыкание держит `self`. Это классическая утечка памяти в Swift. --- ## Optionals — нулевая безопасность Optional — это **обёртка над значением**, реализованная как `enum Optional { case some(Wrapped); case none }`. Принципиальное отличие от Kotlin: Swift Optional — не просто аннотация типа, а реальный тип-обёртка. ### Объявление ```swift var name: String? = "Alice" // Optional var age: Int? = nil // nil === .none // Non-optional — никогда не nil var definite: String = "Always here" ``` ### Unwrapping ```swift // 1. Forced Unwrapping (!) — опасно, может crash let length = name!.count // crash если nil // 2. Optional Binding — if let if let unwrapped = name { print("Имя: \(unwrapped)") // unwrapped — String, не String? } // Shorthand (Swift 5.7+): имя переменной совпадает if let name { print(name) } // 3. guard let — ранний выход + unwrap func process(_ value: String?) { guard let value else { return } print(value) // value доступен как String дальше в функции } // 4. Nil-Coalescing Operator (??) — значение по умолчанию let displayName = name ?? "Аноним" // 5. Optional Chaining (?.) — безопасная цепочка let count = name?.count // Optional let upper = name?.uppercased() // Optional // Цепочка: если любое звено nil — весь результат nil struct User { var address: Address? } struct Address { var city: String? } let user: User? = User(address: Address(city: "Москва")) let city = user?.address?.city // Optional // 6. Implicitly Unwrapped Optional (T!) — используй с осторожностью // Типично для IBOutlet и случаев, когда инициализация отложена @IBOutlet weak var label: UILabel! ``` ### Pattern Matching с Optional ```swift switch name { case .some(let value): print("Есть значение: \(value)") case .none: print("Нет значения") } // Краткий вариант switch name { case let value?: print(value) case nil: print("nil") } ``` --- ## Перечисления (Enum) Swift `enum` — один из самых мощных инструментов языка, несравнимо богаче Java/Kotlin перечислений. ### Базовый enum ```swift enum Direction { case north, south, east, west } let dir = Direction.north ``` ### Raw Values ```swift enum Planet: Int { case mercury = 1, venus, earth, mars // автоинкремент } let earth = Planet(rawValue: 3) // Optional print(Planet.venus.rawValue) // 2 enum HTTPMethod: String { case get = "GET" case post = "POST" case put = "PUT" case delete = "DELETE" } print(HTTPMethod.get.rawValue) // "GET" ``` ### Associated Values — мощнейшая особенность Swift ```swift enum NetworkResult { case success(Data) case failure(Error) case loading(progress: Double) } // Работа с associated values let result: NetworkResult = .success(someData) switch result { case .success(let data): print("Данные: \(data)") case .failure(let error): print("Ошибка: \(error)") case .loading(let progress): print("Загрузка: \(progress * 100)%") } // if case — проверка одного конкретного кейса if case .loading(let p) = result { print("Загрузка: \(p)") } ``` > **vs Kotlin:** Ближайший аналог `sealed class`. Swift enum с associated values компактнее и поддерживает exhaustive switch без `else`. ### Рекурсивный enum (indirect) ```swift indirect enum BinaryTree { case leaf case node(value: T, left: BinaryTree, right: BinaryTree) } ``` ### Методы и computed properties в enum ```swift enum Suit: String { case hearts, diamonds, clubs, spades var isRed: Bool { return self == .hearts || self == .diamonds } func display() { print("Масть: \(rawValue), красная: \(isRed)") } } Suit.hearts.display() ``` ### CaseIterable ```swift enum Season: CaseIterable { case spring, summer, autumn, winter } Season.allCases.forEach { print($0) } ``` --- ## Структуры и классы ### Фундаментальное различие: Value vs Reference | Характеристика | `struct` (value type) | `class` (reference type) | |---------------------|-----------------------|---------------------------| | Хранение | Стек (обычно) | Куча | | Передача | Копирование | Ссылка | | Наследование | Нет | Да | | `deinit` | Нет | Да | | Управление памятью | - | ARC | | Mutability | `let` запрещает всё | `let` запрещает перепривязывание | | Аналог в Kotlin | `data class` (но ref) | `class` | > **Рекомендация Apple:** По умолчанию используй `struct`. Переходи на `class`, когда нужно наследование, идентичность объекта или жизненный цикл. ### struct ```swift struct Point { var x: Double var y: Double // Memberwise initializer генерируется автоматически // Computed property var magnitude: Double { (x * x + y * y).squareRoot() } // Mutating method — обязательно, т.к. struct — value type mutating func translate(dx: Double, dy: Double) { x += dx y += dy } // Static method static func origin() -> Point { Point(x: 0, y: 0) } } var p = Point(x: 3, y: 4) print(p.magnitude) // 5.0 p.translate(dx: 1, dy: 0) let immutable = Point(x: 1, y: 2) // immutable.x = 5 // Ошибка: нельзя изменить let struct ``` ### class ```swift class Animal { var name: String private var energy: Int = 100 init(name: String) { self.name = name } func eat() { energy += 10 print("\(name) ест") } // Деструктор deinit { print("\(name) удалён из памяти") } } class Dog: Animal { var breed: String init(name: String, breed: String) { self.breed = breed super.init(name: name) // super.init обязателен } override func eat() { super.eat() print("\(name) виляет хвостом!") } // final — запрещает переопределение final func bark() { print("Гав!") } } ``` ### Инициализаторы в деталях ```swift class Vehicle { var make: String var year: Int // Designated initializer — основной init(make: String, year: Int) { self.make = make self.year = year } // Convenience initializer — вторичный, вызывает designated convenience init(make: String) { self.init(make: make, year: 2024) } // Failable initializer — возвращает Optional init?(rawYear: String) { guard let year = Int(rawYear) else { return nil } self.make = "Unknown" self.year = year } } ``` ### `actor` — для безопасной конкурентности (Swift 5.5+) ```swift actor BankAccount { private var balance: Double = 0 func deposit(_ amount: Double) { balance += amount } func getBalance() -> Double { balance } } // Использование (требует await) let account = BankAccount() await account.deposit(100) let bal = await account.getBalance() ``` `actor` — это `class` с автоматической сериализацией доступа. Обращение к методам актора из внешнего кода всегда `async`. --- ## Протоколы и расширения ### Protocol — аналог interface + trait в Kotlin ```swift protocol Drawable { var color: String { get } // read-only property var area: Double { get set } // read-write property func draw() func resize(factor: Double) -> Self // Self requirement // Инициализатор в протоколе init(color: String) } // Conformance struct Circle: Drawable { var color: String var radius: Double var area: Double { get { Double.pi * radius * radius } set { radius = (newValue / Double.pi).squareRoot() } } init(color: String) { self.color = color; self.radius = 1.0 } func draw() { print("Рисую \(color) круг") } func resize(factor: Double) -> Circle { Circle(color: color) // упрощённо } } ``` ### Protocol Inheritance и Composition ```swift protocol Printable { func printDescription() } protocol Serializable { func serialize() -> Data } // Протокол наследует другие протоколы protocol Document: Printable, Serializable { var title: String { get } } // Composition через & — без создания нового протокола func process(_ item: Printable & Serializable) { } ``` ### Расширения (Extensions) ```swift // Добавление методов к существующим типам (даже стандартной библиотеки!) extension String { var isPalindrome: Bool { self == String(self.reversed()) } func trimmed() -> String { trimmingCharacters(in: .whitespacesAndNewlines) } } "level".isPalindrome // true " hello ".trimmed() // Ретроактивный conformance — добавляем протокол к чужому типу extension Int: CustomStringConvertible { public var description: String { "Число \(self)" } } ``` ### Protocol Extensions с дефолтными реализациями ```swift protocol Greetable { var name: String { get } func greet() -> String } // Дефолтная реализация через extension extension Greetable { func greet() -> String { return "Привет, \(name)!" } } struct Person: Greetable { var name: String // greet() уже реализован через extension } // Условный extension — расширение с ограничением extension Array where Element: Comparable { func sortedUnique() -> [Element] { Array(Set(self)).sorted() } } [3, 1, 2, 1, 3].sortedUnique() // [1, 2, 3] ``` ### Existential Types (any) vs Generic Constraints (some) Swift 5.7 ввёл важные ключевые слова: ```swift // any — existential type (упаковка в коробку, dynamic dispatch) // Используй, когда тип может быть разным в runtime var shapes: [any Drawable] = [] // some — opaque type (конкретный тип скрыт, static dispatch) // Используй в возвращаемых типах когда тип один конкретный func makeShape() -> some Drawable { Circle(color: "red") } // SwiftUI активно использует some View struct ContentView: View { var body: some View { // конкретный тип известен компилятору Text("Hello") } } ``` --- ## Обобщённые типы (Generics) ### Generic Functions ```swift func swap(_ a: inout T, _ b: inout T) { let temp = a a = b b = temp } var x = 5, y = 10 swap(&x, &y) // x=10, y=5 ``` ### Generic Types ```swift struct Stack { private var items: [Element] = [] mutating func push(_ item: Element) { items.append(item) } mutating func pop() -> Element? { items.popLast() } var top: Element? { items.last } } var stack = Stack() stack.push(1) stack.push(2) print(stack.pop()) // 2 ``` ### Type Constraints ```swift // T должен соответствовать протоколу Comparable func findMax(_ array: [T]) -> T? { array.max() } // where clause — сложные ограничения func merge(_ dict1: [T: U], _ dict2: [T: U]) -> [T: U] where T: Hashable, U: Equatable { dict1.merging(dict2) { $1 } } ``` ### Associated Types в протоколах ```swift protocol Container { associatedtype Item // тип-заполнитель var count: Int { get } mutating func append(_ item: Item) subscript(i: Int) -> Item { get } } struct IntStack: Container { typealias Item = Int // можно опустить — компилятор выведет сам private var items: [Int] = [] var count: Int { items.count } mutating func append(_ item: Int) { items.append(item) } subscript(i: Int) -> Int { items[i] } } ``` ### Primary Associated Types (Swift 5.7+) ```swift // Указываем primary associated type в угловых скобках protocol Collection { associatedtype Element } // Теперь можно использовать как параметризованный existential var coll: any Collection // вместо any Collection ``` --- ## Управление памятью — ARC Swift использует **Automatic Reference Counting (ARC)** — нет GC, нет пауз. Память освобождается детерминировано. ### Принцип работы 1. Создание объекта → счётчик ссылок = 1 2. Каждое присваивание или передача в функцию → счётчик ++ 3. Выход из scope → счётчик -- 4. Счётчик = 0 → вызывается `deinit` → освобождение памяти ### Strong, Weak, Unowned ```swift class Person { var name: String var apartment: Apartment? init(name: String) { self.name = name } deinit { print("\(name) deinitialized") } } class Apartment { var unit: String // weak — опциональная слабая ссылка, не удерживает объект // обнуляется автоматически при удалении объекта weak var tenant: Person? init(unit: String) { self.unit = unit } } var john: Person? = Person(name: "John") var apt: Apartment? = Apartment(unit: "4B") john?.apartment = apt apt?.tenant = john // weak — нет retain cycle john = nil // Person deinitialized — нет утечки ``` ```swift // unowned — не удерживает, но НЕ обнуляется (нет Optional) // Используй, когда уверен, что объект живёт не меньше ссылающегося class CreditCard { let number: String unowned let customer: Customer // customer всегда живёт дольше карты init(number: String, customer: Customer) { self.number = number self.customer = customer } } ``` ### Retain Cycle в замыканиях ```swift class Timer { var handler: (() -> Void)? var count = 0 func start() { // Retain cycle: self → handler (замыкание) → self // handler = { self.count += 1 } // ❌ утечка! // Решение — capture list с [weak self] handler = { [weak self] in guard let self = self else { return } self.count += 1 } } } ``` --- ## Обработка ошибок ### throws / try / catch ```swift // 1. Определяем тип ошибки enum ValidationError: Error { case tooShort(minimumLength: Int) case invalidCharacters([Character]) case alreadyTaken } // 2. Функция, которая может бросить ошибку func validateUsername(_ username: String) throws -> String { guard username.count >= 3 else { throw ValidationError.tooShort(minimumLength: 3) } let invalid = username.filter { !$0.isLetter && !$0.isNumber } guard invalid.isEmpty else { throw ValidationError.invalidCharacters(Array(invalid)) } return username.lowercased() } // 3. Обработка ошибок do { let name = try validateUsername("ab") print("Успех: \(name)") } catch ValidationError.tooShort(let min) { print("Слишком короткий: минимум \(min) символов") } catch ValidationError.invalidCharacters(let chars) { print("Недопустимые символы: \(chars)") } catch { print("Неизвестная ошибка: \(error)") } // try? — преобразует в Optional (нет ошибки → nil) let name = try? validateUsername("alice") // Optional // try! — crash при ошибке (использовать только при полной уверенности) let forced = try! validateUsername("validname") ``` ### Typed Throws (Swift 6) ```swift // До Swift 6: нельзя было указать конкретный тип ошибки // Swift 6: типизированные throws func parse(_ data: Data) throws(ValidationError) -> String { throw ValidationError.alreadyTaken // только ValidationError, не любой Error } // В catch теперь можно без as-cast do { let result = try parse(someData) } catch .alreadyTaken { print("Уже занято") } catch .tooShort(let min) { print("Минимум \(min)") } ``` ### Result Type ```swift // Result — подходит для async и callback API func fetchUser(id: Int) -> Result { guard id > 0 else { return .failure(.invalidID) } return .success(User(id: id, name: "Bob")) } switch fetchUser(id: 42) { case .success(let user): print("Пользователь: \(user.name)") case .failure(let error): print("Ошибка: \(error)") } // map / flatMap для трансформации Result let nameResult = fetchUser(id: 1).map { $0.name } ``` --- ## Конкурентность — async/await и Actors Swift 6 делает конкурентность безопасной на уровне компилятора — Data Race Safety стала compile-time ошибкой. ### async/await ```swift // async функция — может приостановиться func fetchWeather(city: String) async throws -> WeatherData { let url = URL(string: "https://api.weather.com/\(city)")! let (data, _) = try await URLSession.shared.data(from: url) return try JSONDecoder().decode(WeatherData.self, from: data) } // Вызов из async контекста func showWeather() async { do { let weather = try await fetchWeather(city: "Moscow") print("Температура: \(weather.temperature)°C") } catch { print("Ошибка: \(error)") } } // Точка входа для Task (аналог корутины в Kotlin) Task { await showWeather() } ``` ### Параллельное выполнение ```swift // async let — запускает задачи параллельно func loadDashboard() async throws -> Dashboard { async let user = fetchUser(id: 1) async let posts = fetchPosts(userId: 1) async let notifications = fetchNotifications() // Все три запроса выполняются одновременно return Dashboard( user: try await user, posts: try await posts, notifications: try await notifications ) } // TaskGroup — динамическое число параллельных задач func fetchAllUsers(ids: [Int]) async throws -> [User] { try await withThrowingTaskGroup(of: User.self) { group in for id in ids { group.addTask { try await fetchUser(id: id) } } var users: [User] = [] for try await user in group { users.append(user) } return users } } ``` ### Actors ```swift actor Counter { private var value = 0 func increment() { value += 1 } func decrement() { value -= 1 } func get() -> Int { value } // nonisolated — метод не требует изоляции (read-only, Sendable data) nonisolated func description() -> String { "Counter actor" } } let counter = Counter() await counter.increment() // await обязателен извне let v = await counter.get() ``` ### @MainActor ```swift // Гарантирует выполнение на главном потоке @MainActor class ViewModel: ObservableObject { @Published var title = "" func loadData() async { let data = await fetchFromServer() // Здесь мы уже на MainActor — безопасно обновляем UI title = data.title } } // MainActor.run — переключение на главный поток вручную Task { let result = await backgroundOperation() await MainActor.run { updateUI(with: result) } } ``` ### Sendable ```swift // Sendable — протокол безопасности для передачи между акторами // struct автоматически Sendable (если все поля Sendable) struct UserData: Sendable { let id: Int let name: String } // class требует явного @unchecked Sendable или thread-safe реализации final class Cache: @unchecked Sendable { private let lock = NSLock() private var storage: [String: Any] = [:] func set(_ value: Any, for key: String) { lock.lock() storage[key] = value lock.unlock() } } ``` --- ## Свойства: computed, observers, wrappers ### Computed Properties ```swift struct Rectangle { var width: Double var height: Double // Computed property — только get var area: Double { width * height } // get + set var perimeter: Double { get { 2 * (width + height) } set { width = newValue / 2 - height } // newValue — специальное имя } // Lazy property — вычисляется при первом обращении lazy var expensiveCalc: String = { // Долгие вычисления return "result" }() } ``` ### Property Observers ```swift class StepCounter { var steps: Int = 0 { willSet(newSteps) { // вызывается ДО изменения print("Будет \(newSteps) шагов") } didSet { // вызывается ПОСЛЕ изменения if steps > oldValue { print("Добавлено \(steps - oldValue) шагов") } } } } ``` ### Property Wrappers ```swift // Определение обёртки @propertyWrapper struct Clamped { private var value: Value let range: ClosedRange init(wrappedValue: Value, _ range: ClosedRange) { self.range = range self.value = min(max(wrappedValue, range.lowerBound), range.upperBound) } var wrappedValue: Value { get { value } set { value = min(max(newValue, range.lowerBound), range.upperBound) } } // projectedValue ($) — дополнительное свойство var projectedValue: ClosedRange { range } } struct Player { @Clamped(0...100) var health: Int = 100 @Clamped(0...10) var level: Int = 1 } var player = Player() player.health = 150 // автоматически зажимается до 100 player.level = -1 // автоматически зажимается до 0 print(player.$health) // доступ к projectedValue: 0...100 ``` > **SwiftUI Property Wrappers:** `@State`, `@Binding`, `@StateObject`, `@ObservedObject`, `@EnvironmentObject`, `@Published`, `@AppStorage`, `@FetchRequest` — это всё property wrappers, встроенные во фреймворк. --- ## Паттерн-матчинг Swift обладает исключительно выразительным паттерн-матчингом. ```swift // switch с where let point = CGPoint(x: 3, y: -3) switch (point.x, point.y) { case (0, 0): print("Начало координат") case (let x, 0) where x > 0: print("Положительная ось X") case (0, let y) where y > 0: print("Положительная ось Y") case (let x, let y) where x == -y: print("Антидиагональ") default: print("Произвольная точка") } // ~= оператор — кастомное сравнение struct Range2D { let xRange: ClosedRange let yRange: ClosedRange static func ~= (pattern: Range2D, value: CGPoint) -> Bool { pattern.xRange.contains(value.x) && pattern.yRange.contains(value.y) } } // for case — фильтрация при итерации enum NetworkResult { case success(Data) case failure(Error) } let results: [NetworkResult] = [.success(data1), .failure(err), .success(data2)] for case .success(let data) in results { process(data) // только успешные результаты } // Вложенные паттерны enum Shape { case circle(radius: Double) case rectangle(width: Double, height: Double) } let shapes: [Shape] = [.circle(radius: 5), .rectangle(width: 3, height: 4)] for shape in shapes { switch shape { case .circle(let r) where r > 3: print("Большой круг с радиусом \(r)") case .rectangle(let w, let h) where w == h: print("Квадрат со стороной \(w)") default: break } } ``` --- ## Макросы (Swift 6) Макросы — метапрограммирование на уровне компилятора. Генерируют код на этапе компиляции. ```swift // Встроенные макросы Swift 5.9+ // @Observable — заменяет ObservableObject + @Published + objectWillChange @Observable class UserProfile { var name = "" var email = "" // Автоматически генерируется код для наблюдения } // #Preview — макрос для SwiftUI превью #Preview { ContentView() } // #expect / #require — для Swift Testing import Testing @Test func validateInput() { #expect(validateUsername("alice") == "alice") } // Кастомные макросы — определяются как отдельные пакеты @attached(member, names: named(init)) macro AutoInit() = #externalMacro(module: "MyMacros", type: "AutoInitMacro") ``` --- ## SwiftUI, UIKit, AppKit, WatchKit ### Выбор фреймворка | Фреймворк | Платформы | Подход | Рекомендация | |--------------|------------------------|-----------------|-----------------------| | **SwiftUI** | iOS, macOS, watchOS, tvOS, visionOS | Декларативный | Новые проекты 2023+ | | **UIKit** | iOS, iPadOS, tvOS | Императивный | Legacy, сложные кастомные компоненты | | **AppKit** | macOS | Императивный | Legacy macOS, сложный UI | | **WatchKit** | watchOS (до 7) | Императивный | Устарел, заменён SwiftUI | ### SwiftUI — основной выбор для новых приложений ```swift import SwiftUI // View — протокол, тело описывает структуру UI struct ContentView: View { @State private var count = 0 // локальное состояние @State private var name = "" var body: some View { // some View — opaque return type VStack(spacing: 20) { Text("Счётчик: \(count)") .font(.largeTitle) .foregroundStyle(.blue) Button("Нажми меня") { count += 1 } .buttonStyle(.borderedProminent) TextField("Имя", text: $name) // $name — Binding .textFieldStyle(.roundedBorder) if !name.isEmpty { Text("Привет, \(name)!") } } .padding() } } // @Observable (Swift 5.9+ / iOS 17+) — современная замена ObservableObject @Observable class AppState { var isLoggedIn = false var currentUser: User? } // Навигация (iOS 16+) struct NavigationExample: View { @State private var path = NavigationPath() var body: some View { NavigationStack(path: $path) { List(items) { item in NavigationLink(value: item) { Text(item.title) } } .navigationDestination(for: Item.self) { item in DetailView(item: item) } } } } // Cross-platform — один код для iOS и macOS struct SharedView: View { var body: some View { List { } #if os(macOS) .listStyle(.sidebar) #else .listStyle(.insetGrouped) #endif } } ``` ### Архитектурные паттерны для SwiftUI - **MVVM** — `View` + `@Observable ViewModel` — самый распространённый - **TCA (The Composable Architecture)** — популярна в крупных проектах, строгая однонаправленность - **VIPER** — переносится из UIKit, но громоздко в SwiftUI ### UIKit (для iOS когда нужны legacy или fine-grained компоненты) ```swift class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let label = UILabel() label.text = "Hello" label.translatesAutoresizingMaskIntoConstraints = false view.addSubview(label) NSLayoutConstraint.activate([ label.centerXAnchor.constraint(equalTo: view.centerXAnchor), label.centerYAnchor.constraint(equalTo: view.centerYAnchor) ]) } } // UIHostingController — вставка SwiftUI View в UIKit let swiftUIView = ContentView() let hostingController = UIHostingController(rootView: swiftUIView) present(hostingController, animated: true) ``` ### AppKit (macOS) ```swift class AppDelegate: NSObject, NSApplicationDelegate { var window: NSWindow! func applicationDidFinishLaunching(_ notification: Notification) { window = NSWindow( contentRect: NSRect(x: 100, y: 100, width: 480, height: 300), styleMask: [.titled, .closable, .miniaturizable, .resizable], backing: .buffered, defer: false ) window.contentView = NSHostingView(rootView: ContentView()) window.makeKeyAndOrderFront(nil) } } ``` ### SwiftData (замена CoreData, iOS 17+ / macOS 14+) ```swift import SwiftData @Model class Task { var title: String var isCompleted: Bool = false var createdAt: Date = Date.now init(title: String) { self.title = title } } // Использование в SwiftUI struct TaskListView: View { @Environment(\.modelContext) private var context @Query(sort: \Task.createdAt) private var tasks: [Task] var body: some View { List(tasks) { task in Text(task.title) } .toolbar { Button("Добавить") { context.insert(Task(title: "Новая задача")) } } } } ``` --- ## Экосистема и инструменты ### Xcode Единственная официальная IDE для разработки под Apple платформы. Включает: - **Interface Builder** — визуальный редактор UI (менее актуален с SwiftUI) - **SwiftUI Previews** — live preview в реальном времени - **Instruments** — профилировщик (Memory, CPU, Leaks, Time Profiler) - **Simulator** — симуляторы iOS/macOS/watchOS - **Swift Package Manager (SPM)** — встроенный менеджер зависимостей ### Swift Package Manager (SPM) ```swift // Package.swift import PackageDescription let package = Package( name: "MyApp", platforms: [.iOS(.v17), .macOS(.v14)], dependencies: [ .package(url: "https://github.com/Alamofire/Alamofire", from: "5.8.0"), .package(url: "https://github.com/nicklockwood/SwiftFormat", from: "0.53.0") ], targets: [ .target( name: "MyApp", dependencies: ["Alamofire"] ) ] ) ``` ### Популярные библиотеки | Категория | Библиотека | Описание | |-------------------|-------------------------------|--------------------------------| | Networking | Alamofire / URLSession | HTTP клиент | | JSON | Codable (встроен) | Сериализация/десериализация | | Async | Combine / AsyncAlgorithms | Реактивное программирование | | DI | Swinject / Factory | Dependency Injection | | Testing | XCTest / Swift Testing | Юнит и UI тесты | | Linting | SwiftLint / SwiftFormat | Стиль кода | | Database | SwiftData / GRDB / Realm | Локальное хранилище | | Архитектура | TCA (The Composable Arch.) | Строгая архитектура | ### Codable — встроенная сериализация ```swift struct User: Codable { let id: Int let name: String let email: String enum CodingKeys: String, CodingKey { case id case name = "full_name" // переименование ключа case email } } // Декодирование из JSON let json = """ {"id": 1, "full_name": "Alice", "email": "alice@example.com"} """.data(using: .utf8)! let user = try JSONDecoder().decode(User.self, from: json) print(user.name) // Alice // Кодирование в JSON let encoded = try JSONEncoder().encode(user) ``` --- ## Таблица сравнений Swift vs Kotlin | Концепция | Kotlin | Swift | |------------------------------|-------------------------------------|-------------------------------------| | Переменная | `var x = 5` | `var x = 5` | | Константа | `val x = 5` | `let x = 5` | | Null/nil | `null` | `nil` | | Nullable тип | `String?` | `String?` | | Safe call | `str?.length` | `str?.count` | | Elvis / Nil-coalescing | `str ?: "default"` | `str ?? "default"` | | Функция | `fun greet(): String` | `func greet() -> String` | | Лямбда | `{ x -> x * 2 }` | `{ x in x * 2 }` / `{ $0 * 2 }` | | Интерфейс/протокол | `interface` | `protocol` | | Data class | `data class Point(val x: Int, ...)` | `struct Point { var x: Int; ... }` | | Extension | `fun String.trim()` | `extension String { func trim() }` | | Sealed class | `sealed class Result` | `enum Result` с associated values | | Корутина / Task | `suspend fun`, `coroutineScope` | `async func`, `Task` | | Shared mutable state | `@Volatile`, `Mutex`, `StateFlow` | `actor`, `@MainActor` | | Наследование | `: ParentClass()` | `: ParentClass` + `super.init()` | | Generics | `fun foo(x: T)` | `func foo(_ x: T)` | | when / switch | `when(x) { 1 -> ... }` | `switch x { case 1: ... }` | | String interpolation | `"Hello $name"` | `"Hello \(name)"` | | this / self | `this` | `self` | | constructor / init | `constructor(x: Int)` | `init(x: Int)` | | companion object / static | `companion object { ... }` | `static` / `class func` | | try? | нет прямого аналога | `try?` | | Tuples | `Pair`, `Triple` | `(Int, String)` — полноценный тип | | Аннотации | `@Annotation` | нет (только атрибуты `@`) | | guard | `require(...)`, `checkNotNull(...)` | `guard let ... else { return }` | | typealias | `typealias` | `typealias` | --- ## Быстрый старт 1. Установи Xcode из Mac App Store 2. Создай новый проект: **File → New → Project → App** 3. Выбери **SwiftUI** как Interface и **Swift** как язык 4. Открой `ContentView.swift` и начни разработку 5. Для watchOS: добавь **WatchKit App** target к существующему проекту ### Ресурсы для изучения - [Swift.org — официальная документация](https://swift.org/documentation) - [The Swift Programming Language (бесплатная книга от Apple)](https://docs.swift.org/swift-book/) - [Apple Developer Documentation](https://developer.apple.com/documentation) - [Hacking with Swift (hackingwithswift.com)](https://www.hackingwithswift.com) - [Stanford CS193p — iOS Development with SwiftUI](https://cs193p.stanford.edu) — бесплатный курс - [Swift Evolution](https://github.com/swiftlang/swift-evolution) — актуальные предложения по развитию языка --- *Последнее обновление: июнь 2026. Актуально для Swift 6.x, Xcode 16+, iOS 18+, macOS 15+.*