Files

54 KiB
Raw Permalink Blame History

Swift: Полный справочник для разработчика с опытом Java/Kotlin

Целевая аудитория: разработчики, знакомые с Java/Kotlin/Python, планирующие разрабатывать приложения для macOS, iOS и watchOS.
Версия: Swift 6.x (актуально на 2026 год)


Содержание

  1. Общее введение
  2. Синтаксические основы
  3. Типы данных и коллекции
  4. Управляющие конструкции
  5. Функции и замыкания
  6. Optionals — нулевая безопасность
  7. Перечисления (Enum)
  8. Структуры и классы
  9. Протоколы и расширения
  10. Обобщённые типы (Generics)
  11. Управление памятью — ARC
  12. Обработка ошибок
  13. Конкурентность — async/await и Actors
  14. Свойства: computed, observers, wrappers
  15. Паттерн-матчинг
  16. Макросы (Swift 6)
  17. SwiftUI, UIKit, AppKit, WatchKit
  18. Экосистема и инструменты
  19. Таблица сравнений Swift vs Kotlin

Общее введение

Swift — статически типизированный, компилируемый язык общего назначения от Apple (2014), разработанный как замена Objective-C. Он открыт с 2015 года и доступен на 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++.

Синтаксические основы

Переменные и константы

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 нет прямого аналога Любой ссылочный тип

Строки

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

Комментарии

// Однострочный комментарий

/* 
   Многострочный 
   комментарий 
*/

/// Документация (аналог /** */ в Kotlin/Java)
/// - Parameter x: описание параметра
/// - Returns: описание результата
func square(_ x: Int) -> Int { x * x }

Точка с запятой

Точка с запятой не нужна в конце строки (опциональна). Обязательна только для нескольких операторов на одной строке:

let a = 1; let b = 2  // допустимо, но не рекомендуется

Типы данных и коллекции

Array

// Создание
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

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

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

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

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

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 как выражение:

let label = if temp > 30 { "Жарко" } else { "Нормально" }

switch

В Swift switch гораздо мощнее Java/Kotlin — поддерживает паттерн-матчинг, ranges, tuples, where-условия. Не нужен break — нет fall-through по умолчанию.

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, но встроен в язык.

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

// 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

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

Объявление функций

// Базовый синтаксис: 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.

Функции как типы первого класса

// Функция как тип
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.

// Полный синтаксис
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)

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 — не просто аннотация типа, а реальный тип-обёртка.

Объявление

var name: String? = "Alice"   // Optional<String>
var age: Int? = nil           // nil === .none

// Non-optional — никогда не nil
var definite: String = "Always here"

Unwrapping

// 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

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

enum Direction {
    case north, south, east, west
}

let dir = Direction.north

Raw Values

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

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)

indirect enum BinaryTree<T> {
    case leaf
    case node(value: T, left: BinaryTree, right: BinaryTree)
}

Методы и computed properties в enum

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

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

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

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("Гав!") }
}

Инициализаторы в деталях

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

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

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

protocol Printable {
    func printDescription()
}

protocol Serializable {
    func serialize() -> Data
}

// Протокол наследует другие протоколы
protocol Document: Printable, Serializable {
    var title: String { get }
}

// Composition через & — без создания нового протокола
func process(_ item: Printable & Serializable) { }

Расширения (Extensions)

// Добавление методов к существующим типам (даже стандартной библиотеки!)
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 с дефолтными реализациями

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 ввёл важные ключевые слова:

// 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

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

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

// 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 в протоколах

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

// Указываем 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

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 — нет утечки
// 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 в замыканиях

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

// 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 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

// 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

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

Параллельное выполнение

// 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

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

// Гарантирует выполнение на главном потоке
@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

// 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

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

class StepCounter {
    var steps: Int = 0 {
        willSet(newSteps) {  // вызывается ДО изменения
            print("Будет \(newSteps) шагов")
        }
        didSet {             // вызывается ПОСЛЕ изменения
            if steps > oldValue {
                print("Добавлено \(steps - oldValue) шагов")
            }
        }
    }
}

Property Wrappers

// Определение обёртки
@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 обладает исключительно выразительным паттерн-матчингом.

// 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 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 — основной выбор для новых приложений

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 компоненты)

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)

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

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)

// 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 — встроенная сериализация

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 к существующему проекту

Ресурсы для изучения


Последнее обновление: июнь 2026. Актуально для Swift 6.x, Xcode 16+, iOS 18+, macOS 15+.