Files

1726 lines
54 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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>` (аналог `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<Int>
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<Int> — ключ может отсутствовать
let safe = dict["z"] ?? 0 // значение по умолчанию через nil-coalescing
// Итерация
for (key, value) in dict {
print("\(key) → \(value)")
}
```
### Set
```swift
var fruits: Set<String> = ["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<Wrapped> { case some(Wrapped); case none }`. Принципиальное отличие от Kotlin: Swift Optional — не просто аннотация типа, а реальный тип-обёртка.
### Объявление
```swift
var name: String? = "Alice" // Optional<String>
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<Int>
let upper = name?.uppercased() // Optional<String>
// Цепочка: если любое звено 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<String>
// 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<Planet>
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<T> {
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<T>(_ 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<Element> {
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<Int>()
stack.push(1)
stack.push(2)
print(stack.pop()) // 2
```
### Type Constraints
```swift
// T должен соответствовать протоколу Comparable
func findMax<T: Comparable>(_ array: [T]) -> T? {
array.max()
}
// where clause — сложные ограничения
func merge<T, U>(_ 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<Element> {
associatedtype Element
}
// Теперь можно использовать как параметризованный existential
var coll: any Collection<Int> // вместо 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<String>
// 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<Success, Failure: Error> — подходит для async и callback API
func fetchUser(id: Int) -> Result<User, NetworkError> {
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<Value: Comparable> {
private var value: Value
let range: ClosedRange<Value>
init(wrappedValue: Value, _ range: ClosedRange<Value>) {
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<Value> { 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<Double>
let yRange: ClosedRange<Double>
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 <T> foo(x: T)` | `func foo<T>(_ 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+.*