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! вызывает crash при ошибке, а rethrows позволяет пробрасывать ошибку из замыкания. Do-catch остаётся основным способом явной обработки.
Swift не типизирует выбрасываемые ошибки — любая throwing-функция может выбросить любой тип, соответствующий Error. Разделяйте обработку через сопоставление в catch по конкретным типам ошибок.
Да, это лучшая практика. Определите AppError как enum с подтверждением Error и конвертируйте системные ошибки в доменные. Это унифицирует обработку и изолирует слой приложения от системных деталей.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также