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 функциите са част от публичния договор. Типът на грешката не е специфициран в сигнатурата, затова документирайте какви грешки може да хвърли функцията в коментар или чрез enum, подобен на Result. Това помага на потребителите на 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 грешка. Използвайте 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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също