Do-Catch: що це, конструкція та обробка помилок в Swift

Автор: IT Sectr Опубліковано: 2026-05-26 Час читання: 8 хв

Do-Catch — це конструкція мови Swift для обробки помилок, яка перехоплює винятки, викинуті через throw, та виконує відповідний блок коду. На відміну від традиційного try-catch в інших мовах, Swift вимагає явного позначення функцій, які можуть викинути помилку, модифікатором throws. Згідно з Swift.org, 2026, Do-Catch — основний механізм обробки помилок в Swift, що забезпечує безпеку типів.

Головне

  • Do-Catch — конструкція Swift, яка перехоплює помилки з блоків з try та throw.
  • throws — маркер функції, що сигналізує, що вона може викинути помилку.
  • try — ключове слово для виклику throwing-функції всередині do-блоку.
  • catch — блок обробки помилки з можливістю зіставлення за типом.
  • try? та try! — альтернативні форми, що перетворюють помилку в nil або викликають креш.

Що таке Do-Catch?

Do-Catch — це конструкція в Swift, що складається з блоку do, всередині якого виконується код, здатний викинути помилку, та одного або кількох блоків catch, що обробляють цю помилку. Блок do містить виклики throwing-функцій, позначені try, а catch-блоки зіставляють помилку за типом.

Swift реалізує модель структурованої обробки помилок, відмінну від винятків в Objective-C. Тут помилка — не exception з розкруткою стеку, а значення, що відповідає протоколу Error. Викидання помилки через throw передає керування найближчому catch-блоку без накладних витрат на розкрутку.

Ключова особливість Swift — явність обробки. Компілятор не дозволяє викликати throwing-функцію без try, do-catch або альтернативного механізму (try?, try!). Це виключає ситуації, коли помилка лишається непоміченою.

Модель помилок в Swift: протокол Error та throwing функції

В основі обробки помилок в Swift лежить протокол Error. Будь-який тип, що відповідає цьому протоколу, може бути викинутий через throw. Помилки зазвичай визначають як enum із підтвердженням Error.

Протокол Error

Error — порожній (маркерний) протокол. Він не вимагає реалізації методів, лише підтвердження відповідності. Компілятор Swift використовує Error для статичної перевірки: функція з throws може викинути лише значення, що відповідає Error.

Throwing функції

Функція, позначена throws, зобов’язана викликатися з try. Якщо всередині такої функції відбувається throw, керування передається в викликаючий do-catch. Якщо помилка не оброблена ніде в ланцюжку викликів, вона прокидається на рівень вище.

swift
enum FileError: Error {
    case notFound
    case permissionDenied
    case corrupted(String)
}

func readFile(path: String) throws -> String {
    guard FileManager.default.fileExists(atPath: path) else {
        throw FileError.notFound
    }
    return try String(contentsOfFile: path)
}

Прокидання помилки rethrows

rethrows — модифікатор для функцій, які приймають throwing-замикання як параметр. Якщо передане замикання не викидає помилку, викликати rethrows-функцію можна без try. Це дозволяє писати узагальнені функції вищого порядку, сумісні з throwing та non-throwing замиканнями.

Синтаксис Do-Catch: кілька блоків catch

Swift підтримує кілька catch-блоків із зіставленням за типом помилки. Це схоже на when в Kotlin або switch за типом: перший підходячий catch виконується, інші пропускаються.

Зіставлення за типом

Кожен catch-блок може містити шаблон зіставлення з конкретним типом помилки. Swift перевіряє блоки по порядку, тому більш специфічні типи повинні розташовуватися раніше загальних. Якщо жоден шаблон не підійшов, виконується catch-блок без шаблону.

Обробка кількох типів

Типобезпека Do-Catch дозволяє обробляти кожну помилку окремо або об’єднувати кілька типів в одному блоці через кому в pattern-matching. Це усунає громіздкі if-else ланцюжки, характерні для Objective-C, де аналіз помилок виконувався через код повернення або NSError із перевіркою domain та code.

Swift також підтримує catch where — зіставлення з додатковою умовою. Наприклад, можна відфільтрувати помилки за конкретним кодом або текстом: catch URLError where error.code == .notConnectedToInternet. Це скорочує кількість catch-блоків при збереженні точності обробки.

Для налагодження throwing-функцій Swift надає різницю між помилками та винятками. Помилка Swift — це значення, що повертається, яке не потребує розкрутки стеку. Винятки Objective-C (@try/@catch) працюють на рівні runtime та використовуються лише для фатальних збоїв. Do-Catch обробляє саме помилки, а не винятки, що робить його предбачуваним та продуктивним.

При розробці бібліотек та SDK важливо пам’ятати, що throwing-функції — частина публічного контракту. Тип помилки не специфікований в сигнатурі, тому документуйте, які помилки може викинути функція, в коментарях або через Result-подібні enum. Це допомагає користувачам API писати коректні catch-блоки без необхідності заглядати в реалізацію.

В контексті асинхронного програмування Do-Catch з async/await вирішує проблему callback hell. Раніше в Swift обробка помилок в асинхронному коді вимагала вкладених замикань з перевіркою Error? в completion handler. З async/await та Do-Catch асинхронний throwing-код виглядає як синхронний, що спрощує читання та підтримку складних ланцюжків запитів з обробкою помилок на кожному кроці.

swift
do {
    let content = try readFile(path: "/data/config.json")
    process(content)
} catch FileError.notFound {
    createDefaultConfig()
} catch FileError.permissionDenied {
    requestAccess()
} catch FileError.corrupted(let detail) {
    logCorruption(detail)
} catch {
    print("Невідома помилка: \(error)")
}

Варіації try: try, try?, try! та коли їх використовувати

Swift пропонує три варіанти виклику throwing-функцій: try, try? та try!. Кожен варіант вирішує конкретне завдання та має свої обмеження.

try — стандартний виклик

try використовується всередині do-блоку, і помилка обробляється в catch. Це основний спосіб виклику throwing-функцій. Компілятор вимагає, щоб try знаходився в контексті, де помилка може бути перехоплена.

try? — перетворення в Optional

try? перетворює результат throwing-функції в Optional. Якщо функція викидає помилку, try? повертає nil, інакше — Optional із успішним значенням. Зручно для викликів, де помилку можна ігнорувати, але потрібен признак успіху.

try! — примусове виконання

try! прибирає обробку помилок. Якщо throwing-функція викидає помилку, додаток завершується з runtime error. Використовуйте try! лише коли абсолютно впевнені, що помилка неможлива — наприклад, при завантаженні вбудованого ресурсу.

swift
// try? — ігноруємо помилку, отримуємо nil при збої
if let data = try? Data(contentsOf: url) {
    processData(data)
}

// try! — лише якщо помилка гарантовано неможлива
let bundled = try! String(contentsOfFile: "Assets/default.txt")

// try — стандартний варіант в do-catch
do {
    let result = try performNetworkRequest()
    updateUI(result)
} catch {
    showError(error)
}

Do-Catch в iOS-розробці: практичні приклади

Do-Catch активно використовується в iOS-розробці для роботи з системними API. Розглянемо типові сценарії: робота з файловою системою, Core Data та мережеві запити.

Файлова система

Багато методів FileManager є throwing. Do-Catch дозволяє коректно обробити відсутність файлу, зайнятість ресурсу або недостатню кількість прав.

Мережеві запити з Codable

Парсинг JSON через JSONDecoder — throwing операція. Do-Catch перехоплює помилки декодування та мережеві збої окремо, надаючи користувачеві точний текст помилки.

swift
struct User: Codable {
    let id: Int
    let name: String
}

func fetchUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!
    let (data, _) = try await URLSession.shared.data(from: url)
    return try JSONDecoder().decode(User.self, from: data)
}

// Виклик з обробкою
do {
    let user = try await fetchUser(id: 42)
    showUser(user)
} catch let error as URLError {
    showNetworkAlert(error)
} catch let error as DecodingError {
    showParseError(error)
} catch {
    showGenericError(error)
}

Типові помилки при роботі з Do-Catch

Розробники, особливо ті, що переходять з інших мов, часто припускають характерні помилки при використанні Do-Catch. Розглянемо найпоширеніші.

  • Забутий try — виклик throwing-функції без try призводить до помилки компіляції. Swift не дозволяє неявно ігнорувати помилки.
  • Порожній catch — catch-блок без обробки приховує помилку. Завжди логуйте або обробляйте помилку, навіть якщо вона здається неможливою.
  • Catch без зіставлення — один загальний catch для всіх помилок позбавляє переваг типобезпеки. Розділяйте обробку за типами через кілька блоків.
  • Надмірне використання try! — try! в production-коді — джерело несподіваних крешів. Використовуйте лише для вбудованих ресурсів або тестів.

Часто задавані питання

Чим Do-Catch в Swift відрізняється від try-catch в інших мовах?

В Swift помилка — це значення, що відповідає протоколу Error, а не exception з розкруткою стеку. Throwing-функції зобов’язані бути явно позначені throws, і компілятор вимагає try при виклику — це виключає необроблені помилки на етапі компіляції.

Чи можна використовувати Do-Catch з async/await?

Так, async функції також можуть бути throws, і для них працює do-catch. Виклик async throwing функції потребує try await всередині do-блоку. Це стандартний спосіб обробки помилок в асинхронному коді Swift.

Як обробити помилку без do-catch?

Є альтернативи: try? перетворює помилку в nil, try! викликає креш при помилці, а rethrows дозволяє прокидати помилку з замикання. Do-catch залишається основним способом явної обробки.

Чи може одна функція викидати різні типи помилок?

Swift не типізує помилки, що викидаються — будь-яка throwing-функція може викинути будь-який тип, що відповідає Error. Розділяйте обробку через зіставлення в catch за конкретними типами помилок.

Чи варто обортати всі помилки в свій кастомний тип?

Так, це найкраща практика. Визначте AppError як enum із підтвердженням Error та конвертуйте системні помилки в доменні. Це уніфікує обробку та ізолює шар додатку від системних деталей.

Підсумки

  • Do-Catch — конструкція Swift для обробки помилок із обов’язковою позначкою throws на функціях та try при виклику.
  • Помилки Swift — значення, що відповідають протоколу Error, а не винятки з розкруткою стеку, що дає нульові накладні витрати при відсутності збою.
  • Кілька catch дозволяють обробляти різні типи помилок окремо з pattern-matching, аналогічним switch.
  • try? перетворює помилку в nil для простих сценаріїв, try! — примусовий виклик з ризиком крешу, що використовується лише для гарантовано успішних операцій.
  • Do-Catch з async/await — стандартний спосіб обробки помилок в асинхронному Swift коді з використанням try await.
  • Уникайте порожніх catch та надмірного використання try! — це приховує помилки та призводить до несподіваних збоїв в production.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також