Do-Catch: چیست، ساختار و مدیریت خطا در Swift

نویسنده: IT Sectr منتشر شده: 2026-05-26 زمان مطالعه: 8 دقیقه

Do-Catch — ساختاری در زبان Swift برای مدیریت خطا که استثناهای پرتاب شده از طریق throw را گرفته و بلوک کد مربوطه را اجرا می‌کند. برخلاف try-catch سنتی در زبان‌های دیگر، Swift نیاز به نشانه‌گذاری صریح توابعی که می‌توانند خطا پرتاب کنند با اصلاح‌کننده throws دارد. به گفته Swift.org، 2026، Do-Catch مکانیزم اصلی مدیریت خطا در Swift است که ایمنی در سطح نوع را تضمین می‌کند.

نکات اصلی

  • Do-Catch — ساختار Swift که خطاها را از بلوک‌های try و throw می‌گیرد.
  • throws — نشانگر تابع که نشان می‌دهد می‌تواند خطا پرتاب کند.
  • try — کلمه کلیدی برای فراخوانی تابع throwing درون بلوک do.
  • catch — بلوک مدیریت خطا با امکان تطبیق بر اساس نوع.
  • try? و try! — اشکال جایگزین که خطا را به nil تبدیل کرده یا باعث crash می‌شوند.

Do-Catch چیست؟

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 و توابع throwing

اساس مدیریت خطا در Swift پروتکل Error است. هر نوع مطابق با این پروتکل می‌تواند از طریق throw پرتاب شود. معمولاً خطاها به عنوان enum با تأیید Error تعریف می‌شوند.

پروتکل Error

Error — یک پروتکل خالی (نشانه‌گذار) است. نیاز به پیاده‌سازی متدها ندارد، فقط تأیید تطابق را می‌خواهد. کامپایلر Swift از Error برای بررسی ایستا استفاده می‌کند: تابع با throws فقط می‌تواند مقداری مطابق با Error پرتاب کند.

توابع throwing

تابع مشخص شده با throws باید با try فراخوانی شود. اگر درون چنین تابعی throw رخ دهد، کنترل به do-catch فراخوان منتقل می‌شود. اگر خطا در هیچ جای زنجیره فراخوانی مدیریت نشود، به سطح بالاتر منتقل می‌شود.

swift
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

rethrows — اصلاح‌کننده برای توابعی که closure throwing را به عنوان پارامتر می‌پذیرند. اگر closure ارسالی خطا پرتاب نکند، می‌توان تابع rethrows را بدون try فراخوانی کرد. این امکان نوشتن توابع مرتبه بالای عمومی سازگار با closureهای throwing و non-throwing را فراهم می‌کند.

نحو Do-Catch: چندین بلوک catch

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 مانند کد همگام به نظر می‌رسد، که خواندن و نگهداری زنجیره‌های پیچیده درخواست با مدیریت خطا در هر مرحله را ساده می‌کند.

swift
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)")
}

انواع try: try، try؟، try! و زمان استفاده از آنها

Swift سه نوع فراخوانی توابع throwing را ارائه می‌دهد: try، try؟ و try!. هر نوع یک کار خاص را حل می‌کند و محدودیت‌های خود را دارد.

try — فراخوانی استاندارد

try درون بلوک do استفاده می‌شود و خطا در catch مدیریت می‌شود. این روش اصلی فراخوانی توابع throwing است. کامپایلر نیاز دارد که try در زمینه‌ای باشد که خطا قابل گرفته شدن باشد.

try؟ — تبدیل به Optional

try؟ نتیجه تابع throwing را به Optional تبدیل می‌کند. اگر تابع خطا پرتاب کند، try؟ nil برمی‌گرداند، در غیر این صورت — Optional با مقدار موفق. برای فراخوانی‌هایی که خطا قابل نادیده گرفتن است اما نیاز به نشانگر موفقیت دارید مفید است.

try! — اجرای اجباری

try! مدیریت خطا را سرکوب می‌کند. اگر تابع throwing خطا پرتاب کند، برنامه با خطای runtime پایان می‌یابد. از try! فقط زمانی استفاده کنید که کاملاً مطمئن هستید خطا غیرممکن است — مثلاً هنگام بارگذاری منبع داخلی.

swift
// 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: مثال‌های عملی

Do-Catch به طور فعال در توسعه iOS برای کار با APIهای سیستمی استفاده می‌شود. بیایید سناریوهای معمولی را بررسی کنیم: کار با سیستم فایل، Core Data و درخواست‌های شبکه.

سیستم فایل

بسیاری از متدهای FileManager throwing هستند. Do-Catch امکان مدیریت صحیح عدم وجود فایل، اشغال بودن منبع یا کمبود مجوزها را فراهم می‌کند.

درخواست‌های شبکه با Codable

تجزیه JSON از طریق JSONDecoder — یک عملیات throwing است. Do-Catch خطاهای رمزگشایی و خرابی‌های شبکه را جداگانه گرفته و متن دقیق خطا را به کاربر ارائه می‌دهد.

swift
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

توسعه‌دهندگان، به ویژه آنهایی که از زبان‌های دیگر می‌آیند، اغلب هنگام استفاده از Do-Catch اشتباهات مشخصی مرتکب می‌شوند. بیایید رایج‌ترین آنها را بررسی کنیم.

  • try فراموش شده — فراخوانی تابع throwing بدون try منجر به خطای کامپایل می‌شود. Swift اجازه نادیده گرفتن ضمنی خطاها را نمی‌دهد.
  • catch خالی — بلوک catch بدون مدیریت خطا را پنهان می‌کند. همیشه خطا را ثبت یا مدیریت کنید، حتی اگر غیرممکن به نظر برسد.
  • catch بدون تطبیق — یک catch عمومی برای همه خطاها مزایای ایمنی نوع را از بین می‌برد. مدیریت را بر اساس نوع از طریق چندین بلوک جدا کنید.
  • استفاده بیش از حد از try! — try! در کد production منبع crashهای غیرمنتظره است. فقط برای منابع داخلی یا تست‌ها استفاده کنید.

سوالات متداول

Do-Catch در Swift چه تفاوتی با try-catch در زبان‌های دیگر دارد؟

در Swift خطا مقداری مطابق با پروتکل Error است، نه exception با باز کردن پشته. توابع throwing باید به صراحت با throws مشخص شوند و کامپایلر هنگام فراخوانی try را الزام می‌کند — این کار خطاهای مدیریت نشده را در مرحله کامپیل حذف می‌کند.

آیا می‌توان از Do-Catch با async/await استفاده کرد؟

بله، توابع async نیز می‌توانند throws باشند و do-catch برای آنها کار می‌کند. فراخوانی تابع async throwing نیاز به try await درون بلوک do دارد. این روش استاندارد مدیریت خطا در کد ناهمگام Swift است.

چگونه خطا را بدون do-catch مدیریت کنیم؟

جایگزین‌هایی وجود دارد: try؟ خطا را به nil تبدیل می‌کند، try! هنگام خطا crash ایجاد می‌کند، و rethrows امکان انتشار خطا از closure را فراهم می‌کند. do-catch روش اصلی مدیریت صریح باقی می‌ماند.

آیا یک تابع می‌تواند انواع مختلف خطا پرتاب کند؟

Swift خطاهای پرتاب شده را تایپ نمی‌کند — هر تابع throwing می‌تواند هر نوع مطابق با Error را پرتاب کند. مدیریت را از طریق تطبیق در catch با انواع خاص خطا جدا کنید.

آیا ارزش دارد همه خطاها را در نوع سفارشی خود بپیچیم؟

بله، این بهترین روش است. AppError را به عنوان enum با تأیید Error تعریف کرده و خطاهای سیستمی را به خطاهای دامنه تبدیل کنید. این کار مدیریت را یکپارچه می‌کند و لایه برنامه را از جزئیات سیستمی جدا می‌کند.

خلاصه

  • Do-Catch — ساختار Swift برای مدیریت خطا با نشانه‌گذاری اجباری throws روی توابع و try هنگام فراخوانی.
  • خطاهای Swift — مقادیر مطابق با پروتکل Error، نه استثنا با باز کردن پشته، که در صورت عدم خرابی هزینه صفر دارد.
  • چندین catch امکان مدیریت جداگانه انواع مختلف خطا با pattern-matching مشابه switch را فراهم می‌کنند.
  • try؟ خطا را به nil برای سناریوهای ساده تبدیل می‌کند، try! — فراخوانی اجباری با خطر crash که فقط برای عملیات قطعاً موفق استفاده می‌شود.
  • Do-Catch با async/await — روش استاندارد مدیریت خطا در کد ناهمگام Swift با استفاده از try await.
  • از catch خالی و استفاده بیش از حد از try! خودداری کنید — این کار خطاها را پنهان کرده و منجر به خرابی‌های غیرمنتظره در production می‌شود.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید