54 KiB
Swift: Полный справочник для разработчика с опытом Java/Kotlin
Целевая аудитория: разработчики, знакомые с Java/Kotlin/Python, планирующие разрабатывать приложения для macOS, iOS и watchOS.
Версия: Swift 6.x (актуально на 2026 год)
Содержание
- Общее введение
- Синтаксические основы
- Типы данных и коллекции
- Управляющие конструкции
- Функции и замыкания
- Optionals — нулевая безопасность
- Перечисления (Enum)
- Структуры и классы
- Протоколы и расширения
- Обобщённые типы (Generics)
- Управление памятью — ARC
- Обработка ошибок
- Конкурентность — async/await и Actors
- Свойства: computed, observers, wrappers
- Паттерн-матчинг
- Макросы (Swift 6)
- SwiftUI, UIKit, AppKit, WatchKit
- Экосистема и инструменты
- Таблица сравнений 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. Отличие: в Swiftletна struct запрещает изменение любых свойств, аletна class — только перепривязывание ссылки (как в Kotlinval).
Типы данных
| 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
- Каждое присваивание или передача в функцию → счётчик ++
- Выход из scope → счётчик --
- Счётчик = 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 |
Быстрый старт
- Установи Xcode из Mac App Store
- Создай новый проект: File → New → Project → App
- Выбери SwiftUI как Interface и Swift как язык
- Открой
ContentView.swiftи начни разработку - Для watchOS: добавь WatchKit App target к существующему проекту
Ресурсы для изучения
- Swift.org — официальная документация
- The Swift Programming Language (бесплатная книга от Apple)
- Apple Developer Documentation
- Hacking with Swift (hackingwithswift.com)
- Stanford CS193p — iOS Development with SwiftUI — бесплатный курс
- Swift Evolution — актуальные предложения по развитию языка
Последнее обновление: июнь 2026. Актуально для Swift 6.x, Xcode 16+, iOS 18+, macOS 15+.