{"id":"l-sw-2-4","courseId":"swift-essentials","lang":"ar","kind":"lesson","summary":"صِف حالات الفشل كـ enums للأخطاء، وارمِها ومرّرها باستخدام try، وعالجها بـ do-catch، وقرّر متى تستحق الـ typed throws في Swift 6 أن تستخدمها.","html":"<p>ماذا يجب أن يحدث حين يحاول المستخدم حفظ مصروف بلا عنوان، أو بمبلغ سالب، أو بمبلغ يتجاوز ميزانية الطعام لهذا الشهر دفعة واحدة؟ إعادة optional، كما فعلت في درس الـ optionals، تخبر المستدعي <em>أن</em> العملية فشلت لكنها لا تخبره <em>لماذا</em>، وPocket Budget يحتاج إلى السبب ليعرض الرسالة المناسبة. معالجة الأخطاء في Swift تجعل الفشل جزءًا من توقيع الدالة، مع أسباب مكتوبة كأنواع.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">الأخطاء enums</h2>\n<p>ابدأ بسرد الطرق التي قد يفشل بها تسجيل مصروف:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">BudgetError.swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Foundation</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">enum</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> BudgetError</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">Error</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">Equatable </span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    case</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> emptyTitle</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    case</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> nonPositiveAmount</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    case</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> overLimit</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(by: Decimal)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>أي نوع يتوافق مع <code dir=\"ltr\">Error</code> يمكن رميه، والـ enum ذو القيم المرتبطة هو الخيار الطبيعي: كل حالة فشلٌ واحد، و<code dir=\"ltr\">overLimit</code> تحمل مقدار التجاوز الذي سيقع فيه المستخدم. ليس لـ <code dir=\"ltr\">Error</code> أي متطلّبات؛ هو فقط يعلّم النوع على أنه قابل للرمي. وسيساعد <code dir=\"ltr\">Equatable</code> في الاختبارات في الدرس الأخير.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">throw وtry</h2>\n<p>الدالة التي قد تفشل تصرّح بذلك عبر <code dir=\"ltr\">throws</code> وتتوقّف عبر <code dir=\"ltr\">throw</code>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">CategoryBudget.swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">struct</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> CategoryBudget</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> limit: Decimal</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    private</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">set</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> spent: Decimal </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    mutating</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> func</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> record</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">title</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">amount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: Decimal) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">throws</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        guard</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> !</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">title.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">trimmingCharacters</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">in</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: .whitespaces).</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">isEmpty</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> else</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">            throw</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> BudgetError.emptyTitle</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        guard</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> amount </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">></span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> else</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">            throw</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> BudgetError.nonPositiveAmount</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> newTotal </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> spent </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> amount</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        guard</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> newTotal </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x3C;=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> limit </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">else</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">            throw</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> BudgetError.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">overLimit</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">by</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: newTotal </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> limit)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        spent </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> newTotal</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p><code dir=\"ltr\">guard</code> و<code dir=\"ltr\">throw</code> ثنائي مثالي: كل guard تنصّ على قاعدة، وفرع <code dir=\"ltr\">else</code> فيها يرمي الخطأ المناسب. و<code dir=\"ltr\">private(set)</code> تسمح لأي أحد بقراءة <code dir=\"ltr\">spent</code> لكنها لا تسمح بتغييرها إلا للميزانية نفسها، فتكون الطريقة الوحيدة لإنفاق المال عبر <code dir=\"ltr\">record</code> التي تفرض القواعد.</p>\n<p>كل استدعاء لدالة ترمي أخطاء يجب أن يُعلَّم بـ <code dir=\"ltr\">try</code>. هذه الكلمة موجّهة للبشر: امسح الدالة بعينيك بحثًا عن <code dir=\"ltr\">try</code> وسترى كل سطر قد يغادر فيه التنفيذ مبكرًا.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">معالجة الأخطاء بـ do-catch</h2>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> food </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> CategoryBudget</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">limit</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">50</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">do</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    try</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> food.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">record</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">title</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Dinner\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">amount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">62</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">    print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Saved\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">} </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">catch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> BudgetError.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">overLimit</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> by) {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">    print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"That's </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">by</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> over your food budget\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">} </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">catch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">    print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Couldn't save: </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">error</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>حين ترمي <code dir=\"ltr\">record</code> خطأً، يقفز التنفيذ مباشرة إلى أول <code dir=\"ltr\">catch</code> يطابق نمطها، متخطّيًا <code dir=\"ltr\">print(&quot;Saved&quot;)</code>. وتستخدم جمل catch مطابقة الأنماط نفسها التي في <code dir=\"ltr\">switch</code>، فيمكنك ربط القيم المرتبطة. أما <code dir=\"ltr\">catch</code> الأخيرة المجرّدة فتعالج كل ما تبقّى وتعطيك الخطأ كثابت اسمه <code dir=\"ltr\">error</code>.</p>\n<p>إن لم تكن الدالة قادرة على معالجة الخطأ بشكل معقول، فليس عليها ذلك. علّمها بـ <code dir=\"ltr\">throws</code> أيضًا، واستدعِ بـ <code dir=\"ltr\">try</code> دون <code dir=\"ltr\">do</code>، وسيصعد الخطأ إلى من استدعاها.</p>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">نصيحة</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>التقط الخطأ حيث تستطيع التصرّف</span></p>\n<p>عالج الخطأ في المستوى الذي يستطيع فعل شيء مفيد به. نموذج الميزانية لا يستطيع عرض تنبيه، والدالة المساعدة التي تحلّل CSV لا تستطيع أن تقرّر هل تعيد المحاولة. هذا يعني عادةً أن النماذج والدوال المساعدة ترمي الأخطاء، والشاشة التي بدأت العملية تلتقطها، وتعرض رسالة، وتسمح للمستخدم بالمحاولة مجدّدًا.</p>\n</aside>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 230\" role=\"img\" aria-labelledby=\"l-sw-2-4-t1\"><defs><marker id=\"arrow-l-sw-2-4-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-sw-2-4-t1\">ترمي record الخطأ overLimit. والدالة saveExpense معلّمة بـ throws فتمرّر الخطأ إلى الأعلى. ومعالج زر Save فيه do-catch يلتقطه ويعرض رسالة.</title>\n  <rect class=\"d-box-warn\" x=\"20\" y=\"150\" width=\"190\" height=\"60\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"115\" y=\"177\" text-anchor=\"middle\">record(...)</text>\n  <text class=\"d-label-muted\" x=\"115\" y=\"198\" text-anchor=\"middle\">throw .overLimit</text>\n  <rect class=\"d-box\" x=\"245\" y=\"85\" width=\"190\" height=\"60\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"340\" y=\"112\" text-anchor=\"middle\">saveExpense()</text>\n  <text class=\"d-label-muted\" x=\"340\" y=\"133\" text-anchor=\"middle\">throws: يمرّره للأعلى</text>\n  <rect class=\"d-box-success\" x=\"470\" y=\"20\" width=\"190\" height=\"60\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"565\" y=\"47\" text-anchor=\"middle\">Save button</text>\n  <text class=\"d-label-muted\" x=\"565\" y=\"68\" text-anchor=\"middle\">do-catch: يعرض تنبيهًا</text>\n  <path class=\"d-arrow\" d=\"M210 165 L245 135\" marker-end=\"url(#arrow-l-sw-2-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M435 100 L470 70\" marker-end=\"url(#arrow-l-sw-2-4-1)\"/>\n</svg><figcaption>يصعد الخطأ حتى يلتقطه شيء ما</figcaption></figure>\n<h2 id=\"sec-4\" tabindex=\"-1\">try? وtry!</h2>\n<p>هناك اختصاران. <code dir=\"ltr\">try?</code> تحوّل النتيجة إلى optional: النجاح يعطي قيمة، والفشل يعطي <code dir=\"ltr\">nil</code>. أما <code dir=\"ltr\">try!</code> فتؤكّد أن الاستدعاء لا يمكن أن يفشل، وتُسقط التطبيق إن فشل، بالمخاطرة نفسها التي في الفكّ القسري.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">خطأ شائع</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>استخدام try? لإسكات الـ compiler</span></p>\n<p><code dir=\"ltr\">try? food.record(title: title, amount: amount)</code> تُترجَم، فينقر المستخدم Save ولا يحدث شيء: لا مصروف ولا رسالة. لقد أهدرت المعلومة الوحيدة التي كانت الواجهة تحتاجها. استخدم <code dir=\"ltr\">try?</code> فقط حين كنت ستتجاهل السبب فعلًا، مثل حذف ملف cache قد لا يكون موجودًا.</p>\n</aside>\n<h2 id=\"sec-5\" tabindex=\"-1\">رسائل يفهمها المستخدم</h2>\n<p><code dir=\"ltr\">print(error)</code> تعرض <code dir=\"ltr\">overLimit(by: 12)</code>، وهذا مقبول في السجلّ لكنه بلا فائدة في تنبيه. اجعل النوع يتوافق مع <code dir=\"ltr\">LocalizedError</code> لتقدّم نصًا يفهمه البشر:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">Swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">extension</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> BudgetError</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">LocalizedError </span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> errorDescription: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        switch</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> self</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        case</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> .emptyTitle</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"Give the expense a name.\"</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        case</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> .nonPositiveAmount</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"Enter an amount greater than zero.\"</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        case</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> .</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">overLimit</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> by)</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"This puts you </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">by.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">formatted</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">currency</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">code</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"USD\"</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">))</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> over budget.\"</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>الآن تعيد <code dir=\"ltr\">error.localizedDescription</code> تلك الجملة، جاهزة لتنبيه في SwiftUI.</p>\n<h2 id=\"sec-6\" tabindex=\"-1\">الـ typed throws في Swift 6</h2>\n<p><code dir=\"ltr\">throws</code> العادية تعني «قد ترمي أي <code dir=\"ltr\">Error</code>». وتضيف Swift 6 الـ <strong>typed throws</strong> (الرمي محدّد النوع)، حيث يسمّي التوقيع النوع الدقيق:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">Swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">mutating</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> func</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> record</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">title</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">amount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: Decimal) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">throws</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(BudgetError) {</span></span>\n<span class=\"line\"><span style=\"color:#6E7781;--shiki-dark:#8B949E\">    // same body; inside, `throw .emptyTitle` can drop the type name</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>الفائدة تظهر في موضع الاستدعاء. حين ترمي كل <code dir=\"ltr\">try</code> داخل كتلة <code dir=\"ltr\">do</code> الخطأ <code dir=\"ltr\">BudgetError</code>، يكون <code dir=\"ltr\">error</code> داخل <code dir=\"ltr\">catch</code> من النوع <code dir=\"ltr\">BudgetError</code> لا <code dir=\"ltr\">any Error</code>، فيمكنك كتابة switch شاملة عليه دون حالة التقاط عامة. اكتب <code dir=\"ltr\">do throws(BudgetError)</code> لتصرّح بذلك صراحةً:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"5\"><div class=\"code-head\"><span class=\"code-title\">Swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">do</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> throws</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(BudgetError) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    try</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> food.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">record</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">title</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Snack\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">amount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">100</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">} </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">catch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    switch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> error {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    case</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> .emptyTitle</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Give it a name\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    case</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> .nonPositiveAmount</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Amount must be positive\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    case</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> .</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">overLimit</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> by)</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">:</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"That's </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">by</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> over budget\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>الثمن هو الجمود: متى وعدت الدالة بـ <code dir=\"ltr\">BudgetError</code>، لا تستطيع أن تبدأ برمي خطأ فكّ ترميز أو خطأ شبكة دون تغيير توقيعها وكل من يستدعيها. وتوصية مشروع Swift نفسه هي إبقاء <code dir=\"ltr\">throws</code> غير المحدّدة النوع خيارًا افتراضيًا، واستخدام الـ typed throws داخل الـ module حيث يعالج المستدعون كل حالة، كما يفعل التحقّق في Pocket Budget. وفي الشبكات لاحقًا في الدورة ستبقى مع <code dir=\"ltr\">throws</code> العادية، لأن تلك الاستدعاءات قد تفشل بطرق أكثر مما تتحكّم فيه.</p>\n<p>بهذا يكتمل قسم التصميم. صار بإمكانك الآن وصف بيانات Pocket Budget وحالات فشلها بدقة. بعد ذلك تضع هذه البيانات على الشاشة باستخدام SwiftUI.</p>\n","toc":[{"id":"sec-1","text":"الأخطاء enums","level":2},{"id":"sec-2","text":"throw وtry","level":2},{"id":"sec-3","text":"معالجة الأخطاء بـ do-catch","level":2},{"id":"sec-4","text":"try? وtry!","level":2},{"id":"sec-5","text":"رسائل يفهمها المستخدم","level":2},{"id":"sec-6","text":"الـ typed throws في Swift 6","level":2}],"takeaways":["صِف الطرق التي قد تفشل بها عملية ما كـ enum يتوافق مع <code dir=\"ltr\">Error</code>، مع قيم مرتبطة للتفاصيل.","كل استدعاء لدالة ترمي أخطاء يُعلَّم بـ <code dir=\"ltr\">try</code>، فيرى القارئ بالضبط أين قد يقفز التنفيذ إلى الخارج.","الكتلة <code dir=\"ltr\">do</code>-<code dir=\"ltr\">catch</code> تعالج الأخطاء؛ ويمكن لجمل <code dir=\"ltr\">catch</code> أن تطابق حالات محدّدة، وتستقبل <code dir=\"ltr\">catch</code> الأخيرة الباقي باسم <code dir=\"ltr\">error</code>.","<code dir=\"ltr\">try?</code> تحوّل الفشل إلى <code dir=\"ltr\">nil</code> وتتخلّص من السبب، فاستخدمها فقط حين لا يهمّك السبب فعلًا.","الـ typed throws (<code dir=\"ltr\">throws(BudgetError)</code>) تناسب الكود الذي يعالج فيه المستدعون كل حالة؛ أما <code dir=\"ltr\">throws</code> غير المحدّدة النوع فتبقى الخيار الافتراضي الأفضل للـ APIs العامة."],"further":[{"title":"Error Handling","url":"https://docs.swift.org/swift-book/documentation/the-swift-programming-language/errorhandling/"},{"title":"LocalizedError","url":"https://developer.apple.com/documentation/foundation/localizederror"}],"quiz":[{"q":"<p>لديك <code dir=\"ltr\">func record(...) throws</code>. ماذا يعطيك <code dir=\"ltr\">let result = try? budget.record(title: &quot;Tea&quot;, amount: 3)</code> حين يفشل؟</p>","options":[{"text":"الخطأ نفسه، مخزّنًا في <code dir=\"ltr\">result</code>.","why":"<code dir=\"ltr\">try?</code> تتخلّص من الخطأ كليًا؛ لا تعرف إلا أن شيئًا ما فشل."},{"text":"<code dir=\"ltr\">nil</code>، ويضيع السبب.","why":"صحيح. هذه هي المقايضة مع <code dir=\"ltr\">try?</code>، ولهذا لا تناسب إلا حالات الفشل التي كنت ستتجاهلها أصلًا."},{"text":"عطل مع وصف الخطأ.","why":"هذا ما تفعله <code dir=\"ltr\">try!</code>. أما <code dir=\"ltr\">try?</code> فلا تُسقط التطبيق أبدًا."}],"answer":1},{"q":"<p>دالة تستدعي <code dir=\"ltr\">try budget.record(...)</code> لكن ليس فيها <code dir=\"ltr\">do</code>-<code dir=\"ltr\">catch</code>. متى تُترجَم؟</p>","options":[{"text":"حين تكون الدالة نفسها معلّمة بـ <code dir=\"ltr\">throws</code>، فينتقل الخطأ إلى من استدعاها.","why":"صحيح. تصعد الأخطاء في سلسلة الاستدعاءات حتى يلتقطها شيء ما."},{"text":"أبدًا؛ كل <code dir=\"ltr\">try</code> يجب أن تكون داخل كتلة <code dir=\"ltr\">do</code>.","why":"الدالة التي ترمي أخطاء تستطيع أن تمرّر الأخطاء إلى من استدعاها دون أن تلتقطها."},{"text":"دائمًا؛ الأخطاء غير الملتقَطة تُسجَّل وتُتجاهل.","why":"تفحص Swift معالجة الأخطاء وقت الترجمة. والخطأ غير الملتقَط في دالة لا ترمي أخطاء هو خطأ ترجمة."}],"answer":0},{"q":"<p>متى تكون الـ typed throws، مثل <code dir=\"ltr\">throws(BudgetError)</code>، الخيار الأفضل؟</p>","options":[{"text":"لكل دالة، لأن الأخطاء محدّدة النوع أدقّ دائمًا.","why":"توصية Apple عكس ذلك؛ تبقى <code dir=\"ltr\">throws</code> غير المحدّدة النوع هي الافتراضية لأنها تسمح للـ API بإضافة أنواع فشل جديدة لاحقًا."},{"text":"للـ APIs العامة في المكتبات التي تعتمد عليها فرق أخرى.","why":"هنا بالتحديد يؤلم نوع الخطأ الثابت أكثر؛ فإضافة نوع فشل لاحقًا تصبح تغييرًا كاسرًا."},{"text":"للكود داخل الـ module الخاص بك حيث يعالج المستدعون كل حالة بشكل شامل.","why":"صحيح. هنا تؤتي معرفة النوع الدقيق ثمارها، مع <code dir=\"ltr\">switch</code> بلا حالة التقاط عامة."}],"answer":2}],"exercise":{"kind":"choice","options":["a","b","c","d"],"correct":["d"],"title":"تتبّع الأخطاء","prompt":"<p>يستخدم هذا المقطع <code dir=\"ltr\">CategoryBudget</code> و<code dir=\"ltr\">BudgetError</code> من الدرس: ترمي <code dir=\"ltr\">record</code> الخطأ <code dir=\"ltr\">.emptyTitle</code> للعنوان الفارغ، و<code dir=\"ltr\">.nonPositiveAmount</code> للمبلغ الذي يساوي صفرًا أو أقل، و<code dir=\"ltr\">.overLimit(by:)</code> حين يتجاوز المجموع الجديد الحدّ. توقّع كل ما تطبعه الحلقة.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Swift</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"نسخ الكود\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>نسخ</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> food </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> CategoryBudget</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">limit</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">50</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> attempts: [(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, Decimal)] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Lunch\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">12</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), (</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">5</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">), (</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Dinner\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">45</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">for</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (title, amount) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">in</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> attempts {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    do</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">        try</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> food.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">record</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">title</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: title, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">amount</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: amount)</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">        print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Saved </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">catch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> BudgetError.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">overLimit</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> by) {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">        print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Over by </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">by</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">catch</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">        print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Rejected: </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">error</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    }</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(food.spent)</span></span></code></pre></div>\n","hints":["تتبّع قيمة <code dir=\"ltr\">spent</code> بعد كل محاولة. هل تغيّرها المحاولة الفاشلة؟","المحاولة الثانية تفشل حتى قبل فحص المبلغ. أيّ guard تأتي أولًا؟","في المحاولة الثالثة، سيكون المجموع الجديد 12 + 45. بكم يتجاوز ذلك 50؟"],"explanation":"<p>يجتاز Lunch كل الـ guards، فتصبح <code dir=\"ltr\">spent</code> مساوية لـ 12 ويُطبع “Saved Lunch”. العنوان الفارغ يفشل في أول guard؛ و<code dir=\"ltr\">.emptyTitle</code> لا يطابق نمط <code dir=\"ltr\">overLimit</code>، فتطبع <code dir=\"ltr\">catch</code> المجرّدة “Rejected: emptyTitle”. أما Dinner فسيرفع المجموع إلى 57، فترمي <code dir=\"ltr\">record</code> الخطأ <code dir=\"ltr\">.overLimit(by: 7)</code> وتطبع أول catch العبارة “Over by 7”.</p>\n<p>ولأن <code dir=\"ltr\">record</code> ترمي الخطأ قبل أن تُسند <code dir=\"ltr\">spent</code>، فالمحاولات الفاشلة لا تغيّر شيئًا، ويطبع السطر الأخير 12. كتابة كل الفحوصات أولًا والتعديل في النهاية هي ما يجعل استدعاء الدالة مرة أخرى بعد الفشل آمنًا.</p>\n","checkLabels":{},"optionText":{"a":{"text":"Saved Lunch / Saved / Saved Dinner / 62","why":"هذا يتجاهل فحصَي guard كليهما. العنوان الفارغ والمجموع 57 مقابل حدّ 50 كلاهما يرمي خطأً."},"b":{"text":"Saved Lunch / Rejected: emptyTitle / Over by 7 / 57","why":"محاولة التجاوز ترمي الخطأ قبل تنفيذ <code dir=\"ltr\">spent = newTotal</code>، فلا يصل المجموع أبدًا إلى 57."},"c":{"text":"Saved Lunch / Over by 7 / 12","why":"العنوان الفارغ ما زال ينتج مخرجات. تلتقطه <code dir=\"ltr\">catch</code> الأخيرة المجرّدة، فتطبع “Rejected: emptyTitle”."},"d":{"text":"Saved Lunch / Rejected: emptyTitle / Over by 7 / 12","why":"صحيح. كل فشل تلتقطه أول جملة مطابقة، والمحاولات الفاشلة تترك <code dir=\"ltr\">spent</code> دون تغيير."}}},"words":624,"readMinutes":4}