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. هنا الخطأ ليس استثناءً مع فك تكديس المكدس، بل قيمة تتوافق مع بروتوكول 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 وغير throwing.
يدعم Swift كتل catch متعددة مع مطابقة الأخطاء حسب النوع. هذا مشابه لـ when في Kotlin أو switch حسب النوع: أول catch يطابق يتم تنفيذه، ويتم تخطي الباقي.
يمكن أن تحتوي كل كتلة catch على نمط يطابق نوعًا محددًا من الخطأ. يتحقق Swift من الكتل بالترتيب، لذلك يجب أن تأتي الأنواع الأكثر تحديدًا قبل الأنواع العامة. إذا لم يطابق أي نمط، يتم تنفيذ كتلة catch بدون نمط.
يسمح أمان الأنواع لـ Do-Catch بمعالجة كل خطأ على حدة أو دمج أنواع متعددة في كتلة واحدة عبر مطابقة الأنماط المفصولة بفواصل. هذا يلغي سلاسل if-else المرهقة النموذجية في Objective-C، حيث كان تحليل الأخطاء يتم عبر رموز الإرجاع أو NSError مع التحقق من المجال والرمز.
يدعم Swift أيضًا catch where — المطابقة مع شرط إضافي. على سبيل المثال، يمكنك تصفية الأخطاء حسب رمز أو نص محدد: catch URLError where error.code == .notConnectedToInternet. هذا يقلل من عدد كتل catch مع الحفاظ على الدقة.
لتصحيح الدوال throwing، يوفر Swift تمييزًا بين الأخطاء والاستثناءات. خطأ Swift هو قيمة إرجاع لا تتطلب فك تكديس المكدس. تعمل استثناءات Objective-C (@try/@catch) على مستوى runtime وتستخدم فقط للأعطال الفادحة. يعالج Do-Catch الأخطاء، وليس الاستثناءات، مما يجعله متوقعًا وفعالًا.
عند تطوير المكتبات وSDKs، من المهم تذكر أن الدوال throwing هي جزء من العقد العام. لا يتم تحديد نوع الخطأ في التوقيع، لذلك وثق الأخطاء التي يمكن أن تلقيها الدالة في التعليقات أو من خلال enum شبيه بـ Result. هذا يساعد مستخدمي API على كتابة كتل catch صحيحة دون الحاجة إلى النظر في التنفيذ.
في سياق البرمجة غير المتزامنة، يحل Do-Catch مع async/await مشكلة callback hell. سابقًا في Swift، كانت معالجة الأخطاء في الكود غير المتزامن تتطلب إغلاقات متداخلة مع التحقق من Error? في completion handlers. مع 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 خطأ، ينتهي التطبيق بـ خطأ وقت التشغيل. استخدم 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 للعمل مع APIs النظام. دعنا ننظر في السيناريوهات النموذجية: العمل مع نظام الملفات و 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، وليس استثناءً مع فك تكديس المكدس. يجب وضع علامة صريحة على الدوال throwing بـ throws، ويتطلب المترجم try عند الاستدعاء — هذا يلغي الأخطاء غير المعالجة في وقت الترجمة.
نعم، يمكن أن تكون الدوال async أيضًا throws، ويعمل do-catch معها. يتطلب استدعاء دالة async throwing try await داخل كتلة do. هذه هي الطريقة القياسية لمعالجة الأخطاء في كود Swift غير المتزامن.
هناك بدائل: try? يحول الخطأ إلى nil، try! يسبب تعطلًا عند الخطأ، و rethrows يسمح بنشر الخطأ من إغلاق. يبقى do-catch الطريقة الرئيسية للمعالجة الصريحة.
لا يحدد Swift أنواع الأخطاء الملقاة — أي دالة throwing يمكنها إلقاء أي نوع يتوافق مع Error. افصل المعالجة عبر المطابقة في catch حسب أنواع الأخطاء المحددة.
نعم، هذه ممارسة جيدة. عرف AppError كـ enum يتوافق مع Error وحول أخطاء النظام إلى أخطاء مجال. هذا يوحد المعالجة ويعزل طبقة التطبيق عن تفاصيل النظام.
الخلاصة
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا