{"id":"l-sw-2-3","courseId":"swift-essentials","lang":"ar","kind":"lesson","summary":"عرّف الـ protocols كعقود، وتبنَّ الـ protocols القياسية مثل Identifiable وHashable، وأضف سلوكًا افتراضيًا عبر الـ protocol extensions، ووسّع أنواعًا لا تملكها.","html":"<p>يوشك Pocket Budget أن يضيف نوعًا ثانيًا من التكاليف. إلى جانب المصاريف التي تحدث مرة واحدة هناك الاشتراكات، التي تُدفع شهريًا أو سنويًا. لا يجب أن تهتم شاشة الملخّص بأيّهما أمامها: تحتاج من كلٍّ منهما عنوانًا وتكلفة شهرية. الوراثة ستُجبر الاثنين على الدخول في هرمية classes. أما جواب Swift فهو الـ <strong>protocol</strong> (البروتوكول): قائمة متطلّبات يستطيع أي نوع أن يتعهّد بتحقيقها، سواء كان struct أو class أو enum.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">كتابة protocol</h2>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">BudgetItem.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\">protocol</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> BudgetItem</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\"> title: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">get</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\"> monthlyCost: Decimal { </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">get</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\">{ get }</code> تعني «قابلة للقراءة». يستطيع النوع المتوافق أن يحقّقها بخاصية مخزّنة أو محسوبة. إليك نوعين متوافقين، يعتمدان على الـ enum المسمّى <code dir=\"ltr\">Category</code> من الدرس السابق:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"swift\" data-index=\"1\"><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\">struct</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Expense</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">Identifiable</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">Hashable</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">BudgetItem </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\"> id </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> UUID</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\"> title: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> amount: Decimal</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> category: Category</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> date: Date</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> monthlyCost: Decimal { amount }</span></span>\n<span class=\"line\"><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\">struct</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Subscription</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">BudgetItem </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\"> title: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> price: Decimal</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> billedYearly: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">Bool</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    var</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> monthlyCost: Decimal {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        billedYearly </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> price </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">/</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 12</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> :</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> price</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\">monthlyCost</code> وسيوقفك الـ compiler بالرسالة “type ‘Subscription’ does not conform to protocol ‘BudgetItem’”، ويعرض عليك Xcode إضافة هياكل فارغة لكل ما ينقص.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">protocols قياسية ستستخدمها كل يوم</h2>\n<p>يتبنّى <code dir=\"ltr\">Expense</code> أيضًا اثنين من الـ protocols في المكتبة القياسية. <strong>Identifiable</strong> يتطلّب خاصية <code dir=\"ltr\">id</code>، ويستخدمها <code dir=\"ltr\">List</code> و<code dir=\"ltr\">ForEach</code> في SwiftUI للتمييز بين الصفوف، لذلك ستراه على معظم النماذج في هذه الدورة. و<strong>Hashable</strong> يسمح بوضع المصاريف في <code dir=\"ltr\">Set</code> أو استخدامها كمفاتيح في قاموس، وهو يتضمّن <strong>Equatable</strong> الذي يعطيك <code dir=\"ltr\">==</code>.</p>\n<p>لم تكتب <code dir=\"ltr\">==</code> ولا دالة hash. في الـ structs والـ enums، <strong>تولّد</strong> Swift التوافق مع <code dir=\"ltr\">Equatable</code> و<code dir=\"ltr\">Hashable</code> تلقائيًا حين تكون كل خاصية مخزّنة متوافقة أصلًا، و<code dir=\"ltr\">UUID</code> و<code dir=\"ltr\">String</code> و<code dir=\"ltr\">Decimal</code> و<code dir=\"ltr\">Date</code> و<code dir=\"ltr\">Category</code> كلها كذلك. ولأن <code dir=\"ltr\">id</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>protocol له نوع متوافق واحد</span></p>\n<p>كثيرًا ما يكتب المبتدئون <code dir=\"ltr\">protocol ExpenseProtocol</code> بجانب <code dir=\"ltr\">struct Expense</code> «من أجل المرونة». الـ protocol الذي له تنفيذ واحد يضيف طبقة للقراءة ولا يمنحك شيئًا. أضِف واحدًا حين يحتاج نوع ثانٍ فعلًا إلى شغل المكان نفسه، كما يفعل <code dir=\"ltr\">Subscription</code> هنا، أو حين تحتاج الاختبارات إلى نسخة مزيّفة.</p>\n</aside>\n<h2 id=\"sec-3\" tabindex=\"-1\">سلوك افتراضي عبر الـ protocol extensions</h2>\n<p>كل عنصر في الميزانية يحتاج سعرًا منسّقًا. بدل أن تكتبه مرتين، وسّع الـ protocol:</p>\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\">extension</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> BudgetItem</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">    func</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> formattedCost</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">currencyCode</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"USD\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-></span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">        monthlyCost.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">formatted</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">currency</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">code</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: currencyCode))</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>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> coffee </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> Expense</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\">\"Coffee\"</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\">4.5</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">category</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: .food, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">date</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: .now)</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> music </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> Subscription</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\">\"Music\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">price</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">120</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">billedYearly</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">true</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">coffee.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">formattedCost</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">()                     </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// \"$4.50\"</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">music.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">formattedCost</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">currencyCode</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"EUR\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">)   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// \"€10.00\"</span></span></code></pre></div>\n<p>كل نوع يتوافق مع <code dir=\"ltr\">BudgetItem</code>، الآن أو في المستقبل، يحصل على <code dir=\"ltr\">formattedCost</code> دون أي جهد. هذا هو جوهر ما يقصده الناس بـ Swift الموجّهة بالـ protocols: protocols صغيرة، وسلوك مشترك في الـ extensions، تتبنّاها أنواع القيم.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 230\" role=\"img\" aria-labelledby=\"l-sw-2-3-t1\"><defs><marker id=\"arrow-l-sw-2-3-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-3-t1\">يتوافق Expense وSubscription كلاهما مع الـ protocol المسمّى BudgetItem، الذي يتطلّب title وmonthlyCost. ويضيف protocol extension الدالة formattedCost إلى كل نوع متوافق.</title>\n  <rect class=\"d-box-primary\" x=\"230\" y=\"20\" width=\"220\" height=\"70\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"340\" y=\"48\" text-anchor=\"middle\">protocol BudgetItem</text>\n  <text class=\"d-label-muted\" x=\"340\" y=\"72\" text-anchor=\"middle\">title, monthlyCost</text>\n  <rect class=\"d-box-accent\" x=\"490\" y=\"30\" width=\"180\" height=\"50\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"580\" y=\"60\" text-anchor=\"middle\">formattedCost()</text>\n  <path class=\"d-line d-dashed\" d=\"M450 55 L490 55\"/>\n  <text class=\"d-label-muted\" x=\"580\" y=\"102\" text-anchor=\"middle\">من الـ extension</text>\n  <rect class=\"d-box-success\" x=\"80\" y=\"150\" width=\"200\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"180\" y=\"183\" text-anchor=\"middle\">struct Expense</text>\n  <rect class=\"d-box-success\" x=\"400\" y=\"150\" width=\"220\" height=\"56\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"510\" y=\"183\" text-anchor=\"middle\">struct Subscription</text>\n  <path class=\"d-arrow\" d=\"M180 150 L300 92\" marker-end=\"url(#arrow-l-sw-2-3-1)\"/>\n  <path class=\"d-arrow\" d=\"M510 150 L380 92\" marker-end=\"url(#arrow-l-sw-2-3-1)\"/>\n  <text class=\"d-label-muted\" x=\"340\" y=\"135\" text-anchor=\"middle\">يتوافق مع</text>\n</svg><figcaption>نوعان، protocol واحد، وسلوك مشترك</figcaption></figure>\n<h2 id=\"sec-4\" tabindex=\"-1\">some وany</h2>\n<p>كلمتان مفتاحيتان تصفان «قيمة من نوع ما متوافق»:</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\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items: [</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">any</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> BudgetItem] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [coffee, music]</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> monthly </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> items.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">reduce</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">$0</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> +</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> $1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.monthlyCost }    </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// 14.5</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">func</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> cheapest</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">_</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> list: [</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">some</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> BudgetItem]) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-></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:#1F2328;--shiki-dark:#E6EDF3\">    list.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">min</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">$0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.monthlyCost </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">&#x3C;</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> $1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.monthlyCost }</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">?</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">.title</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p><code dir=\"ltr\">any BudgetItem</code> صندوق يمكن أن يحمل نوعًا متوافقًا مختلفًا في كل خانة، وهذا ما تحتاجه القائمة المختلطة. أما <code dir=\"ltr\">some BudgetItem</code> فتعني «نوعًا واحدًا محدّدًا متوافقًا، يقرّره المستدعي»: تعمل <code dir=\"ltr\">cheapest</code> مع <code dir=\"ltr\">[Expense]</code> أو مع <code dir=\"ltr\">[Subscription]</code>، لكن ليس مع خليط منهما. فضّل <code dir=\"ltr\">some</code> حين تناسب، لأن الـ compiler يعرف النوع بالضبط ويستطيع تحسين الأداء. وتستخدم SwiftUI الكلمة نفسها في <code dir=\"ltr\">var body: some View</code> لكن في موضع القيمة المُعادة، حيث تنقلب الأدوار: تنفيذ الـ view هو الذي يختار النوع المحدّد الوحيد، ولا يعرف المستدعي عنه إلا أنه <code dir=\"ltr\">View</code>.</p>\n<h2 id=\"sec-5\" tabindex=\"-1\">توسيع أنواع لا تملكها</h2>\n<p>الـ extensions (الامتدادات) لا تقتصر على الـ protocols. يمكنك إضافة خصائص محسوبة وmethods وتوافقات إلى أي نوع، بما في ذلك أنواع Apple:</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\">extension</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Decimal</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\"> asCurrency: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">formatted</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">currency</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">code</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"USD\"</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>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">extension</span><span style=\"color:#953800;--shiki-dark:#FFA657\"> Expense</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">CustomStringConvertible </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\"> description: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">String</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"</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:#CF222E;--shiki-dark:#FF7B72\">\\(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">category.</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">rawValue</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">)</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">): </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">\\(</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">formattedCost</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">()</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>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">print</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(coffee)   </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// Coffee (food): $4.50</span></span></code></pre></div>\n<p>كثير من فرق Swift تستخدم الـ extensions أيضًا لتنظيم الملف: الخصائص المخزّنة للنوع في الأعلى، ثم extension واحد لكل توافق. وهناك قيد واحد ينطبق في كل مكان: الـ extension لا يستطيع إضافة خصائص <strong>مخزّنة</strong>، بل محسوبة فقط. كذلك حين تغيّر method في extension على struct إحدى الخصائص، فما زالت تحتاج إلى <code dir=\"ltr\">mutating</code>، تمامًا كأي method في التعريف الأصلي.</p>\n<h2 id=\"sec-6\" tabindex=\"-1\">أين ستقابل الـ protocols بعد ذلك</h2>\n<p>الـ protocols هي طريقة أطر عمل Apple في التحدّث مع كودك. في SwiftUI، كل شاشة تبنيها هي struct يتوافق مع الـ protocol المسمّى <code dir=\"ltr\">View</code>، ومتطلّبه الوحيد خاصية <code dir=\"ltr\">body</code>. ويطلب <code dir=\"ltr\">List</code> عناصر <code dir=\"ltr\">Identifiable</code> حتى يتتبّع الصفوف. ويطلب كود الشبكات <code dir=\"ltr\">Codable</code> حتى يتحوّل JSON إلى أنواعك. وتطلب الـ concurrency في Swift 6 الـ protocol المسمّى <code dir=\"ltr\">Sendable</code> قبل أن تعبر القيمة بين الـ threads. وفي كل مرة يتكرّر النمط نفسه من هذا الدرس: يحدّد إطار العمل عقدًا، ويتعهّد نوعك بتحقيقه، ويتحقّق الـ compiler من الوعد.</p>\n<p>صارت لديك الآن عدّة التصميم الكاملة: الـ structs والـ classes والـ enums والـ protocols. القطعة الأخيرة قبل SwiftUI هي ما يحدث حين تفشل عملية ما، مثل مصروف سيتجاوز الميزانية، وكيف تجعل معالجة الأخطاء في Swift الفشلَ جزءًا من نوع الدالة.</p>\n","toc":[{"id":"sec-1","text":"كتابة protocol","level":2},{"id":"sec-2","text":"protocols قياسية ستستخدمها كل يوم","level":2},{"id":"sec-3","text":"سلوك افتراضي عبر الـ protocol extensions","level":2},{"id":"sec-4","text":"some وany","level":2},{"id":"sec-5","text":"توسيع أنواع لا تملكها","level":2},{"id":"sec-6","text":"أين ستقابل الـ protocols بعد ذلك","level":2}],"takeaways":["الـ protocol يسرد متطلّبات؛ وأي struct أو class أو enum يحقّقها يمكن استخدامه في كل مكان يُتوقَّع فيه ذلك الـ protocol.","في الـ structs والـ enums، تولّد Swift التوافق مع <code dir=\"ltr\">Equatable</code> و<code dir=\"ltr\">Hashable</code> تلقائيًا حين تكون كل الخصائص المخزّنة متوافقة معهما أصلًا.","الـ protocol extension يعطي كل نوع متوافق تنفيذًا افتراضيًا دون أي جهد.","الـ extensions تضيف methods وخصائص محسوبة وتوافقات إلى أي نوع، بما في ذلك أنواع Apple، لكنها لا تضيف أبدًا خصائص مخزّنة جديدة.","<code dir=\"ltr\">some Protocol</code> تمثّل نوعًا واحدًا محدّدًا متوافقًا (في المعامل يختاره المستدعي)؛ أما <code dir=\"ltr\">any Protocol</code> فصندوق يمكن أن يحمل نوعًا متوافقًا مختلفًا في كل مرة."],"further":[{"title":"Protocols","url":"https://docs.swift.org/swift-book/documentation/the-swift-programming-language/protocols/"},{"title":"Extensions","url":"https://docs.swift.org/swift-book/documentation/the-swift-programming-language/extensions/"},{"title":"Identifiable","url":"https://developer.apple.com/documentation/swift/identifiable"}],"quiz":[{"q":"<p>للـ struct المعرّف <code dir=\"ltr\">struct Expense: Hashable</code> خصائص من الأنواع <code dir=\"ltr\">UUID</code> و<code dir=\"ltr\">String</code> و<code dir=\"ltr\">Decimal</code>. ماذا عليك أن تكتب ليصبح hashable؟</p>","options":[{"text":"لا شيء إضافيًا؛ تولّد Swift العامل <code dir=\"ltr\">==</code> والدالة <code dir=\"ltr\">hash(into:)</code> لأن كل خاصية hashable أصلًا.","why":"صحيح. التوليد التلقائي يشمل الـ structs والـ enums التي تتوافق كل خصائصها المخزّنة."},{"text":"دالة <code dir=\"ltr\">hash(into:)</code> تدمج كل خاصية يدويًا.","why":"يمكنك ذلك، لكن فقط حين تريد سلوكًا مختلفًا عن النسخة المولّدة تلقائيًا."},{"text":"‏<code dir=\"ltr\">extension Expense: Equatable</code> مع <code dir=\"ltr\">==</code> مخصّص.","why":"<code dir=\"ltr\">Hashable</code> يرث من <code dir=\"ltr\">Equatable</code>، ويُولَّد الاثنان معًا."}],"answer":0},{"q":"<p>لماذا لا تستطيع إضافة <code dir=\"ltr\">var note: String = &quot;&quot;</code> إلى <code dir=\"ltr\">Decimal</code> داخل extension؟</p>","options":[{"text":"الـ extensions تضيف methods فقط، لا خصائص من أي نوع.","why":"الـ extensions يمكنها إضافة خصائص محسوبة، مثل <code dir=\"ltr\">var asCurrency: String { … }</code>."},{"text":"<code dir=\"ltr\">Decimal</code> هو struct، والـ structs لا يمكن توسيعها.","why":"أي نوع يمكن توسيعه، بما في ذلك الـ structs الخاصة بـ Apple."},{"text":"الـ extensions لا تستطيع إضافة خصائص مخزّنة، لأن ذلك سيغيّر شكل النوع في الذاكرة.","why":"صحيح. أضف خاصية محسوبة، أو غلّف القيمة في نوع خاص بك."}],"answer":2},{"q":"<p>تحتاج مصفوفة واحدة تحمل قيمًا من <code dir=\"ltr\">Expense</code> و<code dir=\"ltr\">Subscription</code> معًا، وكلاهما يتوافق مع <code dir=\"ltr\">BudgetItem</code>. أيّ نوع تعلنه؟</p>","options":[{"text":"<code dir=\"ltr\">[some BudgetItem]</code>","why":"<code dir=\"ltr\">some</code> تعني نوعًا واحدًا محدّدًا للمصفوفة كلها؛ فلا يمكنها خلط المصاريف بالاشتراكات."},{"text":"<code dir=\"ltr\">[any BudgetItem]</code>","why":"صحيح. كل عنصر صندوق (existential) يمكن أن يحمل أي نوع متوافق."},{"text":"<code dir=\"ltr\">[BudgetItem.Type]</code>","why":"هذه مصفوفة من الأنواع نفسها (metatypes)، لا من القيم."}],"answer":1},{"q":"<p>يعرّف protocol extension الدالة <code dir=\"ltr\">formattedCost()</code>. يتوافق <code dir=\"ltr\">Subscription</code> مع الـ protocol لكنه لا ينفّذها. ماذا يحدث حين تستدعي <code dir=\"ltr\">music.formattedCost()</code>؟</p>","options":[{"text":"خطأ ترجمة، لأن <code dir=\"ltr\">Subscription</code> ينقصه أحد المتطلّبات.","why":"الدالة التي يوفّرها protocol extension تُعدّ متحقّقة تلقائيًا لكل نوع متوافق."},{"text":"عطل وقت التشغيل، لأن الدالة لا جسم لها في <code dir=\"ltr\">Subscription</code>.","why":"الجسم موجود في الـ extension، فهناك دائمًا ما يُنفَّذ."},{"text":"يُنفَّذ التنفيذ الافتراضي من الـ extension.","why":"صحيح. هذا هو هدف الـ protocol extension: سلوك مشترك يُكتب مرة واحدة."}],"answer":2}],"exercise":{"kind":"spot-bug","language":"swift","code":"import Foundation\n\nstruct Budget {\n    var limit: Decimal\n    var spent: Decimal = 0\n}\n\nextension Budget: CustomStringConvertible {\n    var remaining: Decimal { limit - spent }\n    var description: String { \"\\(spent) of \\(limit) spent\" }\n\n    func record(_ amount: Decimal) { spent += amount }\n}\n\nvar food = Budget(limit: 300)\nfood.record(42)\nprint(food)","bugLines":[12],"title":"الـ extension الذي لا يُترجَم","prompt":"<p>نقل أحد زملائك سلوك <code dir=\"ltr\">Budget</code> إلى extension، والآن لا يُبنى المشروع. اختر السطر الذي يرفضه الـ compiler.</p>\n","hints":["<code dir=\"ltr\">Budget</code> هو struct. أيّ سطر في الـ extension يغيّر إحدى خصائصه المخزّنة؟","الخصائص المحسوبة تقرأ <code dir=\"ltr\">limit</code> و<code dir=\"ltr\">spent</code> فقط. ابحث عن عملية إسناد.","الـ methods في الـ structs التي تغيّر <code dir=\"ltr\">self</code> تحتاج إلى كلمة مفتاحية قبل <code dir=\"ltr\">func</code>."],"explanation":"<p>السطر 12 يغيّر <code dir=\"ltr\">spent</code> من داخل method عادية، والـ methods في الـ structs لا تستطيع تعديل <code dir=\"ltr\">self</code> إلا إذا عُلّمت بـ <code dir=\"ltr\">mutating</code>. يُبلغ الـ compiler بالرسالة “left side of mutating operator isn’t mutable: ‘self’ is immutable”. نقل الكود إلى extension لا يغيّر القواعد:</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\">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\">_</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> amount: Decimal) { spent </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> amount }</span></span></code></pre></div>\n<p>موضع الاستدعاء يستخدم <code dir=\"ltr\">var food</code> أصلًا، فيُترجَم الكود بمجرّد أن تصبح الـ method من نوع <code dir=\"ltr\">mutating</code>. أما مع <code dir=\"ltr\">let food</code> فسيفشل مرة أخرى، للسبب الذي رأيته في درس الـ structs.</p>\n","checkLabels":{}},"words":656,"readMinutes":4}