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 — اصلاحکننده برای توابعی که closure throwing را به عنوان پارامتر میپذیرند. اگر closure ارسالی خطا پرتاب نکند، میتوان تابع rethrows را بدون try فراخوانی کرد. این امکان نوشتن توابع مرتبه بالای عمومی سازگار با closureهای 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 مدیریت خطا در کد ناهمگام نیاز به closureهای تودرتو با بررسی 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 امکان انتشار خطا از closure را فراهم میکند. do-catch روش اصلی مدیریت صریح باقی میماند.
Swift خطاهای پرتاب شده را تایپ نمیکند — هر تابع throwing میتواند هر نوع مطابق با Error را پرتاب کند. مدیریت را از طریق تطبیق در catch با انواع خاص خطا جدا کنید.
بله، این بهترین روش است. AppError را به عنوان enum با تأیید Error تعریف کرده و خطاهای سیستمی را به خطاهای دامنه تبدیل کنید. این کار مدیریت را یکپارچه میکند و لایه برنامه را از جزئیات سیستمی جدا میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید