معالجة الأخطاء في Swift: try/catch و throws وكتل do-catch

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

معالجة الأخطاء في Swift: try/catch وما بعدها



يتطلب التطوير لنظامي iOS وmacOS إدارة موثوقة للأخطاء. توفر Swift آلية قوية وأنيقة لمعالجة الأعطال، تعتمد على الكلمات المفتاحية throws وtry وcatch وdo. في هذا المقال، سنشرح كيفية معالجة الأخطاء بشكل صحيح، وكتابة كود قابل للقراءة، وتجنب المزالق الشائعة.



1. الأساسيات: بروتوكول Error والكلمة المفتاحية throws



يجب أن يتوافق أي خطأ في Swift مع بروتوكول Error. عادةً ما يتم استخدام التعدادات (enum) لهذا الغرض:



enum NetworkError: Error {    case badURL    case noData    case decodingFailed(String)}


الدالة التي يمكنها إلقاء خطأ تُعلَم بالكلمة المفتاحية throws:



func fetchUser(from urlString: String) throws -> User {    guard let url = URL(string: urlString) else {        throw NetworkError.badURL    }    // ... منطق الطلب}


الآن، عند استدعاء هذه الدالة، سيلزمك المترجم بمعالجة الخطأ المحتمل.



2. بناء do-catch: اعتراض الأخطاء



الطريقة الأكثر شيوعاً للمعالجة هي كتلة do-catch. تضع الكود الذي قد يكون خطيراً داخل do، وفي catch تعالج العطل:



do {    let user = try fetchUser(from: "https://api.example.com/user")    print("User loaded: \\(user.name)")} catch NetworkError.badURL {    print("رابط URL غير صحيح")} catch NetworkError.noData {    print("الخادم لم يُرجع بيانات")} catch NetworkError.decodingFailed(let message) {    print("خطأ في فك الترميز: \\(message)")} catch {    print("خطأ غير معروف: \\(error.localizedDescription)")}


هام: كتلة catch الأخيرة بدون نوع محدد تلتقط جميع الأخطاء المتبقية — يجب إضافتها دائماً.



2.1. مطابقة الأنماط في catch



يمكنك استخدام شروط أكثر تعقيداً:



do {    let result = try riskyOperation()} catch let error as NetworkError where error == .badURL {    print("مشكلة في الرابط URL")} catch {    print(error)}


3. البدائل: try? و try!



ليست هناك حاجة دائماً للمعالجة الكاملة. تقدم Swift طريقتين مبسطتين:



3.1. try? — قيمة اختيارية



إذا لم يكن الخطأ حرجاً، يمكن تحويل النتيجة إلى قيمة اختيارية (optional). عند العطل، ستعيد الدالة nil:



let user = try? fetchUser(from: "https://example.com/user")// user من النوع User? — يكون nil عند الخطأ


مثالي للحالات التي يمكن فيها تجاهل الخطأ ببساطة (مثل تحميل الصورة الرمزية).



3.2. try! — التنفيذ الإجباري



استخدم try! فقط عندما تكون متأكداً تماماً من عدم وجود خطأ. وإلا — سيحدث تعطل التطبيق:



let user = try! fetchUser(from: "https://valid-url.com/user")// إذا كان الرابط غير صالح — سيتعطل التطبيق


استخدم try! في الاختبارات أو في الحالات التي يكون فيها العطل مستحيلاً حسب منطق البرنامج.



4. تقنيات متقدمة: rethrows و deferred



4.1. rethrows للدوال عالية المستوى



إذا كانت الدالة الخاصة بك تستقبل إغلاقاً (closure) يمكنه إلقاء خطأ، استخدم rethrows. هذا يسمح بعدم تعليم الدالة نفسها كـ throws إذا لم يلقِ الإغلاق خطأ:



func process(_ closure: () throws -> Void) rethrows {    try closure()}

// استدعاء بدون إغلاق throws — لا يتطلب do-catchprocess { print("بدون أخطاء") }

// استدعاء مع إغلاق throws — يتطلب معالجةtry process { throw NetworkError.noData }


4.2. deferred: التنظيف المضمون



كتلة defer تُنفذ عند أي إنهاء للدالة (نجاح أو خطأ). مناسبة تماماً لإغلاق الملفات أو تحرير الموارد.

المدونات

توصيات الكتب