LEARN X · ЗА 16 МИН

Swift

Swift за 16 минут: переменные, опционалы, switch, коллекции, функции, структуры и классы, enum, протоколы, ошибки и дженерики на примерах.

Swift — современный, безопасный и быстрый язык от Apple для iOS, macOS и не только. Главная фишка — опционалы: компилятор не даёт случайно обратиться к nil. Ниже весь язык на одной странице: почти без прозы, всё в комментариях кода.

Вывод и комментарии

// Однострочный комментарий
/* Многострочный
   комментарий */
/* Вложенные /* тоже работают */ в Swift */

print("Привет, мир!")        // Привет, мир!
print("a", "b", "c")          // a b c (разделитель — пробел)
print("без переноса", terminator: "")  // без \n в конце
print("1", "2", separator: "-")        // 1-2

Переменные и типы

Тип чаще выводится автоматически, но его можно указать явно.

let constant = 42        // константа (let) — менять нельзя
var variable = 10        // переменная (var) — можно менять
variable = 20            // ок

// Явное указание типа
let age: Int = 30
let pi: Double = 3.14
let isReady: Bool = true
let name: String = "Анна"

// Вывод типа компилятором
let n = 7          // Int
let d = 2.5        // Double
let flag = false   // Bool
let text = "hi"    // String

// Числовые литералы
let hex = 0xFF         // 255
let binary = 0b1010    // 10
let million = 1_000_000  // подчёркивания для читаемости

// Преобразование типов — только явное
let i = 3
let x = 2.0
let sum = Double(i) + x   // 5.0

Строки

let first = "Иван"
let last = "Петров"

// Интерполяция через \(...)
let full = "\(first) \(last)"     // Иван Петров
let info = "Сумма: \(2 + 3)"      // Сумма: 5

// Конкатенация
let greeting = "Привет, " + first  // Привет, Иван

// Многострочная строка
let poem = """
Строка один
Строка два
"""

// Методы
let s = "Swift"
print(s.count)              // 5 (длина)
print(s.uppercased())       // SWIFT
print(s.lowercased())       // swift
print(s.hasPrefix("Sw"))    // true
print(s.hasSuffix("ft"))    // true
print(s.contains("wi"))     // true
print(s.isEmpty)            // false
print(s.replacingOccurrences(of: "i", with: "I"))  // SwIft

Опционалы

Ключевая фишка Swift. Опционал (Optional) — это значение, которое может быть nil. Тип с ? хранит либо значение, либо его отсутствие.

var maybeName: String? = "Олег"   // опционал: String или nil
maybeName = nil                    // допустимо

// Принудительное извлечение (!) — упадёт, если nil. Опасно!
var surname: String? = "Сидоров"
print(surname!)   // Сидоров (но если бы был nil — crash)

// Безопасно: if let (опциональная привязка)
if let name = maybeName {
    print("Имя: \(name)")
} else {
    print("Имени нет")   // если maybeName == nil
}

// guard let — ранний выход из функции
func greet(_ who: String?) {
    guard let who = who else {
        print("Никого")
        return
    }
    print("Привет, \(who)")  // who здесь уже не опционал
}
greet("Маша")   // Привет, Маша
greet(nil)      // Никого

// Оператор ?? — значение по умолчанию (nil-coalescing)
let display = maybeName ?? "Гость"
print(display)   // Гость (если maybeName == nil)

// Опциональная цепочка — ?. безопасно обращается к свойству
let len = maybeName?.count   // Int? : длина или nil
print(len ?? 0)              // 0, если nil

Операторы и условия

// Арифметика: + - * / % и сравнения == != < > <= >=
let a = 7, b = 3
print(a % b)   // 1 (остаток)

// Логические: && (и), || (или), ! (не)
if a > 5 && b < 5 {
    print("оба условия верны")
}

// Тернарный оператор
let max = a > b ? a : b   // 7

// switch с сопоставлением с образцом (pattern matching)
let score = 85
switch score {
case 90...100:
    print("Отлично")
case 70..<90:           // 70..<90 — без верхней границы
    print("Хорошо")       // сюда
case 0..<70:
    print("Подтянись")
default:
    print("Некорректно")
}
// switch должен покрывать все случаи (отсюда default)
// break по умолчанию — проваливания нет

// switch по кортежу с привязкой значений
let point = (2, 0)
switch point {
case (0, 0):
    print("начало координат")
case (let x, 0):
    print("на оси X в \(x)")   // на оси X в 2
case (_, let y) where y > 0:
    print("верхняя полуплоскость")
default:
    print("другое")
}

Циклы

// for-in по диапазону
for i in 1...3 {        // 1...3 — включая 3
    print(i)            // 1, 2, 3
}
for i in 0..<3 {         // 0..<3 — без 3
    print(i)            // 0, 1, 2
}

// Шаг через stride
for i in stride(from: 0, to: 10, by: 2) {
    print(i)            // 0 2 4 6 8
}

// for-in по массиву
for fruit in ["яблоко", "груша"] {
    print(fruit)
}

// Игнорирование значения через _
for _ in 1...3 {
    print("тук")        // три раза
}

// while — проверка до тела
var k = 0
while k < 3 {
    print(k)            // 0 1 2
    k += 1
}

// repeat-while — тело выполняется хотя бы раз
var m = 5
repeat {
    print(m)            // 5 (один раз)
    m += 1
} while m < 3

Коллекции

// Массив (Array) — упорядоченный
var nums: [Int] = [1, 2, 3]
nums.append(4)            // [1, 2, 3, 4]
nums.insert(0, at: 0)     // [0, 1, 2, 3, 4]
nums.remove(at: 0)        // [1, 2, 3, 4]
print(nums.count)         // 4
print(nums.first ?? -1)   // 1 (опционал!)
print(nums.contains(3))   // true
let doubled = nums.map { $0 * 2 }       // [2, 4, 6, 8]
let evens = nums.filter { $0 % 2 == 0 } // [2, 4]
let total = nums.reduce(0, +)           // 10

// Словарь (Dictionary) — пары ключ-значение
var ages: [String: Int] = ["Анна": 30, "Боб": 25]
ages["Вера"] = 40         // добавить
print(ages["Анна"] ?? 0)  // 30 (доступ — опционал)
ages["Боб"] = nil         // удалить
for (name, age) in ages {
    print("\(name): \(age)")
}

// Множество (Set) — уникальные, без порядка
var tags: Set<String> = ["swift", "ios", "swift"]
print(tags.count)         // 2 (дубль убран)
tags.insert("apple")
print(tags.contains("ios"))  // true

Функции

// Базовая функция с типами параметров и возврата
func add(_ a: Int, _ b: Int) -> Int {
    return a + b
}
print(add(2, 3))   // 5
// _ перед параметром убирает метку аргумента при вызове

// Метки аргументов (внешнее имя)
func greet(name: String, from city: String) -> String {
    return "\(name) из \(city)"   // city — внутреннее имя
}
print(greet(name: "Лена", from: "Казани"))  // Лена из Казани

// Значения по умолчанию
func power(_ base: Int, _ exp: Int = 2) -> Int {
    var r = 1
    for _ in 0..<exp { r *= base }
    return r
}
print(power(5))     // 25 (exp по умолчанию 2)
print(power(2, 3))  // 8

// Несколько возвращаемых значений через кортеж
func minMax(_ arr: [Int]) -> (min: Int, max: Int) {
    return (arr.min()!, arr.max()!)
}
let r = minMax([3, 1, 9])
print(r.min, r.max)   // 1 9

// Замыкания (closures) — функции-значения
let square = { (x: Int) -> Int in x * x }
print(square(4))   // 16

// Замыкание как аргумент; $0 — первый параметр
let sorted = [3, 1, 2].sorted { $0 < $1 }  // [1, 2, 3]
print(sorted)

Структуры и классы

struct — тип-значение (копируется при присваивании), class — ссылочный тип (передаётся по ссылке). Это ключевое различие.

// Структура — value type
struct Point {
    var x: Int
    var y: Int
    // Метод, меняющий свойства, помечается mutating
    mutating func moveRight() { x += 1 }
    func describe() -> String { "(\(x), \(y))" }
}
var p1 = Point(x: 0, y: 0)   // авто-инициализатор
var p2 = p1                   // КОПИЯ
p2.moveRight()
print(p1.describe())          // (0, 0) — оригинал не тронут
print(p2.describe())          // (1, 0)

// Класс — reference type
class Counter {
    var value = 0             // свойство со значением по умолчанию
    init(start: Int) {        // явный инициализатор
        value = start
    }
    func increment() { value += 1 }
}
let c1 = Counter(start: 10)
let c2 = c1                   // ССЫЛКА на тот же объект
c2.increment()
print(c1.value)               // 11 — изменился и c1!

// Наследование — только у классов
class Animal {
    func sound() -> String { "..." }
}
class Dog: Animal {
    override func sound() -> String { "Гав" }
}
print(Dog().sound())          // Гав

Перечисления

// Простой enum
enum Direction {
    case north, south, east, west
}
let dir = Direction.north
switch dir {
case .north: print("север")   // север
default: print("другое")
}

// Raw values — сырые значения
enum Planet: Int {
    case mercury = 1, venus, earth   // 1, 2, 3
}
print(Planet.earth.rawValue)         // 3
print(Planet(rawValue: 1)!)          // mercury (опционал!)

enum Status: String {
    case active = "активен"
    case banned = "заблокирован"
}
print(Status.active.rawValue)        // активен

// Associated values — связанные значения
enum Result {
    case success(code: Int)
    case failure(message: String)
}
let res = Result.failure(message: "нет сети")
switch res {
case .success(let code):
    print("ок \(code)")
case .failure(let message):
    print("ошибка: \(message)")      // ошибка: нет сети
}

Протоколы и расширения

protocol — набор требований (как интерфейс). extension добавляет функциональность существующему типу.

// Протокол — контракт
protocol Describable {
    var title: String { get }       // требуемое свойство
    func describe() -> String       // требуемый метод
}

// Тип соответствует протоколу
struct Book: Describable {
    var title: String
    func describe() -> String { "Книга: \(title)" }
}
print(Book(title: "Swift").describe())  // Книга: Swift

// Расширение добавляет методы существующему типу
extension Int {
    func squared() -> Int { self * self }
    var isEven: Bool { self % 2 == 0 }
}
print(5.squared())   // 25
print(4.isEven)      // true

// Расширение протокола с реализацией по умолчанию
extension Describable {
    func describe() -> String { "Объект: \(title)" }
}
struct Movie: Describable {
    var title: String   // describe() берётся из расширения
}
print(Movie(title: "Дюна").describe())  // Объект: Дюна

Обработка ошибок

// Свой тип ошибки — соответствует протоколу Error
enum DivError: Error {
    case divisionByZero
}

// Функция, которая может бросить ошибку — throws
func divide(_ a: Int, by b: Int) throws -> Int {
    if b == 0 {
        throw DivError.divisionByZero   // бросаем ошибку
    }
    return a / b
}

// do / try / catch — перехват ошибки
do {
    let r = try divide(10, by: 2)
    print(r)                       // 5
    let bad = try divide(1, by: 0)
    print(bad)                     // не выполнится
} catch DivError.divisionByZero {
    print("Деление на ноль!")      // сюда
} catch {
    print("Другая ошибка: \(error)")
}

// try? — превращает ошибку в nil (получаем опционал)
let safe = try? divide(8, by: 0)   // nil
print(safe ?? -1)                  // -1

// try! — уверены, что ошибки не будет (иначе crash)
let sure = try! divide(9, by: 3)   // 3
print(sure)

Дженерики

Дженерики (generics) позволяют писать код, работающий с любым типом, сохраняя типобезопасность.

// Обобщённая функция: T — параметр-тип
func swapTwo<T>(_ a: inout T, _ b: inout T) {
    let tmp = a
    a = b
    b = tmp
}
var x = 1, y = 2
swapTwo(&x, &y)        // & передаёт по ссылке (inout)
print(x, y)            // 2 1

// Обобщённый тип — стек для любого T
struct Stack<T> {
    private var items: [T] = []
    mutating func push(_ item: T) { items.append(item) }
    mutating func pop() -> T? { items.popLast() }
    var count: Int { items.count }
}
var s = Stack<Int>()
s.push(1)
s.push(2)
print(s.pop() ?? -1)   // 2
print(s.count)         // 1

// Ограничение типа: T должен соответствовать протоколу
func maxOf<T: Comparable>(_ a: T, _ b: T) -> T {
    return a > b ? a : b
}
print(maxOf(3, 9))         // 9
print(maxOf("a", "b"))     // b (строки тоже Comparable)
Поддержать проект