Do-Catch — це конструкція мови Swift для обробки помилок, яка перехоплює винятки, викинуті через throw, та виконує відповідний блок коду. На відміну від традиційного try-catch в інших мовах, Swift вимагає явного позначення функцій, які можуть викинути помилку, модифікатором throws. Згідно з Swift.org, 2026, Do-Catch — основний механізм обробки помилок в Swift, що забезпечує безпеку типів.
Головне
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. Будь-який тип, що відповідає цьому протоколу, може бути викинутий через throw. Помилки зазвичай визначають як enum із підтвердженням Error.
Error — порожній (маркерний) протокол. Він не вимагає реалізації методів, лише підтвердження відповідності. Компілятор Swift використовує Error для статичної перевірки: функція з throws може викинути лише значення, що відповідає Error.
Функція, позначена throws, зобов’язана викликатися з try. Якщо всередині такої функції відбувається throw, керування передається в викликаючий do-catch. Якщо помилка не оброблена ніде в ланцюжку викликів, вона прокидається на рівень вище.
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 — модифікатор для функцій, які приймають throwing-замикання як параметр. Якщо передане замикання не викидає помилку, викликати rethrows-функцію можна без try. Це дозволяє писати узагальнені функції вищого порядку, сумісні з throwing та non-throwing замиканнями.
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-код виглядає як синхронний, що спрощує читання та підтримку складних ланцюжків запитів з обробкою помилок на кожному кроці.
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)")
}
Swift пропонує три варіанти виклику throwing-функцій: try, try? та try!. Кожен варіант вирішує конкретне завдання та має свої обмеження.
try використовується всередині do-блоку, і помилка обробляється в catch. Це основний спосіб виклику throwing-функцій. Компілятор вимагає, щоб try знаходився в контексті, де помилка може бути перехоплена.
try? перетворює результат throwing-функції в Optional. Якщо функція викидає помилку, try? повертає nil, інакше — Optional із успішним значенням. Зручно для викликів, де помилку можна ігнорувати, але потрібен признак успіху.
try! прибирає обробку помилок. Якщо throwing-функція викидає помилку, додаток завершується з runtime error. Використовуйте try! лише коли абсолютно впевнені, що помилка неможлива — наприклад, при завантаженні вбудованого ресурсу.
// 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-розробці для роботи з системними API. Розглянемо типові сценарії: робота з файловою системою, Core Data та мережеві запити.
Багато методів FileManager є throwing. Do-Catch дозволяє коректно обробити відсутність файлу, зайнятість ресурсу або недостатню кількість прав.
Парсинг JSON через JSONDecoder — throwing операція. Do-Catch перехоплює помилки декодування та мережеві збої окремо, надаючи користувачеві точний текст помилки.
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. Розглянемо найпоширеніші.
Часто задавані питання
В Swift помилка — це значення, що відповідає протоколу Error, а не exception з розкруткою стеку. Throwing-функції зобов’язані бути явно позначені throws, і компілятор вимагає try при виклику — це виключає необроблені помилки на етапі компіляції.
Так, async функції також можуть бути throws, і для них працює do-catch. Виклик async throwing функції потребує try await всередині do-блоку. Це стандартний спосіб обробки помилок в асинхронному коді Swift.
Є альтернативи: try? перетворює помилку в nil, try! викликає креш при помилці, а rethrows дозволяє прокидати помилку з замикання. Do-catch залишається основним способом явної обробки.
Swift не типізує помилки, що викидаються — будь-яка throwing-функція може викинути будь-який тип, що відповідає Error. Розділяйте обробку через зіставлення в catch за конкретними типами помилок.
Так, це найкраща практика. Визначте AppError як enum із підтвердженням Error та конвертуйте системні помилки в доменні. Це уніфікує обробку та ізолює шар додатку від системних деталей.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також