Swift поддерживает строгую систему управления ошибками через протокол Error и конструкции throw, try, catch. Вы можете определить собственные типы ошибок, наследуясь от Error:
enum NetworkError: Error { case noInternet case serverError(code: Int) case unknown }
Функции, которые могут выбросить ошибку, объявляются с throws:
func fetchData() throws -> Data { // ... throw NetworkError.noInternet }
Ошибки можно обрабатывать с помощью do-catch-блока:
do { let data = try fetchData() // Работа с данными } catch NetworkError.noInternet { print("Нет интернета") } catch { print("Другая ошибка: \(error)") }
Альтернативный подход — тип Result<T, Error>, позволяющий возвращать результат либо ошибку без need try-catch:
func fetchData() -> Result<Data, NetworkError> { // ... return .failure(.noInternet) } let result = fetchData() switch result { case .success(let data): // OK case .failure(let err): // Обработка ошибки }
Когда использовать:
try/catch, если ошибка критичная и её нельзя не обработать.Result, если функция асинхронная, или когда удобно передавать ошибку "наружу" без исключений.Вопрос: "Можно ли выбрасывать и ловить ошибки любого типа, например, строки или числа?"
Ответ: Нет, в Swift можно выбрасывать только типы, соответствующие протоколу Error.
// Неправильно: throw "StringError" // Компилятор не даст этого сделать // Правильно: struct MyError: Error {} throw MyError()
История
В проекте REST API клиент выбрасывал ошибку строкой (throw "No data"). Код компилировался на JavaScript, но после перевода в Swift возникла фатальная ошибка компиляции.
История
Разработчик возвращал ошибку через опциональные значения (return nil при ошибке), а не через throw/Result. В результате потерялись детали ошибок, было сложно их корректно обрабатывать — возникали Silent fails.
История
Анализ показал, что в нескольких местах приложения одинаковые ошибки не были сгруппированы в один тип Error. В итоге приложения по-разному обрабатывали однотипные сбои, UI показывал разные сообщения для одной ошибки — сложно поддерживать и тестировать.