{"id":"l-rn-5-4","courseId":"react-native-apps","lang":"ar","kind":"lesson","summary":"أوصل إصلاحات JavaScript إلى مستخدمي Trailhead باستخدام EAS Update، وأبقِ التحديثات متوافقة مع الـ builds المثبّتة عبر القنوات (channels) وإصدارات وقت التشغيل (runtime versions)، وتراجع بأمان، واستقرّ على روتين إصدار مستدام.","html":"<p>تُبلغ مستخدمة أن Trailhead يرفض “8,4” مسافةً على هاتفها الألماني. تصلح أداة التحليل في خمس دقائق. ودون أي شيء آخر، يعني إيصال هذا الإصلاح إليها build جديدًا، وإرسالًا، ومراجعة، ثم انتظار أن تحدّث تطبيقها. يختصر <strong>EAS Update</strong> ذلك إلى أمر واحد: ينشر كود JavaScript وassets جديدة، وتنزّلها نسخ Trailhead المثبّتة في المرة التالية التي تبدأ فيها.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">كيف يصل التحديث إلى الهاتف</h2>\n<p>يحتوي build الإصدار على شيئين: التطبيق الأصلي (كود Swift وKotlin وC++ المترجَم، بما في ذلك كل وحدة أصلية)، و<strong>حزمة JavaScript مضمّنة</strong>. والتحديث يستبدل الجزء الثاني فقط. حين يبدأ التطبيق، يتحقّق <code dir=\"ltr\">expo-updates</code> من الخادم بحثًا عن حزمة أحدث لقناته وإصدار وقت التشغيل الخاص به. وافتراضيًا، يبدأ فورًا بالكود الذي لديه أصلًا، وينزّل التحديث الجديد في الخلفية، ويستخدمه في التشغيل <strong>التالي</strong>. لا ينتظر بدء التشغيل الشبكةَ أبدًا، وهذا مهم لتطبيق يُفتح عند بداية درب وفيه خطّ إشارة واحد.</p>\n<p>اضبطه مرة واحدة، ثم اصنع builds جديدة لتتضمّن إعدادات التحديث:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">Terminal</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:#953800;--shiki-dark:#FFA657\">eas</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> update:configure</span></span></code></pre></div>\n<p>هذا يضيف رابط التحديث و<code dir=\"ltr\">runtimeVersion</code> إلى إعدادات تطبيقك. ثم أعطِ كل build profile قناة (channel) في <code dir=\"ltr\">eas.json</code>:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"json\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">eas.json</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:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  \"build\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: {</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"preview\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: { </span><span style=\"color:#116329;--shiki-dark:#7EE787\">\"distribution\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"internal\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#116329;--shiki-dark:#7EE787\">\"channel\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"preview\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"production\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: { </span><span style=\"color:#116329;--shiki-dark:#7EE787\">\"autoIncrement\"</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 style=\"color:#116329;--shiki-dark:#7EE787\">\"channel\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"production\"</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>ونشر تحديث أمر واحد:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">Terminal</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:#953800;--shiki-dark:#FFA657\">eas</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> update</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --channel</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> production</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> --message</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> \"Accept comma decimals in distance\"</span></span></code></pre></div>\n<p>تلتقطه الـ production builds في تشغيلها التالي. انشر على <code dir=\"ltr\">preview</code> أولًا، وتحقّق منه على builds المختبِرين، ثم انشر الكود نفسه على <code dir=\"ltr\">production</code>.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">ما يمكن أن يصل عبر الهواء وما لا يمكن</h2>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"جدول قابل للتمرير\" tabindex=\"0\"><table><thead>\n<tr>\n<th>يصل باستخدام <code dir=\"ltr\">eas update</code></th>\n<th>يحتاج إلى build جديد وإصدار في المتجر</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>الـ components والشاشات والتنسيقات والمنطق</td>\n<td>إضافة وحدة أصلية أو ترقيتها</td>\n</tr>\n<tr>\n<td>الصور والخطوط المضمّنة مع JavaScript</td>\n<td>أوصاف الأذونات وغيرها من الإعدادات الأصلية</td>\n</tr>\n<tr>\n<td>إصلاح الأخطاء وتعديل النصوص</td>\n<td>أيقونة التطبيق وشاشة البداية ومعرّف الحزمة</td>\n</tr>\n<tr>\n<td>شاشات جديدة مبنية من وحدات أصلية موجودة</td>\n<td>ترقيات Expo SDK</td>\n</tr>\n</tbody>\n</table></div><p>هذا هو التمييز بين JavaScript والكود الأصلي من الدرس الثاني في الدورة، وقد صارت له عواقب الآن. إن مسّ تغيير ما الكود الأصلي أو الإعدادات الأصلية، فلا يستطيع التحديث إيصاله.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">إصدارات وقت التشغيل تُبقي التحديثات متوافقة</h2>\n<p>تخيّل أن Trailhead 1.1 يضيف <code dir=\"ltr\">expo-camera</code>. كود JavaScript الخاص به يستورد وحدة أصلية لا تحتويها builds الإصدار 1.0. لو تلقّى مستخدمو 1.0 ذلك التحديث، لانهار التطبيق حين يلمس الكاميرا. يمنع <strong>إصدار وقت التشغيل</strong> (runtime version) ذلك: كل build وكل تحديث يحمل واحدًا، ولا ينطبق التحديث إلا على الـ builds التي لها إصدار وقت التشغيل نفسه.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"json\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">app.json</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:#1F2328;--shiki-dark:#E6EDF3\">{</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">  \"expo\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: {</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"version\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"1.1.0\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">,</span></span>\n<span class=\"line\"><span style=\"color:#116329;--shiki-dark:#7EE787\">    \"runtimeVersion\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: { </span><span style=\"color:#116329;--shiki-dark:#7EE787\">\"policy\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"appVersion\"</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\">appVersion</code>، يكون إصدار وقت التشغيل هو <code dir=\"ltr\">version</code> الخاص بتطبيقك، فترفع <code dir=\"ltr\">version</code> كلما تغيّر الكود الأصلي، فتتوقّف builds الإصدارين 1.0 و1.1 عن مشاركة التحديثات. أما سياسة <code dir=\"ltr\">fingerprint</code> فتحسب hash للأجزاء الأصلية من مشروعك بدلًا من ذلك، فيتغيّر إصدار وقت التشغيل تلقائيًا حين يتغيّر الكود الأصلي. كلتاهما تعمل؛ اختر واحدة وافهمها.</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>إطلاق تغييرات أصلية بوصفها تحديثًا</span></p>\n<p>العطل الكلاسيكي: يضيف مطوّر مكتبة أصلية، ويختبر في development build جديد حيث تعمل، وينشر تحديثًا دون إصدار وقت تشغيل جديد. تتلقّى الـ builds الأقدم كود JavaScript يستدعي وحدة لا تملكها. قبل كل <code dir=\"ltr\">eas update</code>، اطرح سؤالًا واحدًا: هل تغيّر أي شيء أصلي منذ الـ build الذي تخدمه هذه القناة؟ إن كانت الإجابة نعم، فهو build جديد وإصدار في المتجر.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">حين يسوء تحديث</h2>\n<p>تصل التحديثات إلى مستخدمين كثيرين بسرعة، وهذا سلاح ذو حدّين. إن أساء أحدها التصرّف، فتراجع:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"bash\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">Terminal</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:#953800;--shiki-dark:#FFA657\">eas</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> update:rollback</span></span></code></pre></div>\n<p>يرشدك خطوة بخطوة لإعادة فرع القناة إلى تحديث سابق أو إلى الكود المضمّن في الـ build. ويحصل المستخدمون على التراجع في تشغيلهم التالي، بالطريقة نفسها التي حصلوا بها على التحديث. ولهذا يستحق كل تحديث العناية نفسها التي يستحقها الإصدار: شغّل اختباراتك، وانشر على <code dir=\"ltr\">preview</code>، وافتحه على هاتف حقيقي، ثم انشر على <code dir=\"ltr\">production</code>. يسمح المتجران بتحديث JavaScript بهذه الطريقة لإصلاح الأخطاء والتحسينات؛ أما استخدامه لتغيير ما يفعله التطبيق جوهريًا دون مراجعة فمخالف لقواعدهما.</p>\n<h2 id=\"sec-5\" tabindex=\"-1\">روتين إصدار يصمد</h2>\n<p>هذا إيقاع يناسب فريقًا صغيرًا:</p>\n<ul>\n<li><strong>إصلاحات JavaScript</strong>: اختبر، ثم <code dir=\"ltr\">eas update</code> إلى preview، وتحقّق، ثم إلى production. في اليوم نفسه.</li>\n<li><strong>التغييرات الأصلية أو الميزات الجديدة</strong>: ارفع <code dir=\"ltr\">version</code>، ثم production build، ثم TestFlight والمسار الداخلي، ثم المراجعة، ثم الإطلاق. بتخطيط مسبق، كل بضعة أسابيع.</li>\n<li><strong>مع كل إصدار</strong>: راقب تقارير الانهيار في اليوم الأول، وأبقِ <code dir=\"ltr\">eas update:rollback</code> جاهزًا.</li>\n</ul>\n<h2 id=\"sec-6\" tabindex=\"-1\">أين وصلت الآن</h2>\n<p>بدأت بـ <code dir=\"ltr\">Text</code> واحد في مشروع Expo. أما Trailhead الآن ففيه قوائم تبقى سريعة، وtabs وstacks مع params مضبوطة الأنواع، ونموذج يتعامل مع لوحة المفاتيح، وبيانات في SQLite وtoken في التخزين الآمن، وتتبّع GPS، وصور، وتذكيرات، واختبارات، وbuilds للمتاجر، وطريقة لإصلاح الأخطاء في عصر اليوم نفسه. والأهم أنك تعرف أين يختلف React Native عن الويب ولماذا: native views، وthreads اثنان، ولا cascade، وأذونات يمكن رفضها، ونوعان من التغييرات، JavaScript وأصلي.</p>\n<p>خطوات تالية جيدة: أضف Reanimated وreact-native-gesture-handler لتفاعلات أغنى، وTanStack Query لبيانات الخادم، وMaestro لبضعة اختبارات end-to-end. ثم أطلق شيئًا صغيرًا لمستخدمين حقيقيين وتعلّم مما يفعلونه به.</p>\n","toc":[{"id":"sec-1","text":"كيف يصل التحديث إلى الهاتف","level":2},{"id":"sec-2","text":"ما يمكن أن يصل عبر الهواء وما لا يمكن","level":2},{"id":"sec-3","text":"إصدارات وقت التشغيل تُبقي التحديثات متوافقة","level":2},{"id":"sec-4","text":"حين يسوء تحديث","level":2},{"id":"sec-5","text":"روتين إصدار يصمد","level":2},{"id":"sec-6","text":"أين وصلت الآن","level":2}],"takeaways":["يستبدل EAS Update حزمة JavaScript والملفات المرافقة (assets) في الـ builds المثبّتة، دون ملف تطبيق جديد ودون مراجعة المتجر.","تغييرات JavaScript والـ assets وحدها يمكن أن تصل عبر الهواء؛ أما الوحدات الأصلية الجديدة ونصوص الأذونات والأيقونات وترقيات الـ SDK فتحتاج إلى build جديد.","قناة الـ build (channel) تحدّد أي التحديثات يتلقّاها، وإصدار وقت التشغيل (runtime version) يحدّد أي التحديثات متوافقة مع كوده الأصلي.","افتراضيًا، يُنزَّل التحديث في الخلفية ويُطبَّق عند التشغيل التالي.","حين يسيء تحديث التصرّف، يعيد <code dir=\"ltr\">eas update:rollback</code> المستخدمين إلى تحديث سابق أو إلى الكود المضمّن في الـ build الخاص بهم."],"further":[{"title":"EAS Update introduction","url":"https://docs.expo.dev/eas-update/introduction/"},{"title":"How EAS Update works","url":"https://docs.expo.dev/eas-update/how-it-works/"},{"title":"Runtime versions and updates","url":"https://docs.expo.dev/eas-update/runtime-versions/"},{"title":"Rollbacks","url":"https://docs.expo.dev/eas-update/rollbacks/"}],"quiz":[{"q":"<p>أيّ تغيير في Trailhead يمكن أن يصل إلى المستخدمين باستخدام <code dir=\"ltr\">eas update</code> وحده؟</p>","options":[{"text":"إضافة <code dir=\"ltr\">expo-camera</code> لمسح رموز QR على علامات الدروب.","why":"الوحدة الأصلية الجديدة يجب أن تُترجَم داخل الملف التنفيذي، فتحتاج إلى build جديد وإصدار في المتجر."},{"text":"إصلاح خطأ الفاصلة في <code dir=\"ltr\">parseDistanceKm</code> وإعادة صياغة رسالة خطأ.","why":"صحيح. كلاهما تغيير في JavaScript، وهذا بالضبط ما يستبدله التحديث."},{"text":"تغيير وصف إذن الموقع في app.json.","why":"أوصاف الاستخدام تُترجَم إلى الإعدادات الأصلية؛ ولا تتغيّر إلا مع build جديد."},{"text":"الترقية إلى إصدار Expo SDK التالي.","why":"ترقية الـ SDK تغيّر الكود الأصلي وإصدار وقت التشغيل، فتحتاج دائمًا إلى builds جديدة."}],"answer":1},{"q":"<p>نشرت تحديثًا على قناة production. فتح مستخدم Trailhead بعد ساعة. مع الإعدادات الافتراضية، متى يرى الإصلاح؟</p>","options":[{"text":"فورًا؛ يتوقّف التطبيق عند شاشة البداية حتى يُنزَّل التحديث.","why":"افتراضيًا، يبدأ التطبيق بالكود الذي لديه أصلًا، فلا تؤخّر الشبكة البطيئة بدء التشغيل أبدًا."},{"text":"فقط بعد أن يعيد تثبيت التطبيق من المتجر.","why":"التحديثات تُثبَّت دون المتجر. وهذا هو جوهر EAS Update."},{"text":"في التشغيل الذي يلي هذا التشغيل؛ يُنزَّل التحديث في الخلفية أثناء هذه الجلسة.","why":"صحيح. يتحقّق التطبيق عند التشغيل، وينزّل في الخلفية، ويطبّق التحديث في المرة التالية التي يبدأ فيها."}],"answer":2},{"q":"<p>مستخدمو Trailhead 1.0 (المبني قبل إضافة <code dir=\"ltr\">expo-camera</code>) و1.1 (المبني بعدها) يستخدمون قناة production. نشرت تحديث JavaScript يستورد <code dir=\"ltr\">expo-camera</code>. ما الذي يحمي مستخدمي 1.0 من الانهيار؟</p>","options":[{"text":"إصدارات وقت التشغيل؛ للإصدار 1.1 إصدار وقت تشغيل مختلف، فلا تنطبق التحديثات المبنية له على builds الإصدار 1.0.","why":"صحيح. مع سياسة <code dir=\"ltr\">appVersion</code> أو <code dir=\"ltr\">fingerprint</code>، تنتج التغييرات الأصلية إصدار وقت تشغيل جديدًا، ولا تصل التحديثات إلا إلى الـ builds المتوافقة."},{"text":"لا شيء؛ يجب أن تنشر تحديثات منفصلة يدويًا وتأمل أن يحصل عليها المستخدمون الصحيحون.","why":"إصدارات وقت التشغيل موجودة بالضبط حتى لا تضطر إلى الاعتماد على الأمل."},{"text":"يثبّت EAS Update الوحدة الأصلية الناقصة مع كود JavaScript.","why":"لا تستطيع التحديثات إضافة كود أصلي. ولهذا تهمّ فحوص التوافق."}],"answer":0}],"exercise":{"kind":"choice","multiple":true,"options":["copy-fix","reducer-bug","new-screen","add-sdk-module","icon","permission-text","new-image"],"correct":["copy-fix","reducer-bug","new-screen","new-image"],"title":"تحديث أم build جديد؟","prompt":"<p>Trailhead 1.2 منشور مع ضبط <code dir=\"ltr\">runtimeVersion</code> على سياسة <code dir=\"ltr\">appVersion</code>. قائمة الفريق لهذا الأسبوع أدناه. اختر <strong>كل</strong> تغيير تستطيع إيصاله إلى مستخدمي 1.2 الحاليين باستخدام <code dir=\"ltr\">eas update</code> وحده، دون build جديد ودون مراجعة المتجر.</p>\n","hints":["اسأل عن كل تغيير: هل يمسّ JavaScript والـ assets المضمّنة فقط، أم يغيّر التطبيق الأصلي المترجَم؟","أي شيء في app.json تقرؤه المتاجر أو نظام التشغيل (الأيقونات، نصوص الأذونات) إعدادات أصلية.","أربعة تغييرات يمكن أن تصل عبر الهواء."],"explanation":"<p>النصوص، والمنطق، والشاشات الجديدة المبنية من وحدات موجودة أصلًا في الملف التنفيذي، والصور المضمّنة مع JavaScript، كلها جزء من حزمة التحديث، فتصل باستخدام <code dir=\"ltr\">eas update</code>. أما وحدة أصلية جديدة مثل <code dir=\"ltr\">expo-haptics</code> فيجب أن تُترجَم داخل التطبيق؛ وأيقونة التطبيق وأوصاف الأذونات تعيش في الإعدادات الأصلية التي يقرؤها نظام التشغيل من الملف التنفيذي المثبّت. هذه الثلاثة تحتاج إلى <code dir=\"ltr\">version</code> جديد، وproduction build، وإصدار في المتجر.</p>\n","checkLabels":{},"optionText":{"copy-fix":{"text":"أعِد صياغة حالة الفراغ في سجلّ الرحلات لتصبح “No hikes yet. Log your first one!”","why":"تحديث. إنه نصّ داخل component، وجزء من حزمة JavaScript."},"reducer-bug":{"text":"أصلح <code dir=\"ltr\">hikesReducer</code> بحيث يعيد تعديلُ تاريخ رحلة ترتيبَ القائمة.","why":"تحديث. منطق JavaScript خالص، وهو الإصلاح المثالي عبر الهواء."},"new-screen":{"text":"أضف شاشة إحصاءات سنوية مبنية فقط من View وText وFlatList.","why":"تحديث. الشاشات الجديدة لا بأس بها ما دامت كل وحدة أصلية تستخدمها موجودة أصلًا في الـ build."},"add-sdk-module":{"text":"أضف <code dir=\"ltr\">expo-haptics</code> ليهتزّ زرّ Save اهتزازًا خفيفًا.","why":"build جديد. الوحدة الأصلية غير الموجودة في الملف التنفيذي المثبّت لا يمكن أن تصل في تحديث."},"icon":{"text":"استبدل أيقونة التطبيق بالشعار الجديد.","why":"build جديد. أيقونة الشاشة الرئيسية جزء من حزمة التطبيق المثبّتة، لا من حزمة JavaScript."},"permission-text":{"text":"وضّح وصف إذن الموقع في خيارات الـ plugin الخاص بـ <code dir=\"ltr\">expo-location</code>.","why":"build جديد. خيارات الـ config plugins تُكتب في الإعدادات الأصلية حين يُبنى التطبيق."},"new-image":{"text":"استبدل رسم حالة الفراغ بصورة PNG جديدة مستوردة عبر <code dir=\"ltr\">require()</code>.","why":"تحديث. الصور المضمّنة عبر مُجمِّع JavaScript هي assets تتضمّنها التحديثات."}}},"words":719,"readMinutes":4}