الفرق بين المراجعتين ل"ReactNative/integration with existing apps"
جميل-بيلوني (نقاش | مساهمات) ط |
|||
(10 مراجعات متوسطة بواسطة 3 مستخدمين غير معروضة) | |||
سطر 1: | سطر 1: | ||
− | <noinclude>{{DISPLAYTITLE: | + | <noinclude>{{DISPLAYTITLE:دمج واجهات React Native مع تطبيقات أصيلة قائمة}}</noinclude> |
− | إطار العمل React Native رائعٌ لبرمجة تطبيق جوال جديد من البداية. لكن، يُمكنك كذلك استخدامه لإضافة | + | إطار العمل React Native رائعٌ لبرمجة تطبيق جوال جديد من البداية. لكن، يُمكنك كذلك استخدامه لإضافة واجهة (view) واحدة أو ميّزة إلى التطبيقات الأصيلة الموجودة بالفعل. يمكنك باتّباع بضعة خطواتٍ إضافةُ ميّزات جديدة باستخدام React Native إلى تطبيقك، مثل شاشات (screens) جديدة، وأقسام واجهات، ...إلخ. |
تختلف الخطوات حسب النظام الأساسي الذي تستهدفه: | تختلف الخطوات حسب النظام الأساسي الذي تستهدفه: | ||
+ | *[[ReactNative/integration with existing apps#.D8.A7.D9.84.D8.AF.D9.85.D8.AC .D9.85.D8.B9 .D8.AA.D8.B7.D8.A8.D9.8A.D9.82.D8.A7.D8.AA Android .D9.82.D8.A7.D8.A6.D9.85.D8.A9 .D9.85.D9.83.D8.AA.D9.88.D8.A8.D8.A9 .D8.A8.D9.84.D8.BA.D8.A9 J|الدمج مع تطبيقات Android قائمة مكتوبة بلغة Java]] | ||
* [[ReactNative/integration with existing apps#.D8.A7.D9.84.D8.AF.D9.85.D8.AC .D9.85.D8.B9 .D8.AA.D8.B7.D8.A8.D9.8A.D9.82.D8.A7.D8.AA iOS .D9.82.D8.A7.D8.A6.D9.85.D8.A9 .D9.85.D9.83.D8.AA.D9.88.D8.A8.D8.A9 .D8.A8.D9.84.D8.BA.D8.A9 Objec|الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Objective-C]] | * [[ReactNative/integration with existing apps#.D8.A7.D9.84.D8.AF.D9.85.D8.AC .D9.85.D8.B9 .D8.AA.D8.B7.D8.A8.D9.8A.D9.82.D8.A7.D8.AA iOS .D9.82.D8.A7.D8.A6.D9.85.D8.A9 .D9.85.D9.83.D8.AA.D9.88.D8.A8.D8.A9 .D8.A8.D9.84.D8.BA.D8.A9 Objec|الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Objective-C]] | ||
* [[ReactNative/integration with existing apps#.D8.A7.D9.84.D8.AF.D9.85.D8.AC .D9.85.D8.B9 .D8.AA.D8.B7.D8.A8.D9.8A.D9.82.D8.A7.D8.AA iOS .D9.82.D8.A7.D8.A6.D9.85.D8.A9 .D9.85.D9.83.D8.AA.D9.88.D8.A8.D8.A9 .D8.A8.D9.84.D8.BA.D8.A9 Swift|الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Swift]] | * [[ReactNative/integration with existing apps#.D8.A7.D9.84.D8.AF.D9.85.D8.AC .D9.85.D8.B9 .D8.AA.D8.B7.D8.A8.D9.8A.D9.82.D8.A7.D8.AA iOS .D9.82.D8.A7.D8.A6.D9.85.D8.A9 .D9.85.D9.83.D8.AA.D9.88.D8.A8.D8.A9 .D8.A8.D9.84.D8.BA.D8.A9 Swift|الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Swift]] | ||
− | + | ||
+ | ==الدمج مع تطبيقات Android قائمة مكتوبة بلغة Java== | ||
+ | === المفاهيم الرئيسية === | ||
+ | أهمّ الأمور لدمج مكونات React Native في تطبيق Android الخاص بك هي: | ||
+ | |||
+ | # إعداد اعتماديات (dependencies) إطار العمل React Native وهيكل المجلّدات (directory structure). | ||
+ | # كتابة مكونات React Native باستخدام [[JavaScript]]. | ||
+ | # إضافة واجهة <code>ReactRootView</code> إلى تطبيق Android الخاص بك. ستكون هذه الواجهة بمثابة الحاوية لمكون React Native الخاص بك. | ||
+ | # بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل. | ||
+ | # تحقّق من أن جانب React Native من التطبيق الخاص بك يعمل كما هو متوقع. | ||
+ | |||
+ | ===المتطلبات الأساسية=== | ||
+ | اتبع إرشادات [[ReactNative/getting started|بناء مشاريعٍ بشيفرة أصيلة في الدرس التقديمي]] لتهيئة بيئة التطوير الخاصة بك لإنشاء تطبيقات React Native لنظام التشغيل Android. | ||
+ | |||
+ | ====1. إعداد بنية المجلدات (directory structure)==== | ||
+ | لتسهيل العمل، أنشئ مجلدًا جديدًا لمشروع React Native المُدمَج الخاص بك، ثم انسخ مشروع Android الحالي إلى مجلد فرعي باسم <code>/android</code>. | ||
+ | |||
+ | ====2. تثبيت اعتماديات JavaScript==== | ||
+ | انتقل إلى المجلّد الجذر (root directory) للمشروع الخاص بك وأنشئ ملف <code>package.json</code> جديد بالمحتويات التالية: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | { | ||
+ | "name": "MyReactNativeApp", | ||
+ | "version": "0.0.1", | ||
+ | "private": true, | ||
+ | "scripts": { | ||
+ | "start": "yarn react-native start" | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | بعد ذلك، تأكّد من [https://yarnpkg.com/lang/en/docs/install تثبيت مدير الحزم Yarn]. | ||
+ | |||
+ | ثبّت حزمتي <code>react</code> و<code>react-native</code>. افتح طرفيّةً (terminal) أو مَحثَّ أوامر (command prompt)، ثم انتقل إلى المجلّد الذي يحتوي على ملف <code>package.json</code> ونفّذ الأمر التالي: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | $ yarn add react-native | ||
+ | </syntaxhighlight> | ||
+ | سيؤدي هذا إلى طباعة رسالةٍ مشابهة لما يلي (مرِّر لأعلى مُخرجات <code>yarn</code> لمشاهدتها): | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | warning "react-native@0.52.2" has unmet peer dependency "react@16.2.0". | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | هذا يعني أنّنا بحاجة إلى تثبيت React كذلك: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | $ yarn add react@version_printed_above | ||
+ | </syntaxhighlight> | ||
+ | ضع مكان version_printed_above الإصدار المذكور في رسالة التحذير التي ظهرت لك آنفًا ضمن "react@16.2.0". | ||
+ | |||
+ | أنشأَ Yarn مجلدَ <code>/node_modules</code> جديد. يخزّن هذا المجلد جميع اعتماديات JavaScript المطلوبة لإنشاء مشروعك. | ||
+ | |||
+ | أضف <code>node_modules/</code> إلى ملفّ <code>.gitignore</code> الخاص بك. | ||
+ | |||
+ | ===إضافة React Native إلى تطبيقك=== | ||
+ | ====إعداد maven==== | ||
+ | أضف اعتماديّة React Native إلى ملفّ <code>build.gradle</code> الخاص بتطبيقك: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | dependencies { | ||
+ | implementation "com.android.support:appcompat-v7:27.1.1" | ||
+ | ... | ||
+ | implementation "com.facebook.react:react-native:+" // From node_modules | ||
+ | implementation "org.webkit:android-jsc:+" | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | '''ملاحظة:''' إذا أردت التأكد من استعمال نسخة معينة من React Native على الدوام في بناء تطبيقك الأصيل، فاستبدل <code>+</code> بنسخة React Native التي نزّلتها من <code>npm</code>. | ||
+ | |||
+ | أضف خانةً إلى مجلّد maven الخاص بإطار React Native إلى ملفّ <code>build.gradle</code>. تأكد من إضافته إلى كتلة "allprojects"، فوق مستودعات maven الأخرى: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | allprojects { | ||
+ | repositories { | ||
+ | maven { | ||
+ | // All of React Native (JS, Android binaries) is installed from npm | ||
+ | url "$rootDir/../node_modules/react-native/android" | ||
+ | } | ||
+ | maven { | ||
+ | // Android JSC is installed from npm | ||
+ | url("$rootDir/../node_modules/jsc-android/dist") | ||
+ | } | ||
+ | ... | ||
+ | } | ||
+ | ... | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | '''ملاحظة:''' تأكّد من أن المسار صحيح! لا ينبغي أن تواجهك أية أخطاء من قبيل Failed to resolve: com.facebook.react:react-native:0.x.x بعد تشغيل مزامنة Gradle في Android Studio. | ||
+ | |||
+ | ==== تفعيل الربط التلقائي للوحدات الأصيلة ==== | ||
+ | للاستفادة من قوة الربط التلقائي [https://github.com/react-native-community/cli/blob/master/docs/autolinking.md autolinking] يجب أن نطبقه في عدة مواضع، أولًا أضف المدخل التالي إلى <code>settings.gradle</code>:<syntaxhighlight lang="javascript"> | ||
+ | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) | ||
+ | </syntaxhighlight>ثم أضف المدخل التالي إلى أسفل <code>app/build.gradle</code>:<syntaxhighlight lang="javascript"> | ||
+ | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ====إعداد الأذونات (permissions)==== | ||
+ | بعد ذلك، تأكد من تفعيل إذن الإنترنت في ملفّ <code>AndroidManifest.xml</code>: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | <uses-permission android:name="android.permission.INTERNET" /> | ||
+ | </syntaxhighlight> | ||
+ | إذا كنت بحاجة إلى الوصول إلى نشاط <code>DevSettingsActivity</code>، فأضف السطر التالي إلى <code>AndroidManifest.xml</code>: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" /> | ||
+ | </syntaxhighlight> | ||
+ | يُستخدَم هذا فقط في وضع التطوير (dev mode) عند إعادة تحميل JavaScript من خادم التطوير، لذا يمكنك إزالته في بناءات الإصدار (release builds) إذا كنت بحاجة إلى ذلك. | ||
+ | |||
+ | ====مرور النصوص الواضحة (مستويات الواجهة البرمجية الأعلى من 28)==== | ||
+ | بدءًا من Android 9 (مستوى API 28)، يُعطَّل مرور النصوص الواضحة (cleartext traffic) بشكل افتراضي؛ يمنع هذا تطبيقك من الاتصال بالمجمّع [https://facebook.github.io/metro/ Metro]. التغييرات أدناه تسمح بمرور النصوص الواضحة في بناءات التنقيح (debug builds). | ||
+ | |||
+ | أضف خيار <code>usesCleartextTraffic</code> إلى <code>Debug AndroidManifest.xml</code> الخاص بك:<syntaxhighlight lang="javascript"> | ||
+ | <!-- ... --> | ||
+ | <application | ||
+ | android:usesCleartextTraffic="true" tools:targetApi="28" > | ||
+ | <!-- ... --> | ||
+ | </application> | ||
+ | <!-- ... --> | ||
+ | </syntaxhighlight> | ||
+ | هذا غير مطلوب لبناءات الإصدار (Release builds). | ||
+ | |||
+ | لمعرفة المزيد حول ضبط أمان الشبكة (Network Security Config) وسياسة مرور النصوص الواضحة (cleartext traffic policy)، راجع [https://developer.android.com/training/articles/security-config#CleartextTrafficPermitted هذا الرابط]. | ||
+ | |||
+ | ====دمج الشيفرة==== | ||
+ | سنُعدّل الآن تطبيق Android الأصيل لدمج React Native معه. | ||
+ | |||
+ | =====مكون React Native===== | ||
+ | الجزء الأول من الشيفرة التي سنكتبها هي شيفرة React Native لشاشة "Hello World" الجديدة التي سندمجها في تطبيقنا. | ||
+ | |||
+ | ======1. إنشاء ملف <code>index.js</code>====== | ||
+ | أولاً، أنشئ ملفّ <code>index.js</code> فارغ في جذر مشروع React Native الخاص بك. | ||
+ | |||
+ | ملفّ <code>index.js</code> هو نقطة البداية لتطبيقات React Native، وهو ضروري دائمًا. يمكن أن يكون ملفًا صغيرًا يطلب (يستورد <code>require</code>) ملفًا آخر يمثّل جزءًا من مكوّن أو تطبيق React Native الخاص بك، أو يمكن أن يحتوي على كامل الشيفرة البرمجية اللازمة لذلك. في حالتنا، سنضع كامل الشيفرة في ملفّ <code>index.js</code>. | ||
+ | |||
+ | ======2. أضف شيفرة React Native الخاصة بك====== | ||
+ | |||
+ | أنشئ المكوّن في ملفّ <code>index.js</code> الخاص بك. في نموذجنا هنا، سنضيف مكون <code>[[ReactNative/text|<Text>]]</code> بسيط داخل مكوّن [[ReactNative/view|<code><View></code>]] مُنسَّق: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | import React from 'react'; | ||
+ | import { | ||
+ | AppRegistry, | ||
+ | StyleSheet, | ||
+ | Text, | ||
+ | View | ||
+ | } from 'react-native'; | ||
+ | |||
+ | const HelloWorld = () => { | ||
+ | return ( | ||
+ | <View style={styles.container}> | ||
+ | <Text style={styles.hello}>Hello, World</Text> | ||
+ | </View> | ||
+ | ); | ||
+ | }; | ||
+ | var styles = StyleSheet.create({ | ||
+ | container: { | ||
+ | flex: 1, | ||
+ | justifyContent: 'center' | ||
+ | }, | ||
+ | hello: { | ||
+ | fontSize: 20, | ||
+ | textAlign: 'center', | ||
+ | margin: 10 | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | AppRegistry.registerComponent( | ||
+ | 'MyReactNativeApp', | ||
+ | () => HelloWorld | ||
+ | ); | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | ======3. ضبط الأذونات لشاشة رسائل أخطاء التطوير====== | ||
+ | إذا كان تطبيقك يستهدف واجهة Android البرمجيّة من المستوى 23 أو أكثر، فتأكد من تمكين إذن <code>android.permission.SYSTEM_ALERT_WINDOW</code> لبناء التطوير (development build). يمكنك التحقق من ذلك باستخدام <code>Settings.canDrawOverlays(this);</code>. هذا مطلوب في بناءات التطوير لأنّه من الواجب عرض أخطاء تطوير React Native فوق كل النوافذ الأخرى. نظرًا لنظام الأذونات الجديد الذي قُدِّم في المستوى 23 من الواجهة البرمجيّة (Android M)، فلا بدّ للمستخدم أن يُوافق على عرض شاشة فوق جميع الشاشات الأخرى. يمكن تفعيل الإذن عن طريق إضافة الشيفرة التالية إلى النشاط (Activity) في التابع <code>onCreate()</code>. | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | private final int OVERLAY_PERMISSION_REQ_CODE = 1; // اختر أي قيمة | ||
+ | ... | ||
+ | |||
+ | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||
+ | if (!Settings.canDrawOverlays(this)) { | ||
+ | Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, | ||
+ | Uri.parse("package:" + getPackageName())); | ||
+ | startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | أخيرًا، يجب إعادة تعريف (override) التابع <code>onActivityResult()</code> (كما هو موضح في الشيفرة أدناه) للتعامل مع حالتي قبول الإذن أو رفضه لتجربة مستخدم متجانسة. إضافةً إلى ذلك، لدمج الوحدات النمطية الأصيلة التي تستخدم <code>startActivityForResult</code>، نحتاج إلى تمرير النتيجة إلى التابع <code>onActivityResult</code> الخاص بنسخة <code>ReactInstanceManager</code> الخاصة بنا. | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | @Override | ||
+ | protected void onActivityResult(int requestCode, int resultCode, Intent data) { | ||
+ | if (requestCode == OVERLAY_PERMISSION_REQ_CODE) { | ||
+ | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||
+ | if (!Settings.canDrawOverlays(this)) { | ||
+ | // SYSTEM_ALERT_WINDOW إذن غير معطى | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | mReactInstanceManager.onActivityResult( this, requestCode, resultCode, data ); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ====الرابط العجيب: <code>ReactRootView</code>==== | ||
+ | لنُضِف شيفرةً أصيلة لبدء مشغل React Native الآني (React Native runtime) ولنجعَله يُصيِّر مكوّن JavaScript الخاص بنا. للقيام بهذا، سنُنشئ نشاطًا <code>Activity</code> ليُنشئ واجهة <code>ReactRootView</code>، ويبدأ تطبيق React بداخله مع ضبطه كواجهة رئيسيّة للمحتوى. | ||
+ | |||
+ | '''ملاحظة:''' إذا كنت تستهدف نسخ Android الأقدم من النسخة 5، فاستعمل صنف <code>AppCompatActivity</code> من حزمة <code>com.android.support:appcompat</code> عوضًا عن <code>Activity</code>. | ||
+ | |||
+ | <syntaxhighlight lang="javascript"> | ||
+ | |||
+ | public class MyReactActivity extends Activity implements DefaultHardwareBackBtnHandler { | ||
+ | private ReactRootView mReactRootView; | ||
+ | private ReactInstanceManager mReactInstanceManager; | ||
+ | |||
+ | @Override | ||
+ | protected void onCreate(Bundle savedInstanceState) { | ||
+ | super.onCreate(savedInstanceState); | ||
+ | SoLoader.init(this, false); | ||
+ | |||
+ | mReactRootView = new ReactRootView(this); | ||
+ | List<ReactPackage> packages = new PackageList(getApplication()).getPackages(); | ||
+ | // Packages that cannot be autolinked yet can be added manually here, for example: | ||
+ | // packages.add(new MyReactNativePackage()); | ||
+ | // Remember to include them in `settings.gradle` and `app/build.gradle` too. | ||
+ | |||
+ | mReactInstanceManager = ReactInstanceManager.builder() | ||
+ | .setApplication(getApplication()) | ||
+ | .setCurrentActivity(this) | ||
+ | .setBundleAssetName("index.android.bundle") | ||
+ | .setJSMainModulePath("index") | ||
+ | .addPackages(packages) | ||
+ | .setUseDeveloperSupport(BuildConfig.DEBUG) | ||
+ | .setInitialLifecycleState(LifecycleState.RESUMED) | ||
+ | .build(); | ||
+ | // السلسلة النصيّة ("MyReactNativeApp") | ||
+ | // يجب أن تُطابق السلسلة النصيّة الموجودة في | ||
+ | // AppRegistry.registerComponent() في index.js | ||
+ | mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null); | ||
+ | |||
+ | setContentView(mReactRootView); | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void invokeDefaultOnBackPressed() { | ||
+ | super.onBackPressed(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </syntaxhighlight> | ||
+ | '''ملاحظة:''' إذا كنت تستخدم حزمةَ بدايةٍ مساعدة (starter kit) للعمل مع React Native، فاستبدل السلسلة النصيّة <code>"HelloWorld"</code> بتلك الموجودة في ملفّ <code>index.js</code> الخاص بك (وهي المعامل الأول للتابع <code>AppRegistry.registerComponent()</code>). | ||
+ | |||
+ | نفذ العملية "Sync Project files with Gradle". | ||
+ | |||
+ | إذا كنت تستخدم Android Studio، فاستخدم <code>Alt + Enter</code> لإضافة جميع الاستيرادات المفقودة في صنف MyReactActivity. كن حريصًا على استخدام إعداد <code>BuildConfig</code> الخاص بحزمتك وليس الحزمة الموجودة في حزمة <code>facebook</code>. | ||
+ | |||
+ | ينبغي تعيين السمة <code>Theme</code> التي تخص <code>MyReactActivity</code> لتكون <code>Theme.AppCompat.Light.NoActionBar</code> لأن بعض مكونات واجهة مستخدم React Native تعتمد على هذه السمة. | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | <activity | ||
+ | android:name=".MyReactActivity" | ||
+ | android:label="@string/app_name" | ||
+ | android:theme="@style/Theme.AppCompat.Light.NoActionBar"> | ||
+ | </activity> | ||
+ | </syntaxhighlight> | ||
+ | '''ملاحظة:''' يمكن مشاركة <code>ReactInstanceManager</code> من طرف عدّة أنشطة أو أجزاء (fragments) متعددة. عليك إنشاء <code>ReactFragment</code> أو <code>ReactActivity</code> خاصّة بك ويجب أن يكونَ لديك حامل مفرد (singleton holder) يحمل <code>ReactInstanceManager</code>. عندما تحتاج <code>ReactInstanceManager</code> (على سبيل المثال، لتوصيل <code>ReactInstanceManager</code> بدورة حياة تلك الأنشطة أو الأجزاء) فاستخدم تلك التي يوفّرها المفرد. | ||
+ | |||
+ | بعد ذلك، نحتاج إلى تمرير بعض من دوال رد النداء لدورة حياة النشاط (activity lifecycle callbacks) إلى <code>ReactInstanceManager</code> و <code>ReactRootView</code>: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | @Override | ||
+ | protected void onPause() { | ||
+ | super.onPause(); | ||
+ | |||
+ | if (mReactInstanceManager != null) { | ||
+ | mReactInstanceManager.onHostPause(this); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | protected void onResume() { | ||
+ | super.onResume(); | ||
+ | |||
+ | if (mReactInstanceManager != null) { | ||
+ | mReactInstanceManager.onHostResume(this, this); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | protected void onDestroy() { | ||
+ | super.onDestroy(); | ||
+ | |||
+ | if (mReactInstanceManager != null) { | ||
+ | mReactInstanceManager.onHostDestroy(this); | ||
+ | } | ||
+ | if (mReactRootView != null) { | ||
+ | mReactRootView.unmountReactApplication(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | نحتاج كذلك إلى تمرير أحداث زر الرجوع إلى React Native: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | @Override | ||
+ | public void onBackPressed() { | ||
+ | if (mReactInstanceManager != null) { | ||
+ | mReactInstanceManager.onBackPressed(); | ||
+ | } else { | ||
+ | super.onBackPressed(); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | يسمح هذا للغة [[JavaScript]] بالتحكم في ما يحدث عندما يضغط المستخدم على زر رجوع الجهاز (لتنفيذ عملية التنقل [navigation] على سبيل المثال). عندما لا تُعالج JavaScript الضغط على زر الرجوع، سيُستدعى تابع <code>invokeDefaultOnBackPressed</code> الخاص بك. افتراضيًّا، يُنهي هذا نشاطَك (<code>Activity</code>) ببساطة. | ||
+ | |||
+ | وأخيرا، علينا ربط قائمة التطوير (dev menu). افتراضيًًا، تُفعَّل القائمة عبر هز الجهاز، ولكن هذا ليس مفيدًا في برامج المحاكاة. لذلك سنجعلها تظهر عندما تضغط على زر القائمة في الجهاز hardware menu button (استخدم <code>Ctrl + M</code> إذا كنت تستخدم محاكي Android Studio): | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | @Override | ||
+ | public boolean onKeyUp(int keyCode, KeyEvent event) { | ||
+ | if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) { | ||
+ | mReactInstanceManager.showDevOptionsDialog(); | ||
+ | return true; | ||
+ | } | ||
+ | return super.onKeyUp(keyCode, event); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | نشاطك جاهز الآن لتشغيل شيفرة JavaScript. | ||
+ | |||
+ | ====اختبار الدمج==== | ||
+ | انتهيت الآن من جميع الخطوات الأساسية لدمج React Native مع تطبيقك الحالي. سنبدأ الآن بتشغيل مُحزّم React Native لإنشاء حزمة <code>index.bundle</code> والخادم الذي سيعمل على المضيف المحلي <code>localhost</code>. | ||
+ | |||
+ | =====1. تشغيل المُحزّم===== | ||
+ | لتشغيل تطبيقك، عليك أولاً تشغيل خادم التطوير (development server) عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروع React Native الخاص بك: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | $ yarn start | ||
+ | </syntaxhighlight> | ||
+ | =====2. تشغيل التطبيق===== | ||
+ | يمكنك الآن بناء تطبيقك كأيّ تطبيق Android آخر وتشغيله كالمعتاد. | ||
+ | |||
+ | بمجرد الوصول إلى نشاطك القائم على React داخل التطبيق، من المفترض أن تُحمَّل شيفرة JavaScript من خادم التطوير مع عرض الشاشة التالية: | ||
+ | [[ملف:EmbeddedAppAndroid.png|مركز|لاإطار]] | ||
+ | |||
+ | ====إنشاء بناء إصدار (release build) في Android Studio==== | ||
+ | يمكنك استخدام Android Studio لإنشاء بناءات الإصدار أيضًا! كل ما عليك فعله هو إنشاء بناءات الإصدار من تطبيق Android الأصيل الموجود مسبقًا. هناك خطوة إضافية واحدة فقط، والتي يجب عليك القيام بها قبل كل بناء إصدار. تحتاج إلى تنفيذ ما يلي لإنشاء حزمة React Native، والتي ستُضمَّن مع تطبيق Android الأصيل الخاص بك: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | $ npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/com/your-company-name/app-package-name/src/main/assets/index.android.bundle --assets-dest android/com/your-company-name/app-package-name/src/main/res/ | ||
+ | </syntaxhighlight> | ||
+ | '''ملاحظة:''' لا تنس استبدال المسارات في الأمر بالمسارات الصحيحة وإنشاء مجلد الأصول (assets folder) إذا لم يكن موجودًا. | ||
+ | |||
+ | الآن أنشئ بناء إصدارٍ لتطبيقك الأصيل من داخل Android Studio كالمعتاد وسيكون كل شيء على ما يرام! | ||
+ | |||
+ | ====ماذا بعد؟==== | ||
+ | يمكنك الآن متابعة تطوير تطبيقك كالمعتاد. راجع توثيق [[ReactNative/debugging|التنقيح]] و<nowiki/>[[ReactNative/running on device|النشر]] لمعرفة المزيد حول العمل مع React Native. | ||
==الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Objective-C== | ==الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Objective-C== | ||
سطر 16: | سطر 354: | ||
# إضافة هذه المكوّنات كاعتماديّاتٍ باستخدام CocoaPods. | # إضافة هذه المكوّنات كاعتماديّاتٍ باستخدام CocoaPods. | ||
# كتابة مكونات React Native باستخدام JavaScript. | # كتابة مكونات React Native باستخدام JavaScript. | ||
− | # إضافة | + | # إضافة واجهة <code>RCTRootView</code> إلى تطبيق iOS الخاص بك. ستكون هذه الواجهة بمثابة الحاوية لمكون React Native الخاص بك. |
# بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل. | # بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل. | ||
# تحقّق من أن جانب React Native من التطبيق الخاص بك يعمل كما هو متوقع. | # تحقّق من أن جانب React Native من التطبيق الخاص بك يعمل كما هو متوقع. | ||
سطر 42: | سطر 380: | ||
ثبّت حزمتي <code>react</code> و<code>react-native</code>. افتح طرفيّةً (terminal) أو مَحثَّ أوامر (command prompt)، ثم انتقل إلى المجلّد الذي يحتوي على ملف <code>package.json</code> ونفّذ الأمر التالي: | ثبّت حزمتي <code>react</code> و<code>react-native</code>. افتح طرفيّةً (terminal) أو مَحثَّ أوامر (command prompt)، ثم انتقل إلى المجلّد الذي يحتوي على ملف <code>package.json</code> ونفّذ الأمر التالي: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
+ | // yarn | ||
yarn add react-native | yarn add react-native | ||
+ | |||
+ | // أو | ||
+ | |||
+ | |||
+ | // npm | ||
+ | npm install react-native | ||
</syntaxhighlight> | </syntaxhighlight> | ||
سيؤدي هذا إلى طباعة رسالةٍ مشابهة لما يلي (مرِّر لأعلى مُخرجات <code>yarn</code> لمشاهدتها): | سيؤدي هذا إلى طباعة رسالةٍ مشابهة لما يلي (مرِّر لأعلى مُخرجات <code>yarn</code> لمشاهدتها): | ||
سطر 49: | سطر 394: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | هذا يعني أنّنا بحاجة إلى تثبيت React كذلك | + | هذا يعني أنّنا بحاجة إلى تثبيت React كذلك: |
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
+ | //yarn | ||
yarn add react@version_printed_above | yarn add react@version_printed_above | ||
+ | |||
+ | //أو | ||
+ | //npm | ||
+ | npm install react@version_printed_above | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | ضع مكان version_printed_above الإصدار المذكور في رسالة التحذير التي ظهرت لك آنفًا ضمن "react@16.2.0". | |
+ | |||
+ | أنشأت عملية التثبيت الآن مجلدَ <code>/node_modules</code> جديد. يخزّن هذا المجلد جميع اعتماديات JavaScript المطلوبة لإنشاء مشروعك. | ||
أضف <code>node_modules/</code> إلى ملفّ <code>.gitignore</code> الخاص بك. | أضف <code>node_modules/</code> إلى ملفّ <code>.gitignore</code> الخاص بك. | ||
سطر 72: | سطر 424: | ||
====أدوات سطر الأوامر لبيئة Xcode==== | ====أدوات سطر الأوامر لبيئة Xcode==== | ||
لتثبيت أدوات سطر الأوامر (Command Line Tools). اختر "Preferences..." في قائمة Xcode. انتقل إلى لوحة Locations وثبّت الأدوات عن طريق تحديد أحدث إصدارٍ في قائمة Command Line Tools المنسدلة. | لتثبيت أدوات سطر الأوامر (Command Line Tools). اختر "Preferences..." في قائمة Xcode. انتقل إلى لوحة Locations وثبّت الأدوات عن طريق تحديد أحدث إصدارٍ في قائمة Command Line Tools المنسدلة. | ||
− | [[ملف:GettingStartedXcodeCommandLineTools.png|مركز|لاإطار]] | + | |
+ | [[ملف:GettingStartedXcodeCommandLineTools.png|مركز|لاإطار|بديل=|720x720بك]] | ||
====إعداد اعتماديات CocoaPods==== | ====إعداد اعتماديات CocoaPods==== | ||
قبل دمج React Native مع تطبيقك، ستحتاج إلى تحديد أجزاء إطار React Native التي تريد دمجها مع التطبيق. سنستخدم CocoaPods لتحديد أي من هذهِ المواصفات الفرعيّة (subspecs) سيعتمد عليها تطبيقك. | قبل دمج React Native مع تطبيقك، ستحتاج إلى تحديد أجزاء إطار React Native التي تريد دمجها مع التطبيق. سنستخدم CocoaPods لتحديد أي من هذهِ المواصفات الفرعيّة (subspecs) سيعتمد عليها تطبيقك. | ||
− | قائمة المواصفات الفرعية المدعومة متوفّرة في [https://github.com/facebook/react-native/blob/master/React.podspec الملفّ <code>/node_modules/react-native/React.podspec</code>]. وتُسمَّى عمومًا حسب وظيفتها. على سبيل المثال، ستحتاج دائمًا إلى المواصفة الفرعيّة <code>Core</code>. التي تحتوي على <code>AppRegistry</code> و <code>StyleSheet</code> و <code>View</code> وغيرها من المكتبات الرئيسية في إطار React Native. إذا كنت ترغب في إضافة مكتبة <code>Text</code> الخاصة بإطار React Native (لاستخدام عناصر <code><Text></code> على سبيل المثال)، فستحتاج إلى المواصفة الفرعيّة <code>RCTText</code>. إذا كنت ترغب باستخدام مكتبة Image (لعناصر <code><Image></code> مثلًا)، فستحتاج إلى المواصفة الفرعيّة <code>RCTImage</code>. | + | قائمة المواصفات الفرعية المدعومة متوفّرة في [https://github.com/facebook/react-native/blob/master/React.podspec الملفّ <code>/node_modules/react-native/React.podspec</code>]. وتُسمَّى عمومًا حسب وظيفتها. على سبيل المثال، ستحتاج دائمًا إلى المواصفة الفرعيّة <code>Core</code>. التي تحتوي على <code>AppRegistry</code> و <code>StyleSheet</code> و <code>View</code> وغيرها من المكتبات الرئيسية في إطار React Native. إذا كنت ترغب في إضافة مكتبة <code>Text</code> الخاصة بإطار React Native (لاستخدام عناصر <code>[[ReactNative/text|<Text>]]</code> على سبيل المثال)، فستحتاج إلى المواصفة الفرعيّة <code>RCTText</code>. إذا كنت ترغب باستخدام مكتبة Image (لعناصر <code><Image></code> مثلًا)، فستحتاج إلى المواصفة الفرعيّة <code>RCTImage</code>. |
يمكنك تحديد المواصفات الفرعية التي سيعتمد عليها تطبيقك في ملف <code>Podfile</code>. أسهل طريقة لإنشاء ملفّ <code>Podfile</code> هي تنفيذ الأمر <code>init</code> الخاص بأداة CocoaPods في المجلد الفرعي<code>/ios</code> الخاص بمشروعك: | يمكنك تحديد المواصفات الفرعية التي سيعتمد عليها تطبيقك في ملف <code>Podfile</code>. أسهل طريقة لإنشاء ملفّ <code>Podfile</code> هي تنفيذ الأمر <code>init</code> الخاص بأداة CocoaPods في المجلد الفرعي<code>/ios</code> الخاص بمشروعك: | ||
سطر 84: | سطر 437: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | سوف يحتوي ملف Podfile على إعداد نموذجيّ (boilerplate setup) يُمكنك تعديله حسب طريقة الدمج التي تريدها. يجب أن يبدو ملفّ Podfile مشابهًا لهذا في النهاية: | + | سوف يحتوي ملف <code>Podfile</code> على إعداد نموذجيّ (boilerplate setup) يُمكنك تعديله حسب طريقة الدمج التي تريدها. |
+ | |||
+ | '''ملاحظة''': تختلف نسخة <code>Podfile</code> بحسب نسخة react-native، راجع [https://react-native-community.github.io/upgrade-helper/ https://react-native-community.github.io/upgrade-helper] لمعرفة نسخة <code>Podfile</code> التي يجب استخدامها. | ||
+ | |||
+ | يجب أن يبدو ملفّ <code>Podfile</code> مشابهًا لهذا في النهاية: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
+ | |||
# The target name is most likely the name of your project. | # The target name is most likely the name of your project. | ||
# اسم الهدف سيكون في أغلب الظن اسمَ مشروعك | # اسم الهدف سيكون في أغلب الظن اسمَ مشروعك | ||
سطر 93: | سطر 451: | ||
# but if not, adjust the `:path` accordingly | # but if not, adjust the `:path` accordingly | ||
# مجلدُ الاعتماديات موجود على الأغلب في جذر مشروعك، إن لم يكن كذلك، فعدّل المسار | # مجلدُ الاعتماديات موجود على الأغلب في جذر مشروعك، إن لم يكن كذلك، فعدّل المسار | ||
− | pod 'React', :path => '../node_modules/react-native', : | + | pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector" |
− | + | pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec" | |
− | + | pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired" | |
− | + | pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety" | |
− | + | pod 'React', :path => '../node_modules/react-native/' | |
− | + | pod 'React-Core', :path => '../node_modules/react-native/' | |
− | + | pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules' | |
− | + | pod 'React-Core/DevSupport', :path => '../node_modules/react-native/' | |
− | + | pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS' | |
− | + | pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation' | |
− | + | pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob' | |
− | + | pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image' | |
− | pod ' | + | pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS' |
+ | pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network' | ||
+ | pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings' | ||
+ | pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text' | ||
+ | pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration' | ||
+ | pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/' | ||
+ | |||
+ | pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact' | ||
+ | pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi' | ||
+ | pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor' | ||
+ | pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector' | ||
+ | pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon" | ||
+ | pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon" | ||
+ | pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga' | ||
− | |||
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' | pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' | ||
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' | pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' | ||
سطر 115: | سطر 485: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | بعد إنشاء ملف Podfile الخاص بك، ستكون جاهزًا لتثبيت React Native. | + | بعد إنشاء ملف <code>Podfile</code> الخاص بك، ستكون جاهزًا لتثبيت React Native. |
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
$ pod install | $ pod install | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | سيكون | + | سيكون الخرج مشابها لما يلي: |
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
Analyzing dependencies | Analyzing dependencies | ||
Fetching podspec for `React` from `../node_modules/react-native` | Fetching podspec for `React` from `../node_modules/react-native` | ||
Downloading dependencies | Downloading dependencies | ||
− | Installing React (0. | + | Installing React (0.62.0) |
Generating Pods project | Generating Pods project | ||
Integrating client project | Integrating client project | ||
سطر 142: | سطر 512: | ||
أولاً، أنشئ ملفّ <code>index.js</code> فارغ في جذر مشروع React Native الخاص بك. | أولاً، أنشئ ملفّ <code>index.js</code> فارغ في جذر مشروع React Native الخاص بك. | ||
− | ملفّ <code>index.js</code> هو نقطة البداية لتطبيقات React Native، وهو ضروري دائمًا. يمكن أن يكون ملفًا صغيرًا يطلب (<code>require</code>) ملفًا آخر يمثّل جزءًا من مكوّن أو تطبيق React Native الخاص بك، أو يمكن أن يحتوي على كامل الشيفرة البرمجية اللازمة لذلك. في حالتنا، سنضع كامل الشيفرة في ملفّ <code>index.js</code>. | + | ملفّ <code>index.js</code> هو نقطة البداية لتطبيقات React Native، وهو ضروري دائمًا. يمكن أن يكون ملفًا صغيرًا يطلب (يستورد <code>require</code>) ملفًا آخر يمثّل جزءًا من مكوّن أو تطبيق React Native الخاص بك، أو يمكن أن يحتوي على كامل الشيفرة البرمجية اللازمة لذلك. في حالتنا، سنضع كامل الشيفرة في ملفّ <code>index.js</code>. |
======2. أضف شيفرة React Native الخاصة بك====== | ======2. أضف شيفرة React Native الخاصة بك====== | ||
سطر 149: | سطر 519: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
import React from 'react'; | import React from 'react'; | ||
− | import {AppRegistry, StyleSheet, Text, View} from 'react-native'; | + | import { |
+ | AppRegistry, | ||
+ | StyleSheet, | ||
+ | Text, | ||
+ | View | ||
+ | } from 'react-native'; | ||
− | + | const RNHighScores = ({ scores }) => { | |
− | + | const contents = scores.map((score) => ( | |
− | + | <Text key={score.name}> | |
− | + | {score.name}:{score.value} | |
− | + | {'\n'} | |
− | + | </Text> | |
+ | )); | ||
+ | return ( | ||
+ | <View style={styles.container}> | ||
+ | <Text style={styles.highScoresTitle}> | ||
+ | 2048 High Scores! | ||
</Text> | </Text> | ||
− | + | <Text style={styles.scores}>{contents}</Text> | |
− | + | </View> | |
− | + | ); | |
− | + | }; | |
− | |||
− | |||
− | |||
− | |||
− | } | ||
const styles = StyleSheet.create({ | const styles = StyleSheet.create({ | ||
سطر 173: | سطر 548: | ||
justifyContent: 'center', | justifyContent: 'center', | ||
alignItems: 'center', | alignItems: 'center', | ||
− | backgroundColor: '#FFFFFF' | + | backgroundColor: '#FFFFFF' |
}, | }, | ||
highScoresTitle: { | highScoresTitle: { | ||
fontSize: 20, | fontSize: 20, | ||
textAlign: 'center', | textAlign: 'center', | ||
− | margin: 10 | + | margin: 10 |
}, | }, | ||
scores: { | scores: { | ||
textAlign: 'center', | textAlign: 'center', | ||
color: '#333333', | color: '#333333', | ||
− | marginBottom: 5 | + | marginBottom: 5 |
− | } | + | } |
}); | }); | ||
− | + | // Module name | |
− | // | ||
AppRegistry.registerComponent('RNHighScores', () => RNHighScores); | AppRegistry.registerComponent('RNHighScores', () => RNHighScores); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | <code>RNHighScores</code> هو اسم وحدتك الذي سيُستخدَم عند إضافة | + | <code>RNHighScores</code> هو اسم وحدتك الذي سيُستخدَم عند إضافة واجهة إلى React Native من داخل تطبيق iOS الخاص بك. |
====الرابط العجيب: <code>RCTRootView</code>==== | ====الرابط العجيب: <code>RCTRootView</code>==== | ||
الآن، وبعد إنشاء مكون React Native الخاص بك عبر ملفّ <code>index.js</code>، تحتاج إلى إضافة هذا المكون إلى مُتحكّم <code>ViewController</code> جديد أو موجود مسبقا. أيسر سبيل إلى ذلك هو إنشاء مسار حدث (event path) اختياريًّا يُشير إلى المكون الخاص بك، ثم إضافة هذا المكون إلى <code>ViewController</code> موجود. | الآن، وبعد إنشاء مكون React Native الخاص بك عبر ملفّ <code>index.js</code>، تحتاج إلى إضافة هذا المكون إلى مُتحكّم <code>ViewController</code> جديد أو موجود مسبقا. أيسر سبيل إلى ذلك هو إنشاء مسار حدث (event path) اختياريًّا يُشير إلى المكون الخاص بك، ثم إضافة هذا المكون إلى <code>ViewController</code> موجود. | ||
− | سنربط مكوّن React Native الخاص بنا مع | + | سنربط مكوّن React Native الخاص بنا مع واجهة أصيلة جديد في متحكّم <code>ViewController</code>، والذي سيستضيف المكوّن. الواجهة الأصيلة تسمّى <code>RCTRootView</code>. |
======1. إنشاء مسار حدث (Event Path)====== | ======1. إنشاء مسار حدث (Event Path)====== | ||
سطر 203: | سطر 577: | ||
======2. معالج الأحداث====== | ======2. معالج الأحداث====== | ||
− | سنضيف الآن معالج أحداثٍ من رابط القائمة. سيُضاف تابع إلى متحكّم <code>ViewController</code> الرئيسي للتطبيق الخاص بك. هنا سنعتمد على | + | سنضيف الآن معالج أحداثٍ من رابط القائمة. سيُضاف تابع إلى متحكّم <code>ViewController</code> الرئيسي للتطبيق الخاص بك. هنا سنعتمد على واجهة <code>RCTRootView</code>. |
− | عند إنشاء تطبيق React Native، يُستخدَم | + | عند إنشاء تطبيق React Native، يُستخدَم المُجمّع [https://facebook.github.io/metro/ Metro] لإنشاء ملفّ <code>index.bundle</code> الذي سيُقدَّم من طرف خادم React Native. سيحتوي ملفّ <code>index.bundle</code> على وحدة <code>RNHighScore</code> الخاصة بنا. لذا، سنحتاج إلى توجيه واجهة <code>RCTRootView</code> الخاص بنا إلى مكان الملفّ <code>index.bundle</code> (عبر <code>NSURL</code>) وربطه بالوحدة. |
− | لتسهيل التنقيح (debugging)، سنُسجّل (log) أنّ معالج الأحداث قد استُدعَى. بعد ذلك، سننشئ سلسلة نصيّة تحتوي على مكان شيفرة React Native الموجودة داخل ملفّ <code>index.bundle</code>. أخيرًا، سننشئ | + | لتسهيل التنقيح (debugging)، سنُسجّل (log) أنّ معالج الأحداث قد استُدعَى. بعد ذلك، سننشئ سلسلة نصيّة تحتوي على مكان شيفرة React Native الموجودة داخل ملفّ <code>index.bundle</code>. أخيرًا، سننشئ الواجهة <code>RCTRootView</code> الرئيسيّة. لاحظ كيف نُشير إلى الاسم <code>RNHighScores</code> على أنّه اسم الوحدة (moduleName) الذي أنشأناه أعلاه عند كتابة شيفرة مكوّن React Native الخاص بنا. |
أولاً استورد ترويسة <code>RCTRootView</code>: | أولاً استورد ترويسة <code>RCTRootView</code>: | ||
سطر 243: | سطر 617: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | لاحظ أن المقطع <code>RCTRootView initWithURL</code> يبدأ بتشغيل آلة | + | لاحظ أن المقطع <code>RCTRootView initWithURL</code> يبدأ بتشغيل آلة افتراضية JSC جديدة (JSC VM). لتوفير الموارد وتبسيط الاتصال بين واجهات React Native في أجزاء مختلفة من التطبيق الأصلي، يُمكنك إنشاء عدّة واجهات مدعومة من React Native مرتبطة بوقت تشغيل JS واحد. للقيام بهذا، استخدم [https://github.com/facebook/react-native/blob/master/React/Base/RCTBridge.h#L93 <code>RCTBridge initWithBundleURL</code>] بدلاً من استخدام<code>[RCTRootView alloc] initWithURL</code> لإنشاء جسر ثم استخدم <code>RCTRootView initWithBridge</code>. |
+ | |||
+ | عند نقل تطبيقك إلى بيئة الإنتاج (production)، يمكن لعنوان NSURL الإشارة إلى ملفّ مُحزَّم مسبقًا (pre-bundled) على القرص عبر سطرٍ برمجيّ مثل:<syntaxhighlight lang="js"> | ||
+ | [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; | ||
+ | </syntaxhighlight>يمكنك استخدام برمجيّة <code>react-native-xcode.sh</code> الموجودة في المسار <code>node_modules/react-native/scripts/</code> لإنشاء هذا الملف المُحزّم مسبقًا. | ||
+ | |||
+ | ======3. الربط====== | ||
+ | اربط الرابط الجديد في القائمة الرئيسية بتابعِ معالجِ الأحداث المضاف حديثًا. | ||
+ | [[ملف:react-native-add-react-native-integration-wire-up.png|مركز|لاإطار|720x720بك]] | ||
+ | من أسهل الطرق للقيام بهذا هي فتح الواجهة في لوحة العمل (storyboard) والنقر بزر الفأرة الأيمن على الرابط الجديد. حدّد حدثًا مثل حدث <code>Touch Up Inside</code>، واسحبه إلى لوحة العمل ثم حدّد الواجهة المُنشَأ من القائمة المتوفرة. | ||
+ | |||
+ | ====اختبار الدمج==== | ||
+ | انتهيت الآن من جميع الخطوات الأساسية لدمج React Native مع تطبيقك الحالي. سنبدأ الآن بتشغيل المُجمّع [https://facebook.github.io/metro/ Metro] لإنشاء حزمة <code>index.bundle</code> والخادم الذي سيعمل على المضيف المحلي <code>localhost</code>. | ||
+ | |||
+ | =====1. إضافة استثناء App Transport Security===== | ||
+ | |||
+ | حظرت شركة Apple تحميل موارد نصوص HTTP الضمنيّة (implicit cleartext HTTP). لذلك نحتاج إلى إضافة ما يلي إلى ملف <code>Info.plist</code> الخاص بالتطبيق (أو ما يعادله). | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | <key>NSAppTransportSecurity</key> | ||
+ | <dict> | ||
+ | <key>NSExceptionDomains</key> | ||
+ | <dict> | ||
+ | <key>localhost</key> | ||
+ | <dict> | ||
+ | <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key> | ||
+ | <true/> | ||
+ | </dict> | ||
+ | </dict> | ||
+ | </dict> | ||
+ | </syntaxhighlight> | ||
+ | سياسة أمان App Transport Security مفيدة لمستخدمي التطبيق. تأكّد من إعادة تمكينها قبل إطلاق تطبيقك للإنتاج. | ||
+ | |||
+ | =====2. تشغيل المُحزّم===== | ||
+ | لتشغيل تطبيقك، عليك أولاً تشغيل خادم التطوير (development server) عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروع React Native الخاص بك: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | //npm | ||
+ | npm start | ||
+ | |||
+ | //yarn | ||
+ | yarn start | ||
+ | </syntaxhighlight> | ||
+ | =====3. تشغيل التطبيق===== | ||
+ | إذا كنت تستخدم Xcode أو محرّرك المفضّل، فأنشئ تطبيق iOS أصيل وشغّله كالمعتاد. أو يمكنك تشغيل التطبيق من سطر الأوامر عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروعك : | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | $ npx react-native run-ios | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | سترى الآن في تطبيقنا، رابطًا يشير إلى شاشة "High Scores"، وعند النقر فوقه، سترى مكوّن React Native الخاص بك. | ||
+ | |||
+ | هذه هي الشاشة الرئيسيّة للتطبيق الأصيل: | ||
+ | [[ملف:react-native-add-react-native-integration-example-home-scree.png|مركز|لاإطار]] | ||
+ | هذه شاشة مكوّن React Native: | ||
+ | [[ملف:react-native-add-react-native-integration-example-high-score.png|مركز|لاإطار]] | ||
+ | إذا واجهتك مشكلة في تقرير الوحدات (module resolution) عند تشغيل تطبيقك، فيرجى الاطلاع على [https://github.com/facebook/react-native/issues/4968 هذه الصفحة] للحصول على معلوماتٍ قد تساعدك على حل مشكلتك. يبدو [https://github.com/facebook/react-native/issues/4968#issuecomment-220941717 هذا التعليق] حلًّا ممكنًا. | ||
+ | |||
+ | ====ماذا بعد؟==== | ||
+ | يمكنك الآن متابعة تطوير تطبيقك كالمعتاد. راجع توثيق [[ReactNative/debugging|التنقيح]] و<nowiki/>[[ReactNative/running on device|النشر]] لمعرفة المزيد حول العمل مع React Native. | ||
+ | |||
+ | ==الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Swift== | ||
+ | === المفاهيم الرئيسية === | ||
+ | أهمّ الأمور لدمج مكونات React Native في تطبيق iOS الخاص بك هي: | ||
+ | |||
+ | # إعداد اعتماديات (dependencies) إطار العمل React Native وهيكل المجلّدات (directory structure). | ||
+ | # فهم مكونات React Native التي ستستخدمها في تطبيقك. | ||
+ | # إضافة هذه المكوّنات كاعتماديّاتٍ باستخدام CocoaPods. | ||
+ | # كتابة مكونات React Native باستخدام JavaScript. | ||
+ | # إضافة الواجهة <code>RCTRootView</code> إلى تطبيق iOS الخاص بك. ستكون هذه الواجهة بمثابة الحاوية لمكون React Native الخاص بك. | ||
+ | # بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل. | ||
+ | # تحقّق من أن جانب React Native من التطبيق الخاص بك يعمل كما هو متوقع. | ||
+ | |||
+ | ===المتطلبات الأساسية=== | ||
+ | اتبع إرشادات [[ReactNative/getting started|بناء مشاريعٍ بشيفرة أصيلة في الدرس التقديمي]] لتهيئة بيئة التطوير الخاصة بك لإنشاء تطبيقات React Native لنظام التشغيل iOS. | ||
+ | |||
+ | ====1. إعداد بنية المجلدات (directory structure)==== | ||
+ | لتسهيل العمل، أنشئ مجلدًا جديدًا لمشروع React Native المُدمَج الخاص بك، ثم انسخ مشروع iOS الحالي إلى مجلد فرعي باسم <code>/ios</code>. | ||
+ | |||
+ | ====2. تثبيت اعتماديات JavaScript==== | ||
+ | انتقل إلى المجلّد الجذر (root directory) للمشروع الخاص بك وأنشئ ملف <code>package.json</code> جديد بالمحتويات التالية: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | { | ||
+ | "name": "MyReactNativeApp", | ||
+ | "version": "0.0.1", | ||
+ | "private": true, | ||
+ | "scripts": { | ||
+ | "start": "yarn react-native start" | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | بعد ذلك، تأكّد من [https://yarnpkg.com/lang/en/docs/install تثبيت مدير الحزم Yarn]. | ||
+ | |||
+ | ثبّت حزمتي <code>react</code> و<code>react-native</code>. افتح طرفيّةً (terminal) أو مَحثَّ أوامر (command prompt)، ثم انتقل إلى المجلّد الذي يحتوي على ملف <code>package.json</code> ونفّذ الأمر التالي: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | $ yarn add react-native | ||
+ | </syntaxhighlight> | ||
+ | سيؤدي هذا إلى طباعة رسالةٍ مشابهة لما يلي (مرِّر لأعلى مُخرجات <code>yarn</code> لمشاهدتها): | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | warning "react-native@0.52.2" has unmet peer dependency "react@16.2.0". | ||
+ | </syntaxhighlight> | ||
− | + | هذا يعني أنّنا بحاجة إلى تثبيت React كذلك: | |
+ | <syntaxhighlight lang="javascript"> | ||
+ | $ yarn add react@version_printed_above | ||
+ | </syntaxhighlight> | ||
+ | ضع مكان version_printed_above الإصدار المذكور في رسالة التحذير التي ظهرت لك آنفًا ضمن "react@16.2.0". | ||
+ | |||
+ | أنشأَ Yarn مجلدَ <code>/node_modules</code> جديد. يخزّن هذا المجلد جميع اعتماديات JavaScript المطلوبة لإنشاء مشروعك. | ||
+ | |||
+ | أضف <code>node_modules/</code> إلى ملفّ <code>.gitignore</code> الخاص بك. | ||
− | ======الربط====== | + | ====3. تثبيت CocoaPods==== |
+ | [https://cocoapods.org/ CocoaPods] هي أداةٌ لإدارة الحزم لتطوير تطبيقات iOS و macOS. سنستخدمها لإضافة شيفرة إطار React Native الفعليّة محليًا إلى مشروعك الحالي. | ||
+ | |||
+ | نوصي بتثبيت CocoaPods باستخدام [https://brew.sh/ Homebrew]. | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | $ brew install cocoapods | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | '''ملاحظة:''' من الممكن تقنيًا الاستغناء عن أداة CocoaPods، ولكن هذا سيتطلب إضافة مكتباتٍ وروابط يدويًّا، ما سيُعقّد هذه العملية. | ||
+ | ===إضافة React Native إلى تطبيقك=== | ||
+ | لنفترض أن [https://github.com/JoelMarcey/swift-2048 التطبيق] الذي تُريد دمج مكونات جديدة معه هو لعبة [https://en.wikipedia.org/wiki/2048_%28video_game%29 2048]. إليك ما تبدو عليه القائمة الرئيسية للتطبيق الأصيل دون React Native. | ||
+ | [[ملف:react-native-existing-app-integration-ios-before.png|مركز|لاإطار]] | ||
+ | |||
+ | ====أدوات سطر الأوامر لبيئة Xcode==== | ||
+ | لتثبيت أدوات سطر الأوامر (Command Line Tools). اختر "Preferences..." في قائمة Xcode. انتقل إلى لوحة Locations وثبّت الأدوات عن طريق تحديد أحدث إصدارٍ في قائمة Command Line Tools المنسدلة. | ||
+ | [[ملف:GettingStartedXcodeCommandLineTools.png|مركز|لاإطار|720x720بك]] | ||
+ | |||
+ | ====إعداد اعتماديات CocoaPods==== | ||
+ | قبل دمج React Native مع تطبيقك، ستحتاج إلى تحديد أجزاء إطار React Native التي تريد دمجها مع التطبيق. سنستخدم CocoaPods لتحديد أي من هذهِ المواصفات الفرعيّة (subspecs) سيعتمد عليها تطبيقك. | ||
+ | |||
+ | قائمة المواصفات الفرعية المدعومة متوفّرة في [https://github.com/facebook/react-native/blob/master/React.podspec الملفّ <code>/node_modules/react-native/React.podspec</code>]. وتُسمَّى عمومًا حسب وظيفتها. على سبيل المثال، ستحتاج دائمًا إلى المواصفة الفرعيّة <code>Core</code>. التي تحتوي على <code>AppRegistry</code> و <code>StyleSheet</code> و <code>View</code> وغيرها من المكتبات الرئيسية في إطار React Native. إذا كنت ترغب في إضافة مكتبة <code>Text</code> الخاصة بإطار React Native (لاستخدام عناصر <code>[[ReactNative/text|<Text>]]</code> على سبيل المثال)، فستحتاج إلى المواصفة الفرعيّة <code>RCTText</code>. إذا كنت ترغب باستخدام مكتبة <code>Image</code> (لعناصر <code>[[ReactNative/image|<Image>]]</code> مثلًا)، فستحتاج إلى المواصفة الفرعيّة <code>RCTImage</code>. | ||
+ | |||
+ | يمكنك تحديد المواصفات الفرعية التي سيعتمد عليها تطبيقك في ملف <code>Podfile</code>. أسهل طريقة لإنشاء ملفّ <code>Podfile</code> هي تنفيذ الأمر <code>init</code> الخاص بأداة CocoaPods في المجلد الفرعي<code>/ios</code> الخاص بمشروعك: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | $ pod init | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | سوف يحتوي ملف <code>Podfile</code> على إعداد نموذجيّ (boilerplate setup) يُمكنك تعديله حسب طريقة الدمج التي تريدها. | ||
+ | |||
+ | '''ملاحظة''': تختلف نسخة <code>Podfile</code> بحسب نسخة react-native، راجع [https://react-native-community.github.io/upgrade-helper/ https://react-native-community.github.io/upgrade-helper] لمعرفة نسخة <code>Podfile</code> التي يجب استخدامها. | ||
+ | |||
+ | يجب أن يبدو ملفّ <code>Podfile</code> مشابهًا لهذا في النهاية: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | source 'https://github.com/CocoaPods/Specs.git' | ||
+ | |||
+ | # مطلوب للتطبيقات المكتوبة بلغة | ||
+ | # Swift | ||
+ | |||
+ | platform :ios, '8.0' | ||
+ | use_frameworks! | ||
+ | |||
+ | # The target name is most likely the name of your project. | ||
+ | # اسم الهدف سيكون في أغلب الظن اسمَ مشروعك | ||
+ | target 'swift-2048' do | ||
+ | |||
+ | # Your 'node_modules' directory is probably in the root of your project, | ||
+ | # but if not, adjust the `:path` accordingly | ||
+ | # مجلدُ الاعتماديات موجود على الأغلب في جذر مشروعك، إن لم يكن كذلك، فعدّل المسار | ||
+ | pod 'React', :path => '../node_modules/react-native', :subspecs => [ | ||
+ | 'Core', | ||
+ | 'CxxBridge', # Include this for RN >= 0.47 | ||
+ | 'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43 | ||
+ | 'RCTText', | ||
+ | 'RCTNetwork', | ||
+ | 'RCTWebSocket', # needed for debugging | ||
+ | # Add any other subspecs you want to use in your project | ||
+ | # أضف أي مواصفات فرعية أخرى حسب ما تريد الاعتماد عليه في مشروعك | ||
+ | ] | ||
+ | # Explicitly include Yoga if you are using RN >= 0.42.0 | ||
+ | pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga" | ||
+ | |||
+ | # Third party deps podspec link | ||
+ | pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec' | ||
+ | pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec' | ||
+ | pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' | ||
+ | |||
+ | end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | بعد إنشاء ملف <code>Podfile</code> الخاص بك، ستكون جاهزًا لتثبيت React Native. | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | $ pod install | ||
+ | </syntaxhighlight> | ||
+ | سيكون الخرج مشابها لما يلي: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | Analyzing dependencies | ||
+ | Fetching podspec for `React` from `../node_modules/react-native` | ||
+ | Downloading dependencies | ||
+ | Installing React (0.62.0) | ||
+ | Generating Pods project | ||
+ | Integrating client project | ||
+ | Sending stats | ||
+ | Pod installation complete! There are 3 dependencies from the Podfile and 1 total pod installed. | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | إذا فشل هذا مع وجود أخطاء تحتوي على xcrun، فتأكّد من تعيين أدوات Command Line Tools في Xcode عبر قائمة Preferences ثمّ Locations. | ||
+ | |||
+ | إذا حصلت على تنبيه مشابه للنصّ التالي:<syntaxhighlight lang="js"> | ||
+ | "The swift-2048 [Debug] target overrides the FRAMEWORK_SEARCH_PATHS build setting defined in Pods/Target Support Files/Pods-swift-2048/Pods-swift-2048.debug.xcconfig. This can lead to problems with the CocoaPods installation" | ||
+ | </syntaxhighlight>فتأكّد من أن مسارات البحث عن أطر العمل <code>Framework Search Paths</code> في إعدادات البناء <code>Build Settings</code> لكل من <code>Debug</code> و<code>Release</code> تحتوي فقط على <code>$(inherited)</code>. | ||
+ | |||
+ | ====دمج الشيفرة==== | ||
+ | سنُعدّل الآن تطبيق iOS الأصيل لدمج React Native معه. سنضيف شاشة "High Score" لتطبيق 2048 الذي بين أيدينا باستخدام React Native. | ||
+ | |||
+ | =====مكون React Native===== | ||
+ | الجزء الأول من الشيفرة التي سنكتبها هي شيفرة React Native لشاشة "High Score" الجديدة التي سندمجها في تطبيقنا. | ||
+ | |||
+ | ======1. إنشاء ملف <code>index.js</code>====== | ||
+ | أولاً، أنشئ ملفّ <code>index.js</code> فارغ في جذر مشروع React Native الخاص بك. | ||
+ | |||
+ | ملفّ <code>index.js</code> هو نقطة البداية لتطبيقات React Native، وهو ضروري دائمًا. يمكن أن يكون ملفًا صغيرًا يطلب (<code>require</code>) ملفًا آخر يمثّل جزءًا من مكوّن أو تطبيق React Native الخاص بك، أو يمكن أن يحتوي على كامل الشيفرة البرمجية اللازمة لذلك. في حالتنا، سنضع كامل الشيفرة في ملفّ <code>index.js</code>. | ||
+ | |||
+ | ======2. أضف شيفرة React Native الخاصة بك====== | ||
+ | |||
+ | أنشئ المكوّن في ملفّ <code>index.js</code> الخاص بك. في نموذجنا هنا، سنضيف مكون <code><Text></code> بسيط داخل مكوّن <code><View></code> مُنسَّق: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | import React from 'react'; | ||
+ | import { | ||
+ | AppRegistry, | ||
+ | StyleSheet, | ||
+ | Text, | ||
+ | View | ||
+ | } from 'react-native'; | ||
+ | |||
+ | const RNHighScores = ({ scores }) => { | ||
+ | const contents = scores.map((score) => ( | ||
+ | <Text key={score.name}> | ||
+ | {score.name}:{score.value} | ||
+ | {'\n'} | ||
+ | </Text> | ||
+ | )); | ||
+ | return ( | ||
+ | <View style={styles.container}> | ||
+ | <Text style={styles.highScoresTitle}> | ||
+ | 2048 High Scores! | ||
+ | </Text> | ||
+ | <Text style={styles.scores}>{contents}</Text> | ||
+ | </View> | ||
+ | ); | ||
+ | }; | ||
+ | |||
+ | const styles = StyleSheet.create({ | ||
+ | container: { | ||
+ | flex: 1, | ||
+ | justifyContent: 'center', | ||
+ | alignItems: 'center', | ||
+ | backgroundColor: '#FFFFFF' | ||
+ | }, | ||
+ | highScoresTitle: { | ||
+ | fontSize: 20, | ||
+ | textAlign: 'center', | ||
+ | margin: 10 | ||
+ | }, | ||
+ | scores: { | ||
+ | textAlign: 'center', | ||
+ | color: '#333333', | ||
+ | marginBottom: 5 | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | // Module name | ||
+ | AppRegistry.registerComponent('RNHighScores', () => RNHighScores); | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | <code>RNHighScores</code> هو اسم وحدتك الذي سيُستخدَم عند إضافة واجهة إلى React Native من داخل تطبيق iOS الخاص بك. | ||
+ | |||
+ | ====الرابط العجيب: <code>RCTRootView</code>==== | ||
+ | الآن، وبعد إنشاء مكون React Native الخاص بك عبر ملفّ <code>index.js</code>، تحتاج إلى إضافة هذا المكون إلى مُتحكّم <code>ViewController</code> جديد أو موجود مسبقا. أيسر سبيل إلى ذلك هو إنشاء مسار حدث (event path) اختياريًّا يُشير إلى المكون الخاص بك، ثم إضافة هذا المكون إلى <code>ViewController</code> موجود. | ||
+ | |||
+ | سنربط مكوّن React Native الخاص بنا مع واجهة أصيلة جديد في متحكّم <code>ViewController</code>، والذي سيستضيف المكوّن. الواجهة الأصيلة تسمّى <code>RCTRootView</code>. | ||
+ | |||
+ | ======1. إنشاء مسار حدث (Event Path)====== | ||
+ | يمكنك إضافة رابط جديد في قائمة اللعبة الرئيسية للانتقال إلى صفحة "High Score" المبنيّة بإطار React Native. | ||
+ | [[ملف:react-native-add-react-native-integration-link.png|مركز|لاإطار]] | ||
+ | |||
+ | ======2. معالج الأحداث====== | ||
+ | سنضيف الآن معالج أحداثٍ من رابط القائمة. سيُضاف تابع إلى متحكّم <code>ViewController</code> الرئيسي للتطبيق الخاص بك. هنا سنعتمد على واجهة <code>RCTRootView</code>. | ||
+ | |||
+ | عند إنشاء تطبيق React Native، يُستخدَم المجمّع [https://facebook.github.io/metro/ Metro] لإنشاء ملفّ <code>index.bundle</code> الذي سيُقدَّم من طرف خادم React Native. سيحتوي ملفّ <code>index.bundle</code> على وحدة <code>RNHighScore</code> الخاصة بنا. لذا، سنحتاج إلى توجيه الواجهة <code>RCTRootView</code> الخاص بنا إلى مكان الملفّ <code>index.bundle</code> (عبر <code>NSURL</code>) وربطه بالوحدة. | ||
+ | |||
+ | لتسهيل التنقيح (debugging)، سنُسجّل (log) أنّ معالج الأحداث قد استُدعَى. بعد ذلك، سننشئ سلسلة نصيّة تحتوي على مكان شيفرة React Native الموجودة داخل ملفّ <code>index.bundle</code>. أخيرًا، سننشئ الواجهة <code>RCTRootView</code> الرئيسيّ. لاحظ كيف نُشير إلى الاسم <code>RNHighScores</code> على أنّه اسم الوحدة <code>moduleName</code> الذي أنشأناه أعلاه عند كتابة شيفرة مكوّن React Native الخاص بنا. | ||
+ | |||
+ | أولاً استورد (عبر <code>import</code>) مكتبة <code>React</code>: | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | import React | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | الخاصيّات الأوليّة<code>initialProperties</code> موجودة هنا لأغراضٍ توضيحيّة، ولتكون لدينا بعض البيانات لعرضها في شاشة "High Score". في مكوّن React Native الخاص بنا، سنستخدم <code>this.props</code> للوصول إلى هذه البيانات. | ||
+ | <syntaxhighlight lang="javascript"> | ||
+ | @IBAction func highScoreButtonTapped(sender : UIButton) { | ||
+ | NSLog("Hello") | ||
+ | let jsCodeLocation = URL(string: "http://localhost:8081/index.bundle?platform=ios") | ||
+ | let mockData:NSDictionary = ["scores": | ||
+ | [ | ||
+ | ["name":"Alex", "value":"42"], | ||
+ | ["name":"Joel", "value":"10"] | ||
+ | ] | ||
+ | ] | ||
+ | |||
+ | let rootView = RCTRootView( | ||
+ | bundleURL: jsCodeLocation, | ||
+ | moduleName: "RNHighScores", | ||
+ | initialProperties: mockData as [NSObject : AnyObject], | ||
+ | launchOptions: nil | ||
+ | ) | ||
+ | let vc = UIViewController() | ||
+ | vc.view = rootView | ||
+ | self.present(vc, animated: true, completion: nil) | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | لاحظ أن المقطع <code>RCTRootView initWithURL</code> يبدأ بتشغيل آلة افتراضية JSC جديدة (JSC VM). لتوفير الموارد وتبسيط الاتصال بين واجهات React Native في أجزاء مختلفة من التطبيق الأصلي، يُمكنك إنشاء عدّة واجهات مدعومة من React Native مرتبطة بوقت تشغيل JS واحد. للقيام بهذا، استخدم [https://github.com/facebook/react-native/blob/master/React/Base/RCTBridge.h#L89 <code>RCTBridge initWithBundleURL</code>] بدلاً من ستخدام <code>RCTRootView bundleURL</code> لإنشاء جسر ثم استخدم <code>RCTRootView initWithBridge</code>. | ||
+ | |||
+ | عند نقل تطبيقك إلى بيئة الإنتاج (production)، يمكن لعنوان <code>NSURL</code> الإشارة إلى ملفّ مُحزَّم مسبقًا (pre-bundled) على القرص عبر سطرٍ برمجيّ مثل:<syntaxhighlight lang="js"> | ||
+ | let mainBundle = NSBundle(URLForResource: "main" withExtension:"jsbundle") | ||
+ | </syntaxhighlight>يمكنك استخدام برمجيّة <code>react-native-xcode.sh</code> الموجودة في المسار <code>node_modules/react-native/scripts/</code> لإنشاء هذا الملف المُحزّم مسبقًا. | ||
+ | |||
+ | ======3. الربط====== | ||
اربط الرابط الجديد في القائمة الرئيسية بتابعِ معالجِ الأحداث المضاف حديثًا. | اربط الرابط الجديد في القائمة الرئيسية بتابعِ معالجِ الأحداث المضاف حديثًا. | ||
− | [[ملف:react-native-add-react-native-integration-wire-up.png|مركز|لاإطار]] | + | [[ملف:react-native-add-react-native-integration-wire-up.png|مركز|لاإطار|720x720بك]] |
− | من أسهل الطرق للقيام بهذا هي فتح | + | من أسهل الطرق للقيام بهذا هي فتح الواجهة في لوحة العمل (storyboard) والنقر بزر الفأرة الأيمن على الرابط الجديد. حدّد حدثًا مثل حدث <code>Touch Up Inside</code>، واسحبه إلى لوحة العمل ثم حدّد الواجهة المُنشَأة من القائمة المتوفرة. |
====اختبار الدمج==== | ====اختبار الدمج==== | ||
سطر 273: | سطر 959: | ||
سياسة أمان App Transport Security مفيدة لمستخدمي التطبيق. تأكّد من إعادة تمكينها قبل إطلاق تطبيقك للإنتاج. | سياسة أمان App Transport Security مفيدة لمستخدمي التطبيق. تأكّد من إعادة تمكينها قبل إطلاق تطبيقك للإنتاج. | ||
− | =====2. تشغيل | + | =====2. تشغيل المُحزّم===== |
لتشغيل تطبيقك، عليك أولاً تشغيل خادم التطوير (development server) عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروع React Native الخاص بك: | لتشغيل تطبيقك، عليك أولاً تشغيل خادم التطوير (development server) عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروع React Native الخاص بك: | ||
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
سطر 279: | سطر 965: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=====3. تشغيل التطبيق===== | =====3. تشغيل التطبيق===== | ||
− | إذا كنت تستخدم Xcode أو محرّرك المفضّل، فأنشئ تطبيق iOS أصيل وشغّله كالمعتاد. أو يمكنك تشغيل التطبيق من سطر الأوامر عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروعك : | + | إذا كنت تستخدم Xcode أو محرّرك المفضّل، فأنشئ تطبيق iOS أصيل وشغّله كالمعتاد. أو يمكنك تشغيل التطبيق من سطر الأوامر عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروعك: |
<syntaxhighlight lang="javascript"> | <syntaxhighlight lang="javascript"> | ||
− | $ react-native run-ios | + | $ npx react-native run-ios |
</syntaxhighlight> | </syntaxhighlight> | ||
سطر 291: | سطر 977: | ||
[[ملف:react-native-add-react-native-integration-example-high-score.png|مركز|لاإطار]] | [[ملف:react-native-add-react-native-integration-example-high-score.png|مركز|لاإطار]] | ||
إذا واجهتك مشكلة في تقرير الوحدات (module resolution) عند تشغيل تطبيقك، فيرجى الاطلاع على [https://github.com/facebook/react-native/issues/4968 هذه الصفحة] للحصول على معلوماتٍ قد تساعدك على حل مشكلتك. يبدو [https://github.com/facebook/react-native/issues/4968#issuecomment-220941717 هذا التعليق] حلًّا ممكنًا. | إذا واجهتك مشكلة في تقرير الوحدات (module resolution) عند تشغيل تطبيقك، فيرجى الاطلاع على [https://github.com/facebook/react-native/issues/4968 هذه الصفحة] للحصول على معلوماتٍ قد تساعدك على حل مشكلتك. يبدو [https://github.com/facebook/react-native/issues/4968#issuecomment-220941717 هذا التعليق] حلًّا ممكنًا. | ||
− | |||
− | |||
− | |||
====ماذا بعد؟==== | ====ماذا بعد؟==== | ||
يمكنك الآن متابعة تطوير تطبيقك كالمعتاد. راجع توثيق [[ReactNative/debugging|التنقيح]] و<nowiki/>[[ReactNative/running on device|النشر]] لمعرفة المزيد حول العمل مع React Native. | يمكنك الآن متابعة تطوير تطبيقك كالمعتاد. راجع توثيق [[ReactNative/debugging|التنقيح]] و<nowiki/>[[ReactNative/running on device|النشر]] لمعرفة المزيد حول العمل مع React Native. | ||
− | |||
− | |||
== مصادر == | == مصادر == | ||
− | * [https:// | + | * [https://reactnative.dev/docs/integration-with-existing-apps صفحة Integration with Existing Apps في توثيق React Native الرسمي.] |
[[تصنيف:ReactNative]] | [[تصنيف:ReactNative]] | ||
+ | [[تصنيف:React Native Docs]] |
المراجعة الحالية بتاريخ 13:36، 9 أكتوبر 2021
إطار العمل React Native رائعٌ لبرمجة تطبيق جوال جديد من البداية. لكن، يُمكنك كذلك استخدامه لإضافة واجهة (view) واحدة أو ميّزة إلى التطبيقات الأصيلة الموجودة بالفعل. يمكنك باتّباع بضعة خطواتٍ إضافةُ ميّزات جديدة باستخدام React Native إلى تطبيقك، مثل شاشات (screens) جديدة، وأقسام واجهات، ...إلخ.
تختلف الخطوات حسب النظام الأساسي الذي تستهدفه:
- الدمج مع تطبيقات Android قائمة مكتوبة بلغة Java
- الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Objective-C
- الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Swift
الدمج مع تطبيقات Android قائمة مكتوبة بلغة Java
المفاهيم الرئيسية
أهمّ الأمور لدمج مكونات React Native في تطبيق Android الخاص بك هي:
- إعداد اعتماديات (dependencies) إطار العمل React Native وهيكل المجلّدات (directory structure).
- كتابة مكونات React Native باستخدام JavaScript.
- إضافة واجهة
ReactRootView
إلى تطبيق Android الخاص بك. ستكون هذه الواجهة بمثابة الحاوية لمكون React Native الخاص بك. - بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل.
- تحقّق من أن جانب React Native من التطبيق الخاص بك يعمل كما هو متوقع.
المتطلبات الأساسية
اتبع إرشادات بناء مشاريعٍ بشيفرة أصيلة في الدرس التقديمي لتهيئة بيئة التطوير الخاصة بك لإنشاء تطبيقات React Native لنظام التشغيل Android.
1. إعداد بنية المجلدات (directory structure)
لتسهيل العمل، أنشئ مجلدًا جديدًا لمشروع React Native المُدمَج الخاص بك، ثم انسخ مشروع Android الحالي إلى مجلد فرعي باسم /android
.
2. تثبيت اعتماديات JavaScript
انتقل إلى المجلّد الجذر (root directory) للمشروع الخاص بك وأنشئ ملف package.json
جديد بالمحتويات التالية:
{
"name": "MyReactNativeApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "yarn react-native start"
}
}
بعد ذلك، تأكّد من تثبيت مدير الحزم Yarn.
ثبّت حزمتي react
وreact-native
. افتح طرفيّةً (terminal) أو مَحثَّ أوامر (command prompt)، ثم انتقل إلى المجلّد الذي يحتوي على ملف package.json
ونفّذ الأمر التالي:
$ yarn add react-native
سيؤدي هذا إلى طباعة رسالةٍ مشابهة لما يلي (مرِّر لأعلى مُخرجات yarn
لمشاهدتها):
warning "react-native@0.52.2" has unmet peer dependency "react@16.2.0".
هذا يعني أنّنا بحاجة إلى تثبيت React كذلك:
$ yarn add react@version_printed_above
ضع مكان version_printed_above الإصدار المذكور في رسالة التحذير التي ظهرت لك آنفًا ضمن "react@16.2.0".
أنشأَ Yarn مجلدَ /node_modules
جديد. يخزّن هذا المجلد جميع اعتماديات JavaScript المطلوبة لإنشاء مشروعك.
أضف node_modules/
إلى ملفّ .gitignore
الخاص بك.
إضافة React Native إلى تطبيقك
إعداد maven
أضف اعتماديّة React Native إلى ملفّ build.gradle
الخاص بتطبيقك:
dependencies {
implementation "com.android.support:appcompat-v7:27.1.1"
...
implementation "com.facebook.react:react-native:+" // From node_modules
implementation "org.webkit:android-jsc:+"
}
ملاحظة: إذا أردت التأكد من استعمال نسخة معينة من React Native على الدوام في بناء تطبيقك الأصيل، فاستبدل +
بنسخة React Native التي نزّلتها من npm
.
أضف خانةً إلى مجلّد maven الخاص بإطار React Native إلى ملفّ build.gradle
. تأكد من إضافته إلى كتلة "allprojects"، فوق مستودعات maven الأخرى:
allprojects {
repositories {
maven {
// All of React Native (JS, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
...
}
...
}
ملاحظة: تأكّد من أن المسار صحيح! لا ينبغي أن تواجهك أية أخطاء من قبيل Failed to resolve: com.facebook.react:react-native:0.x.x بعد تشغيل مزامنة Gradle في Android Studio.
تفعيل الربط التلقائي للوحدات الأصيلة
للاستفادة من قوة الربط التلقائي autolinking يجب أن نطبقه في عدة مواضع، أولًا أضف المدخل التالي إلى settings.gradle
:
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
ثم أضف المدخل التالي إلى أسفل app/build.gradle
:
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
إعداد الأذونات (permissions)
بعد ذلك، تأكد من تفعيل إذن الإنترنت في ملفّ AndroidManifest.xml
:
<uses-permission android:name="android.permission.INTERNET" />
إذا كنت بحاجة إلى الوصول إلى نشاط DevSettingsActivity
، فأضف السطر التالي إلى AndroidManifest.xml
:
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
يُستخدَم هذا فقط في وضع التطوير (dev mode) عند إعادة تحميل JavaScript من خادم التطوير، لذا يمكنك إزالته في بناءات الإصدار (release builds) إذا كنت بحاجة إلى ذلك.
مرور النصوص الواضحة (مستويات الواجهة البرمجية الأعلى من 28)
بدءًا من Android 9 (مستوى API 28)، يُعطَّل مرور النصوص الواضحة (cleartext traffic) بشكل افتراضي؛ يمنع هذا تطبيقك من الاتصال بالمجمّع Metro. التغييرات أدناه تسمح بمرور النصوص الواضحة في بناءات التنقيح (debug builds).
أضف خيار usesCleartextTraffic
إلى Debug AndroidManifest.xml
الخاص بك:
<!-- ... -->
<application
android:usesCleartextTraffic="true" tools:targetApi="28" >
<!-- ... -->
</application>
<!-- ... -->
هذا غير مطلوب لبناءات الإصدار (Release builds).
لمعرفة المزيد حول ضبط أمان الشبكة (Network Security Config) وسياسة مرور النصوص الواضحة (cleartext traffic policy)، راجع هذا الرابط.
دمج الشيفرة
سنُعدّل الآن تطبيق Android الأصيل لدمج React Native معه.
مكون React Native
الجزء الأول من الشيفرة التي سنكتبها هي شيفرة React Native لشاشة "Hello World" الجديدة التي سندمجها في تطبيقنا.
1. إنشاء ملف index.js
أولاً، أنشئ ملفّ index.js
فارغ في جذر مشروع React Native الخاص بك.
ملفّ index.js
هو نقطة البداية لتطبيقات React Native، وهو ضروري دائمًا. يمكن أن يكون ملفًا صغيرًا يطلب (يستورد require
) ملفًا آخر يمثّل جزءًا من مكوّن أو تطبيق React Native الخاص بك، أو يمكن أن يحتوي على كامل الشيفرة البرمجية اللازمة لذلك. في حالتنا، سنضع كامل الشيفرة في ملفّ index.js
.
2. أضف شيفرة React Native الخاصة بك
أنشئ المكوّن في ملفّ index.js
الخاص بك. في نموذجنا هنا، سنضيف مكون <Text>
بسيط داخل مكوّن <View>
مُنسَّق:
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
const HelloWorld = () => {
return (
<View style={styles.container}>
<Text style={styles.hello}>Hello, World</Text>
</View>
);
};
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10
}
});
AppRegistry.registerComponent(
'MyReactNativeApp',
() => HelloWorld
);
3. ضبط الأذونات لشاشة رسائل أخطاء التطوير
إذا كان تطبيقك يستهدف واجهة Android البرمجيّة من المستوى 23 أو أكثر، فتأكد من تمكين إذن android.permission.SYSTEM_ALERT_WINDOW
لبناء التطوير (development build). يمكنك التحقق من ذلك باستخدام Settings.canDrawOverlays(this);
. هذا مطلوب في بناءات التطوير لأنّه من الواجب عرض أخطاء تطوير React Native فوق كل النوافذ الأخرى. نظرًا لنظام الأذونات الجديد الذي قُدِّم في المستوى 23 من الواجهة البرمجيّة (Android M)، فلا بدّ للمستخدم أن يُوافق على عرض شاشة فوق جميع الشاشات الأخرى. يمكن تفعيل الإذن عن طريق إضافة الشيفرة التالية إلى النشاط (Activity) في التابع onCreate()
.
private final int OVERLAY_PERMISSION_REQ_CODE = 1; // اختر أي قيمة
...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
}
}
أخيرًا، يجب إعادة تعريف (override) التابع onActivityResult()
(كما هو موضح في الشيفرة أدناه) للتعامل مع حالتي قبول الإذن أو رفضه لتجربة مستخدم متجانسة. إضافةً إلى ذلك، لدمج الوحدات النمطية الأصيلة التي تستخدم startActivityForResult
، نحتاج إلى تمرير النتيجة إلى التابع onActivityResult
الخاص بنسخة ReactInstanceManager
الخاصة بنا.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
// SYSTEM_ALERT_WINDOW إذن غير معطى
}
}
}
mReactInstanceManager.onActivityResult( this, requestCode, resultCode, data );
}
الرابط العجيب: ReactRootView
لنُضِف شيفرةً أصيلة لبدء مشغل React Native الآني (React Native runtime) ولنجعَله يُصيِّر مكوّن JavaScript الخاص بنا. للقيام بهذا، سنُنشئ نشاطًا Activity
ليُنشئ واجهة ReactRootView
، ويبدأ تطبيق React بداخله مع ضبطه كواجهة رئيسيّة للمحتوى.
ملاحظة: إذا كنت تستهدف نسخ Android الأقدم من النسخة 5، فاستعمل صنف AppCompatActivity
من حزمة com.android.support:appcompat
عوضًا عن Activity
.
public class MyReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SoLoader.init(this, false);
mReactRootView = new ReactRootView(this);
List<ReactPackage> packages = new PackageList(getApplication()).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
// Remember to include them in `settings.gradle` and `app/build.gradle` too.
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setCurrentActivity(this)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackages(packages)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
// السلسلة النصيّة ("MyReactNativeApp")
// يجب أن تُطابق السلسلة النصيّة الموجودة في
// AppRegistry.registerComponent() في index.js
mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null);
setContentView(mReactRootView);
}
@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}
}
ملاحظة: إذا كنت تستخدم حزمةَ بدايةٍ مساعدة (starter kit) للعمل مع React Native، فاستبدل السلسلة النصيّة "HelloWorld"
بتلك الموجودة في ملفّ index.js
الخاص بك (وهي المعامل الأول للتابع AppRegistry.registerComponent()
).
نفذ العملية "Sync Project files with Gradle".
إذا كنت تستخدم Android Studio، فاستخدم Alt + Enter
لإضافة جميع الاستيرادات المفقودة في صنف MyReactActivity. كن حريصًا على استخدام إعداد BuildConfig
الخاص بحزمتك وليس الحزمة الموجودة في حزمة facebook
.
ينبغي تعيين السمة Theme
التي تخص MyReactActivity
لتكون Theme.AppCompat.Light.NoActionBar
لأن بعض مكونات واجهة مستخدم React Native تعتمد على هذه السمة.
<activity
android:name=".MyReactActivity"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
</activity>
ملاحظة: يمكن مشاركة ReactInstanceManager
من طرف عدّة أنشطة أو أجزاء (fragments) متعددة. عليك إنشاء ReactFragment
أو ReactActivity
خاصّة بك ويجب أن يكونَ لديك حامل مفرد (singleton holder) يحمل ReactInstanceManager
. عندما تحتاج ReactInstanceManager
(على سبيل المثال، لتوصيل ReactInstanceManager
بدورة حياة تلك الأنشطة أو الأجزاء) فاستخدم تلك التي يوفّرها المفرد.
بعد ذلك، نحتاج إلى تمرير بعض من دوال رد النداء لدورة حياة النشاط (activity lifecycle callbacks) إلى ReactInstanceManager
و ReactRootView
:
@Override
protected void onPause() {
super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause(this);
}
}
@Override
protected void onResume() {
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostDestroy(this);
}
if (mReactRootView != null) {
mReactRootView.unmountReactApplication();
}
}
نحتاج كذلك إلى تمرير أحداث زر الرجوع إلى React Native:
@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}
يسمح هذا للغة JavaScript بالتحكم في ما يحدث عندما يضغط المستخدم على زر رجوع الجهاز (لتنفيذ عملية التنقل [navigation] على سبيل المثال). عندما لا تُعالج JavaScript الضغط على زر الرجوع، سيُستدعى تابع invokeDefaultOnBackPressed
الخاص بك. افتراضيًّا، يُنهي هذا نشاطَك (Activity
) ببساطة.
وأخيرا، علينا ربط قائمة التطوير (dev menu). افتراضيًًا، تُفعَّل القائمة عبر هز الجهاز، ولكن هذا ليس مفيدًا في برامج المحاكاة. لذلك سنجعلها تظهر عندما تضغط على زر القائمة في الجهاز hardware menu button (استخدم Ctrl + M
إذا كنت تستخدم محاكي Android Studio):
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}
نشاطك جاهز الآن لتشغيل شيفرة JavaScript.
اختبار الدمج
انتهيت الآن من جميع الخطوات الأساسية لدمج React Native مع تطبيقك الحالي. سنبدأ الآن بتشغيل مُحزّم React Native لإنشاء حزمة index.bundle
والخادم الذي سيعمل على المضيف المحلي localhost
.
1. تشغيل المُحزّم
لتشغيل تطبيقك، عليك أولاً تشغيل خادم التطوير (development server) عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروع React Native الخاص بك:
$ yarn start
2. تشغيل التطبيق
يمكنك الآن بناء تطبيقك كأيّ تطبيق Android آخر وتشغيله كالمعتاد.
بمجرد الوصول إلى نشاطك القائم على React داخل التطبيق، من المفترض أن تُحمَّل شيفرة JavaScript من خادم التطوير مع عرض الشاشة التالية:
إنشاء بناء إصدار (release build) في Android Studio
يمكنك استخدام Android Studio لإنشاء بناءات الإصدار أيضًا! كل ما عليك فعله هو إنشاء بناءات الإصدار من تطبيق Android الأصيل الموجود مسبقًا. هناك خطوة إضافية واحدة فقط، والتي يجب عليك القيام بها قبل كل بناء إصدار. تحتاج إلى تنفيذ ما يلي لإنشاء حزمة React Native، والتي ستُضمَّن مع تطبيق Android الأصيل الخاص بك:
$ npx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/com/your-company-name/app-package-name/src/main/assets/index.android.bundle --assets-dest android/com/your-company-name/app-package-name/src/main/res/
ملاحظة: لا تنس استبدال المسارات في الأمر بالمسارات الصحيحة وإنشاء مجلد الأصول (assets folder) إذا لم يكن موجودًا.
الآن أنشئ بناء إصدارٍ لتطبيقك الأصيل من داخل Android Studio كالمعتاد وسيكون كل شيء على ما يرام!
ماذا بعد؟
يمكنك الآن متابعة تطوير تطبيقك كالمعتاد. راجع توثيق التنقيح والنشر لمعرفة المزيد حول العمل مع React Native.
الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Objective-C
المفاهيم الرئيسية
أهمّ الأمور لدمج مكونات React Native في تطبيق iOS الخاص بك هي:
- إعداد اعتماديات (dependencies) إطار العمل React Native وهيكل المجلّدات (directory structure).
- فهم مكونات React Native التي ستستخدمها في تطبيقك.
- إضافة هذه المكوّنات كاعتماديّاتٍ باستخدام CocoaPods.
- كتابة مكونات React Native باستخدام JavaScript.
- إضافة واجهة
RCTRootView
إلى تطبيق iOS الخاص بك. ستكون هذه الواجهة بمثابة الحاوية لمكون React Native الخاص بك. - بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل.
- تحقّق من أن جانب React Native من التطبيق الخاص بك يعمل كما هو متوقع.
المتطلبات الأساسية
اتبع إرشادات بناء مشاريعٍ بشيفرة أصيلة في الدرس التقديمي لتهيئة بيئة التطوير الخاصة بك لإنشاء تطبيقات React Native لنظام التشغيل iOS.
1. إعداد بنية المجلدات (directory structure)
لتسهيل العمل، أنشئ مجلدًا جديدًا لمشروع React Native المُدمَج الخاص بك، ثم انسخ مشروع iOS الحالي إلى مجلد فرعي باسم /ios
.
2. تثبيت اعتماديات JavaScript
انتقل إلى المجلّد الجذر (root directory) للمشروع الخاص بك وأنشئ ملف package.json
جديد بالمحتويات التالية:
{
"name": "MyReactNativeApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "yarn react-native start"
}
}
بعد ذلك، تأكّد من تثبيت مدير الحزم Yarn.
ثبّت حزمتي react
وreact-native
. افتح طرفيّةً (terminal) أو مَحثَّ أوامر (command prompt)، ثم انتقل إلى المجلّد الذي يحتوي على ملف package.json
ونفّذ الأمر التالي:
// yarn
yarn add react-native
// أو
// npm
npm install react-native
سيؤدي هذا إلى طباعة رسالةٍ مشابهة لما يلي (مرِّر لأعلى مُخرجات yarn
لمشاهدتها):
warning "react-native@0.52.2" has unmet peer dependency "react@16.2.0".
هذا يعني أنّنا بحاجة إلى تثبيت React كذلك:
//yarn
yarn add react@version_printed_above
//أو
//npm
npm install react@version_printed_above
ضع مكان version_printed_above الإصدار المذكور في رسالة التحذير التي ظهرت لك آنفًا ضمن "react@16.2.0".
أنشأت عملية التثبيت الآن مجلدَ /node_modules
جديد. يخزّن هذا المجلد جميع اعتماديات JavaScript المطلوبة لإنشاء مشروعك.
أضف node_modules/
إلى ملفّ .gitignore
الخاص بك.
3. تثبيت CocoaPods
CocoaPods هي أداةٌ لإدارة الحزم لتطوير تطبيقات iOS و macOS. سنستخدمها لإضافة شيفرة إطار React Native الفعليّة محليًا إلى مشروعك الحالي.
نوصي بتثبيت CocoaPods باستخدام Homebrew.
brew install cocoapods
ملاحظة: من الممكن تقنيًا الاستغناء عن أداة CocoaPods، ولكن هذا سيتطلب إضافة مكتباتٍ وروابط يدويًّا، ما سيُعقّد هذه العملية.
إضافة React Native إلى تطبيقك
لنفترض أن التطبيق الذي تُريد دمج مكونات جديدة معه هو لعبة 2048. إليك ما تبدو عليه القائمة الرئيسية للتطبيق الأصيل دون React Native.
أدوات سطر الأوامر لبيئة Xcode
لتثبيت أدوات سطر الأوامر (Command Line Tools). اختر "Preferences..." في قائمة Xcode. انتقل إلى لوحة Locations وثبّت الأدوات عن طريق تحديد أحدث إصدارٍ في قائمة Command Line Tools المنسدلة.
إعداد اعتماديات CocoaPods
قبل دمج React Native مع تطبيقك، ستحتاج إلى تحديد أجزاء إطار React Native التي تريد دمجها مع التطبيق. سنستخدم CocoaPods لتحديد أي من هذهِ المواصفات الفرعيّة (subspecs) سيعتمد عليها تطبيقك.
قائمة المواصفات الفرعية المدعومة متوفّرة في الملفّ /node_modules/react-native/React.podspec
. وتُسمَّى عمومًا حسب وظيفتها. على سبيل المثال، ستحتاج دائمًا إلى المواصفة الفرعيّة Core
. التي تحتوي على AppRegistry
و StyleSheet
و View
وغيرها من المكتبات الرئيسية في إطار React Native. إذا كنت ترغب في إضافة مكتبة Text
الخاصة بإطار React Native (لاستخدام عناصر <Text>
على سبيل المثال)، فستحتاج إلى المواصفة الفرعيّة RCTText
. إذا كنت ترغب باستخدام مكتبة Image (لعناصر <Image>
مثلًا)، فستحتاج إلى المواصفة الفرعيّة RCTImage
.
يمكنك تحديد المواصفات الفرعية التي سيعتمد عليها تطبيقك في ملف Podfile
. أسهل طريقة لإنشاء ملفّ Podfile
هي تنفيذ الأمر init
الخاص بأداة CocoaPods في المجلد الفرعي/ios
الخاص بمشروعك:
pod init
سوف يحتوي ملف Podfile
على إعداد نموذجيّ (boilerplate setup) يُمكنك تعديله حسب طريقة الدمج التي تريدها.
ملاحظة: تختلف نسخة Podfile
بحسب نسخة react-native، راجع https://react-native-community.github.io/upgrade-helper لمعرفة نسخة Podfile
التي يجب استخدامها.
يجب أن يبدو ملفّ Podfile
مشابهًا لهذا في النهاية:
# The target name is most likely the name of your project.
# اسم الهدف سيكون في أغلب الظن اسمَ مشروعك
target 'NumberTileGame' do
# Your 'node_modules' directory is probably in the root of your project,
# but if not, adjust the `:path` accordingly
# مجلدُ الاعتماديات موجود على الأغلب في جذر مشروعك، إن لم يكن كذلك، فعدّل المسار
pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
pod 'React', :path => '../node_modules/react-native/'
pod 'React-Core', :path => '../node_modules/react-native/'
pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon"
pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
end
بعد إنشاء ملف Podfile
الخاص بك، ستكون جاهزًا لتثبيت React Native.
$ pod install
سيكون الخرج مشابها لما يلي:
Analyzing dependencies
Fetching podspec for `React` from `../node_modules/react-native`
Downloading dependencies
Installing React (0.62.0)
Generating Pods project
Integrating client project
Sending stats
Pod installation complete! There are 3 dependencies from the Podfile and 1 total pod installed.
إذا فشل هذا مع وجود أخطاء تحتوي على xcrun، فتأكّد من تعيين أدوات Command Line Tools في Xcode عبر قائمة Preferences ثمّ Locations.
دمج الشيفرة
سنُعدّل الآن تطبيق iOS الأصيل لدمج React Native معه. سنضيف شاشة "High Score" لتطبيق 2048 الذي بين أيدينا باستخدام React Native.
مكون React Native
الجزء الأول من الشيفرة التي سنكتبها هي شيفرة React Native لشاشة "High Score" الجديدة التي سندمجها في تطبيقنا.
1. إنشاء ملف index.js
أولاً، أنشئ ملفّ index.js
فارغ في جذر مشروع React Native الخاص بك.
ملفّ index.js
هو نقطة البداية لتطبيقات React Native، وهو ضروري دائمًا. يمكن أن يكون ملفًا صغيرًا يطلب (يستورد require
) ملفًا آخر يمثّل جزءًا من مكوّن أو تطبيق React Native الخاص بك، أو يمكن أن يحتوي على كامل الشيفرة البرمجية اللازمة لذلك. في حالتنا، سنضع كامل الشيفرة في ملفّ index.js
.
2. أضف شيفرة React Native الخاصة بك
أنشئ المكوّن في ملفّ index.js
الخاص بك. في نموذجنا هنا، سنضيف مكون <Text>
بسيط داخل مكوّن <View>
مُنسَّق:
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
const RNHighScores = ({ scores }) => {
const contents = scores.map((score) => (
<Text key={score.name}>
{score.name}:{score.value}
{'\n'}
</Text>
));
return (
<View style={styles.container}>
<Text style={styles.highScoresTitle}>
2048 High Scores!
</Text>
<Text style={styles.scores}>{contents}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFFFFF'
},
highScoresTitle: {
fontSize: 20,
textAlign: 'center',
margin: 10
},
scores: {
textAlign: 'center',
color: '#333333',
marginBottom: 5
}
});
// Module name
AppRegistry.registerComponent('RNHighScores', () => RNHighScores);
RNHighScores
هو اسم وحدتك الذي سيُستخدَم عند إضافة واجهة إلى React Native من داخل تطبيق iOS الخاص بك.
الرابط العجيب: RCTRootView
الآن، وبعد إنشاء مكون React Native الخاص بك عبر ملفّ index.js
، تحتاج إلى إضافة هذا المكون إلى مُتحكّم ViewController
جديد أو موجود مسبقا. أيسر سبيل إلى ذلك هو إنشاء مسار حدث (event path) اختياريًّا يُشير إلى المكون الخاص بك، ثم إضافة هذا المكون إلى ViewController
موجود.
سنربط مكوّن React Native الخاص بنا مع واجهة أصيلة جديد في متحكّم ViewController
، والذي سيستضيف المكوّن. الواجهة الأصيلة تسمّى RCTRootView
.
1. إنشاء مسار حدث (Event Path)
يمكنك إضافة رابط جديد في قائمة اللعبة الرئيسية للانتقال إلى صفحة "High Score" المبنيّة بإطار React Native.
2. معالج الأحداث
سنضيف الآن معالج أحداثٍ من رابط القائمة. سيُضاف تابع إلى متحكّم ViewController
الرئيسي للتطبيق الخاص بك. هنا سنعتمد على واجهة RCTRootView
.
عند إنشاء تطبيق React Native، يُستخدَم المُجمّع Metro لإنشاء ملفّ index.bundle
الذي سيُقدَّم من طرف خادم React Native. سيحتوي ملفّ index.bundle
على وحدة RNHighScore
الخاصة بنا. لذا، سنحتاج إلى توجيه واجهة RCTRootView
الخاص بنا إلى مكان الملفّ index.bundle
(عبر NSURL
) وربطه بالوحدة.
لتسهيل التنقيح (debugging)، سنُسجّل (log) أنّ معالج الأحداث قد استُدعَى. بعد ذلك، سننشئ سلسلة نصيّة تحتوي على مكان شيفرة React Native الموجودة داخل ملفّ index.bundle
. أخيرًا، سننشئ الواجهة RCTRootView
الرئيسيّة. لاحظ كيف نُشير إلى الاسم RNHighScores
على أنّه اسم الوحدة (moduleName) الذي أنشأناه أعلاه عند كتابة شيفرة مكوّن React Native الخاص بنا.
أولاً استورد ترويسة RCTRootView
:
#import <React/RCTRootView.h>
الخاصيّات الأوليّةinitialProperties
موجودة هنا لأغراضٍ توضيحيّة ولتكون لدينا بعض البيانات لعرضها في شاشة "High Score". في مكوّن React Native الخاص بنا، سنستخدم this.props
للوصول إلى هذه البيانات.
- (IBAction)highScoreButtonPressed:(id)sender {
NSLog(@"High Score Button Pressed");
NSURL *jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.bundle?platform=ios"];
RCTRootView *rootView =
[[RCTRootView alloc] initWithBundleURL: jsCodeLocation
moduleName: @"RNHighScores"
initialProperties:
@{
@"scores" : @[
@{
@"name" : @"Alex",
@"value": @"42"
},
@{
@"name" : @"Joel",
@"value": @"10"
}
]
}
launchOptions: nil];
UIViewController *vc = [[UIViewController alloc] init];
vc.view = rootView;
[self presentViewController:vc animated:YES completion:nil];
}
لاحظ أن المقطع RCTRootView initWithURL
يبدأ بتشغيل آلة افتراضية JSC جديدة (JSC VM). لتوفير الموارد وتبسيط الاتصال بين واجهات React Native في أجزاء مختلفة من التطبيق الأصلي، يُمكنك إنشاء عدّة واجهات مدعومة من React Native مرتبطة بوقت تشغيل JS واحد. للقيام بهذا، استخدم RCTBridge initWithBundleURL
بدلاً من استخدام[RCTRootView alloc] initWithURL
لإنشاء جسر ثم استخدم RCTRootView initWithBridge
.
عند نقل تطبيقك إلى بيئة الإنتاج (production)، يمكن لعنوان NSURL الإشارة إلى ملفّ مُحزَّم مسبقًا (pre-bundled) على القرص عبر سطرٍ برمجيّ مثل:
[[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
يمكنك استخدام برمجيّة react-native-xcode.sh
الموجودة في المسار node_modules/react-native/scripts/
لإنشاء هذا الملف المُحزّم مسبقًا.
3. الربط
اربط الرابط الجديد في القائمة الرئيسية بتابعِ معالجِ الأحداث المضاف حديثًا.
من أسهل الطرق للقيام بهذا هي فتح الواجهة في لوحة العمل (storyboard) والنقر بزر الفأرة الأيمن على الرابط الجديد. حدّد حدثًا مثل حدث Touch Up Inside
، واسحبه إلى لوحة العمل ثم حدّد الواجهة المُنشَأ من القائمة المتوفرة.
اختبار الدمج
انتهيت الآن من جميع الخطوات الأساسية لدمج React Native مع تطبيقك الحالي. سنبدأ الآن بتشغيل المُجمّع Metro لإنشاء حزمة index.bundle
والخادم الذي سيعمل على المضيف المحلي localhost
.
1. إضافة استثناء App Transport Security
حظرت شركة Apple تحميل موارد نصوص HTTP الضمنيّة (implicit cleartext HTTP). لذلك نحتاج إلى إضافة ما يلي إلى ملف Info.plist
الخاص بالتطبيق (أو ما يعادله).
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
سياسة أمان App Transport Security مفيدة لمستخدمي التطبيق. تأكّد من إعادة تمكينها قبل إطلاق تطبيقك للإنتاج.
2. تشغيل المُحزّم
لتشغيل تطبيقك، عليك أولاً تشغيل خادم التطوير (development server) عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروع React Native الخاص بك:
//npm
npm start
//yarn
yarn start
3. تشغيل التطبيق
إذا كنت تستخدم Xcode أو محرّرك المفضّل، فأنشئ تطبيق iOS أصيل وشغّله كالمعتاد. أو يمكنك تشغيل التطبيق من سطر الأوامر عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروعك :
$ npx react-native run-ios
سترى الآن في تطبيقنا، رابطًا يشير إلى شاشة "High Scores"، وعند النقر فوقه، سترى مكوّن React Native الخاص بك.
هذه هي الشاشة الرئيسيّة للتطبيق الأصيل:
هذه شاشة مكوّن React Native:
إذا واجهتك مشكلة في تقرير الوحدات (module resolution) عند تشغيل تطبيقك، فيرجى الاطلاع على هذه الصفحة للحصول على معلوماتٍ قد تساعدك على حل مشكلتك. يبدو هذا التعليق حلًّا ممكنًا.
ماذا بعد؟
يمكنك الآن متابعة تطوير تطبيقك كالمعتاد. راجع توثيق التنقيح والنشر لمعرفة المزيد حول العمل مع React Native.
الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Swift
المفاهيم الرئيسية
أهمّ الأمور لدمج مكونات React Native في تطبيق iOS الخاص بك هي:
- إعداد اعتماديات (dependencies) إطار العمل React Native وهيكل المجلّدات (directory structure).
- فهم مكونات React Native التي ستستخدمها في تطبيقك.
- إضافة هذه المكوّنات كاعتماديّاتٍ باستخدام CocoaPods.
- كتابة مكونات React Native باستخدام JavaScript.
- إضافة الواجهة
RCTRootView
إلى تطبيق iOS الخاص بك. ستكون هذه الواجهة بمثابة الحاوية لمكون React Native الخاص بك. - بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل.
- تحقّق من أن جانب React Native من التطبيق الخاص بك يعمل كما هو متوقع.
المتطلبات الأساسية
اتبع إرشادات بناء مشاريعٍ بشيفرة أصيلة في الدرس التقديمي لتهيئة بيئة التطوير الخاصة بك لإنشاء تطبيقات React Native لنظام التشغيل iOS.
1. إعداد بنية المجلدات (directory structure)
لتسهيل العمل، أنشئ مجلدًا جديدًا لمشروع React Native المُدمَج الخاص بك، ثم انسخ مشروع iOS الحالي إلى مجلد فرعي باسم /ios
.
2. تثبيت اعتماديات JavaScript
انتقل إلى المجلّد الجذر (root directory) للمشروع الخاص بك وأنشئ ملف package.json
جديد بالمحتويات التالية:
{
"name": "MyReactNativeApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "yarn react-native start"
}
}
بعد ذلك، تأكّد من تثبيت مدير الحزم Yarn.
ثبّت حزمتي react
وreact-native
. افتح طرفيّةً (terminal) أو مَحثَّ أوامر (command prompt)، ثم انتقل إلى المجلّد الذي يحتوي على ملف package.json
ونفّذ الأمر التالي:
$ yarn add react-native
سيؤدي هذا إلى طباعة رسالةٍ مشابهة لما يلي (مرِّر لأعلى مُخرجات yarn
لمشاهدتها):
warning "react-native@0.52.2" has unmet peer dependency "react@16.2.0".
هذا يعني أنّنا بحاجة إلى تثبيت React كذلك:
$ yarn add react@version_printed_above
ضع مكان version_printed_above الإصدار المذكور في رسالة التحذير التي ظهرت لك آنفًا ضمن "react@16.2.0".
أنشأَ Yarn مجلدَ /node_modules
جديد. يخزّن هذا المجلد جميع اعتماديات JavaScript المطلوبة لإنشاء مشروعك.
أضف node_modules/
إلى ملفّ .gitignore
الخاص بك.
3. تثبيت CocoaPods
CocoaPods هي أداةٌ لإدارة الحزم لتطوير تطبيقات iOS و macOS. سنستخدمها لإضافة شيفرة إطار React Native الفعليّة محليًا إلى مشروعك الحالي.
نوصي بتثبيت CocoaPods باستخدام Homebrew.
$ brew install cocoapods
ملاحظة: من الممكن تقنيًا الاستغناء عن أداة CocoaPods، ولكن هذا سيتطلب إضافة مكتباتٍ وروابط يدويًّا، ما سيُعقّد هذه العملية.
إضافة React Native إلى تطبيقك
لنفترض أن التطبيق الذي تُريد دمج مكونات جديدة معه هو لعبة 2048. إليك ما تبدو عليه القائمة الرئيسية للتطبيق الأصيل دون React Native.
أدوات سطر الأوامر لبيئة Xcode
لتثبيت أدوات سطر الأوامر (Command Line Tools). اختر "Preferences..." في قائمة Xcode. انتقل إلى لوحة Locations وثبّت الأدوات عن طريق تحديد أحدث إصدارٍ في قائمة Command Line Tools المنسدلة.
إعداد اعتماديات CocoaPods
قبل دمج React Native مع تطبيقك، ستحتاج إلى تحديد أجزاء إطار React Native التي تريد دمجها مع التطبيق. سنستخدم CocoaPods لتحديد أي من هذهِ المواصفات الفرعيّة (subspecs) سيعتمد عليها تطبيقك.
قائمة المواصفات الفرعية المدعومة متوفّرة في الملفّ /node_modules/react-native/React.podspec
. وتُسمَّى عمومًا حسب وظيفتها. على سبيل المثال، ستحتاج دائمًا إلى المواصفة الفرعيّة Core
. التي تحتوي على AppRegistry
و StyleSheet
و View
وغيرها من المكتبات الرئيسية في إطار React Native. إذا كنت ترغب في إضافة مكتبة Text
الخاصة بإطار React Native (لاستخدام عناصر <Text>
على سبيل المثال)، فستحتاج إلى المواصفة الفرعيّة RCTText
. إذا كنت ترغب باستخدام مكتبة Image
(لعناصر <Image>
مثلًا)، فستحتاج إلى المواصفة الفرعيّة RCTImage
.
يمكنك تحديد المواصفات الفرعية التي سيعتمد عليها تطبيقك في ملف Podfile
. أسهل طريقة لإنشاء ملفّ Podfile
هي تنفيذ الأمر init
الخاص بأداة CocoaPods في المجلد الفرعي/ios
الخاص بمشروعك:
$ pod init
سوف يحتوي ملف Podfile
على إعداد نموذجيّ (boilerplate setup) يُمكنك تعديله حسب طريقة الدمج التي تريدها.
ملاحظة: تختلف نسخة Podfile
بحسب نسخة react-native، راجع https://react-native-community.github.io/upgrade-helper لمعرفة نسخة Podfile
التي يجب استخدامها.
يجب أن يبدو ملفّ Podfile
مشابهًا لهذا في النهاية:
source 'https://github.com/CocoaPods/Specs.git'
# مطلوب للتطبيقات المكتوبة بلغة
# Swift
platform :ios, '8.0'
use_frameworks!
# The target name is most likely the name of your project.
# اسم الهدف سيكون في أغلب الظن اسمَ مشروعك
target 'swift-2048' do
# Your 'node_modules' directory is probably in the root of your project,
# but if not, adjust the `:path` accordingly
# مجلدُ الاعتماديات موجود على الأغلب في جذر مشروعك، إن لم يكن كذلك، فعدّل المسار
pod 'React', :path => '../node_modules/react-native', :subspecs => [
'Core',
'CxxBridge', # Include this for RN >= 0.47
'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
'RCTText',
'RCTNetwork',
'RCTWebSocket', # needed for debugging
# Add any other subspecs you want to use in your project
# أضف أي مواصفات فرعية أخرى حسب ما تريد الاعتماد عليه في مشروعك
]
# Explicitly include Yoga if you are using RN >= 0.42.0
pod "Yoga", :path => "../node_modules/react-native/ReactCommon/yoga"
# Third party deps podspec link
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
end
بعد إنشاء ملف Podfile
الخاص بك، ستكون جاهزًا لتثبيت React Native.
$ pod install
سيكون الخرج مشابها لما يلي:
Analyzing dependencies
Fetching podspec for `React` from `../node_modules/react-native`
Downloading dependencies
Installing React (0.62.0)
Generating Pods project
Integrating client project
Sending stats
Pod installation complete! There are 3 dependencies from the Podfile and 1 total pod installed.
إذا فشل هذا مع وجود أخطاء تحتوي على xcrun، فتأكّد من تعيين أدوات Command Line Tools في Xcode عبر قائمة Preferences ثمّ Locations.
إذا حصلت على تنبيه مشابه للنصّ التالي:
"The swift-2048 [Debug] target overrides the FRAMEWORK_SEARCH_PATHS build setting defined in Pods/Target Support Files/Pods-swift-2048/Pods-swift-2048.debug.xcconfig. This can lead to problems with the CocoaPods installation"
فتأكّد من أن مسارات البحث عن أطر العمل Framework Search Paths
في إعدادات البناء Build Settings
لكل من Debug
وRelease
تحتوي فقط على $(inherited)
.
دمج الشيفرة
سنُعدّل الآن تطبيق iOS الأصيل لدمج React Native معه. سنضيف شاشة "High Score" لتطبيق 2048 الذي بين أيدينا باستخدام React Native.
مكون React Native
الجزء الأول من الشيفرة التي سنكتبها هي شيفرة React Native لشاشة "High Score" الجديدة التي سندمجها في تطبيقنا.
1. إنشاء ملف index.js
أولاً، أنشئ ملفّ index.js
فارغ في جذر مشروع React Native الخاص بك.
ملفّ index.js
هو نقطة البداية لتطبيقات React Native، وهو ضروري دائمًا. يمكن أن يكون ملفًا صغيرًا يطلب (require
) ملفًا آخر يمثّل جزءًا من مكوّن أو تطبيق React Native الخاص بك، أو يمكن أن يحتوي على كامل الشيفرة البرمجية اللازمة لذلك. في حالتنا، سنضع كامل الشيفرة في ملفّ index.js
.
2. أضف شيفرة React Native الخاصة بك
أنشئ المكوّن في ملفّ index.js
الخاص بك. في نموذجنا هنا، سنضيف مكون <Text>
بسيط داخل مكوّن <View>
مُنسَّق:
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
const RNHighScores = ({ scores }) => {
const contents = scores.map((score) => (
<Text key={score.name}>
{score.name}:{score.value}
{'\n'}
</Text>
));
return (
<View style={styles.container}>
<Text style={styles.highScoresTitle}>
2048 High Scores!
</Text>
<Text style={styles.scores}>{contents}</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFFFFF'
},
highScoresTitle: {
fontSize: 20,
textAlign: 'center',
margin: 10
},
scores: {
textAlign: 'center',
color: '#333333',
marginBottom: 5
}
});
// Module name
AppRegistry.registerComponent('RNHighScores', () => RNHighScores);
RNHighScores
هو اسم وحدتك الذي سيُستخدَم عند إضافة واجهة إلى React Native من داخل تطبيق iOS الخاص بك.
الرابط العجيب: RCTRootView
الآن، وبعد إنشاء مكون React Native الخاص بك عبر ملفّ index.js
، تحتاج إلى إضافة هذا المكون إلى مُتحكّم ViewController
جديد أو موجود مسبقا. أيسر سبيل إلى ذلك هو إنشاء مسار حدث (event path) اختياريًّا يُشير إلى المكون الخاص بك، ثم إضافة هذا المكون إلى ViewController
موجود.
سنربط مكوّن React Native الخاص بنا مع واجهة أصيلة جديد في متحكّم ViewController
، والذي سيستضيف المكوّن. الواجهة الأصيلة تسمّى RCTRootView
.
1. إنشاء مسار حدث (Event Path)
يمكنك إضافة رابط جديد في قائمة اللعبة الرئيسية للانتقال إلى صفحة "High Score" المبنيّة بإطار React Native.
2. معالج الأحداث
سنضيف الآن معالج أحداثٍ من رابط القائمة. سيُضاف تابع إلى متحكّم ViewController
الرئيسي للتطبيق الخاص بك. هنا سنعتمد على واجهة RCTRootView
.
عند إنشاء تطبيق React Native، يُستخدَم المجمّع Metro لإنشاء ملفّ index.bundle
الذي سيُقدَّم من طرف خادم React Native. سيحتوي ملفّ index.bundle
على وحدة RNHighScore
الخاصة بنا. لذا، سنحتاج إلى توجيه الواجهة RCTRootView
الخاص بنا إلى مكان الملفّ index.bundle
(عبر NSURL
) وربطه بالوحدة.
لتسهيل التنقيح (debugging)، سنُسجّل (log) أنّ معالج الأحداث قد استُدعَى. بعد ذلك، سننشئ سلسلة نصيّة تحتوي على مكان شيفرة React Native الموجودة داخل ملفّ index.bundle
. أخيرًا، سننشئ الواجهة RCTRootView
الرئيسيّ. لاحظ كيف نُشير إلى الاسم RNHighScores
على أنّه اسم الوحدة moduleName
الذي أنشأناه أعلاه عند كتابة شيفرة مكوّن React Native الخاص بنا.
أولاً استورد (عبر import
) مكتبة React
:
import React
الخاصيّات الأوليّةinitialProperties
موجودة هنا لأغراضٍ توضيحيّة، ولتكون لدينا بعض البيانات لعرضها في شاشة "High Score". في مكوّن React Native الخاص بنا، سنستخدم this.props
للوصول إلى هذه البيانات.
@IBAction func highScoreButtonTapped(sender : UIButton) {
NSLog("Hello")
let jsCodeLocation = URL(string: "http://localhost:8081/index.bundle?platform=ios")
let mockData:NSDictionary = ["scores":
[
["name":"Alex", "value":"42"],
["name":"Joel", "value":"10"]
]
]
let rootView = RCTRootView(
bundleURL: jsCodeLocation,
moduleName: "RNHighScores",
initialProperties: mockData as [NSObject : AnyObject],
launchOptions: nil
)
let vc = UIViewController()
vc.view = rootView
self.present(vc, animated: true, completion: nil)
}
لاحظ أن المقطع RCTRootView initWithURL
يبدأ بتشغيل آلة افتراضية JSC جديدة (JSC VM). لتوفير الموارد وتبسيط الاتصال بين واجهات React Native في أجزاء مختلفة من التطبيق الأصلي، يُمكنك إنشاء عدّة واجهات مدعومة من React Native مرتبطة بوقت تشغيل JS واحد. للقيام بهذا، استخدم RCTBridge initWithBundleURL
بدلاً من ستخدام RCTRootView bundleURL
لإنشاء جسر ثم استخدم RCTRootView initWithBridge
.
عند نقل تطبيقك إلى بيئة الإنتاج (production)، يمكن لعنوان NSURL
الإشارة إلى ملفّ مُحزَّم مسبقًا (pre-bundled) على القرص عبر سطرٍ برمجيّ مثل:
let mainBundle = NSBundle(URLForResource: "main" withExtension:"jsbundle")
يمكنك استخدام برمجيّة react-native-xcode.sh
الموجودة في المسار node_modules/react-native/scripts/
لإنشاء هذا الملف المُحزّم مسبقًا.
3. الربط
اربط الرابط الجديد في القائمة الرئيسية بتابعِ معالجِ الأحداث المضاف حديثًا.
من أسهل الطرق للقيام بهذا هي فتح الواجهة في لوحة العمل (storyboard) والنقر بزر الفأرة الأيمن على الرابط الجديد. حدّد حدثًا مثل حدث Touch Up Inside
، واسحبه إلى لوحة العمل ثم حدّد الواجهة المُنشَأة من القائمة المتوفرة.
اختبار الدمج
انتهيت الآن من جميع الخطوات الأساسية لدمج React Native مع تطبيقك الحالي. سنبدأ الآن بتشغيل مُحزّم React Native لإنشاء حزمة index.bundle
والخادم الذي سيعمل على المضيف المحلي localhost
.
1. إضافة استثناء App Transport Security
حظرت شركة Apple تحميل موارد نصوص HTTP الضمنيّة (implicit cleartext HTTP). لذلك نحتاج إلى إضافة ما يلي إلى ملف Info.plist
الخاص بالتطبيق (أو ما يعادله).
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
سياسة أمان App Transport Security مفيدة لمستخدمي التطبيق. تأكّد من إعادة تمكينها قبل إطلاق تطبيقك للإنتاج.
2. تشغيل المُحزّم
لتشغيل تطبيقك، عليك أولاً تشغيل خادم التطوير (development server) عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروع React Native الخاص بك:
$ npm start
3. تشغيل التطبيق
إذا كنت تستخدم Xcode أو محرّرك المفضّل، فأنشئ تطبيق iOS أصيل وشغّله كالمعتاد. أو يمكنك تشغيل التطبيق من سطر الأوامر عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروعك:
$ npx react-native run-ios
سترى الآن في تطبيقنا، رابطًا يشير إلى شاشة "High Scores"، وعند النقر فوقه، سترى مكوّن React Native الخاص بك.
هذه هي الشاشة الرئيسيّة للتطبيق الأصيل:
هذه شاشة مكوّن React Native:
إذا واجهتك مشكلة في تقرير الوحدات (module resolution) عند تشغيل تطبيقك، فيرجى الاطلاع على هذه الصفحة للحصول على معلوماتٍ قد تساعدك على حل مشكلتك. يبدو هذا التعليق حلًّا ممكنًا.
ماذا بعد؟
يمكنك الآن متابعة تطوير تطبيقك كالمعتاد. راجع توثيق التنقيح والنشر لمعرفة المزيد حول العمل مع React Native.