الفرق بين المراجعتين ل"ReactNative/integration with existing apps"

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
ط
 
(13 مراجعة متوسطة بواسطة 3 مستخدمين غير معروضة)
سطر 1: سطر 1:
<noinclude>{{DISPLAYTITLE: الدمج مع تطبيقات قائمة في React Native}}</noinclude>
+
<noinclude>{{DISPLAYTITLE:دمج واجهات React Native مع تطبيقات أصيلة قائمة}}</noinclude>
إطار العمل React Native رائعٌ لبرمجة تطبيق جوال جديد من البداية. لكن، يُمكنك كذلك استخدامه لإضافة عرضٍ (view) واحدٍ أو ميّزة إلى التطبيقات الأصيلة الموجودة بالفعل. يمكنك باتّباع بضعة خطواتٍ إضافةُ ميّزات جديدة باستخدام React Native إلى تطبيقك، مثل شاشات (screens) جديدة، وعروض، إلخ.
+
إطار العمل 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
+
 
 +
==الدمج مع تطبيقات 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 الخاص بك.
+
# إضافة واجهة <code>RCTRootView</code> إلى تطبيق iOS الخاص بك. ستكون هذه الواجهة بمثابة الحاوية لمكون React Native الخاص بك.
 
# بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل.
 
# بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل.
 
# تحقّق من أن جانب React Native من التطبيق الخاص بك يعمل كما هو متوقع.
 
# تحقّق من أن جانب React Native من التطبيق الخاص بك يعمل كما هو متوقع.
سطر 34: سطر 372:
 
   "private": true,
 
   "private": true,
 
   "scripts": {
 
   "scripts": {
     "start": "node node_modules/react-native/local-cli/cli.js start"
+
     "start": "yarn react-native start"
 
   }
 
   }
 
}
 
}
سطر 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>
أنشأَ Yarn مجلدَ ‎‎<code>/node_modules</code>‎‎ جديد. يخزّن هذا المجلد جميع اعتماديات JavaScript المطلوبة لإنشاء مشروعك.
+
ضع مكان 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', :subspecs => [
+
  pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
    'Core',
+
  pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
    'CxxBridge', # Include this for RN >= 0.47
+
  pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
    'DevSupport', # Include this to enable In-App Devmenu if RN >= 0.43
+
  pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
    'RCTText',
+
   pod 'React', :path => '../node_modules/react-native/'
    'RCTNetwork',
+
  pod 'React-Core', :path => '../node_modules/react-native/'
    'RCTWebSocket', # Needed for debugging
+
  pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
    'RCTAnimation', # Needed for FlatList and animations running on native UI thread
+
  pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
    # Add any other subspecs you want to use in your project
+
  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'
   # Explicitly include Yoga if you are using RN >= 0.42.0
+
  pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
   pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
+
  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'
  
  # Third party deps podspec link
 
 
   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.26.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';
  
class RNHighScores extends React.Component {
+
const RNHighScores = ({ scores }) => {
  render() {
+
  const contents = scores.map((score) => (
    var contents = this.props['scores'].map((score) => (
+
    <Text key={score.name}>
      <Text key={score.name}>
+
      {score.name}:{score.value}
        {score.name}:{score.value}
+
      {'\n'}
        {'\n'}
+
    </Text>
 +
  ));
 +
  return (
 +
    <View style={styles.container}>
 +
      <Text style={styles.highScoresTitle}>
 +
        2048 High Scores!
 
       </Text>
 
       </Text>
    ));
+
       <Text style={styles.scores}>{contents}</Text>
    return (
+
    </View>
       <View style={styles.container}>
+
  );
        <Text style={styles.highScoresTitle}>2048 High Scores!</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> هو اسم وحدتك الذي سيُستخدَم عند إضافة عرض إلى React Native من داخل تطبيق iOS الخاص بك.
+
<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 الخاص بنا مع عرض أصيل جديد في متحكّم <code>ViewController</code>، والذي سيستضيف المكوّن. العرضُ الأصيل يُسمّى <code>RCTRootView</code>.
+
سنربط مكوّن React Native الخاص بنا مع واجهة أصيلة جديد في متحكّم <code>ViewController</code>، والذي سيستضيف المكوّن. الواجهة الأصيلة تسمّى <code>RCTRootView</code>.
  
 
======1. إنشاء مسار حدث (Event Path)======
 
======1. إنشاء مسار حدث (Event Path)======
سطر 203: سطر 577:
  
 
======2. معالج الأحداث======
 
======2. معالج الأحداث======
سنضيف الآن معالج أحداثٍ من رابط القائمة. سيُضاف تابع إلى متحكّم <code>ViewController</code> الرئيسي للتطبيق الخاص بك. هنا سنعتمد على عرض <code>RCTRootView</code>.
+
سنضيف الآن معالج أحداثٍ من رابط القائمة. سيُضاف تابع إلى متحكّم <code>ViewController</code> الرئيسي للتطبيق الخاص بك. هنا سنعتمد على واجهة <code>RCTRootView</code>.
  
عند إنشاء تطبيق React Native، يُستخدَم مُحزِّم React Native لإنشاء ملفّ ‎‎<code>index.bundle</code>‎‎ الذي سيُقدَّم من طرف خادم React Native. سيحتوي ملفّ <code>index.bundle</code> على وحدة <code>RNHighScore</code> الخاصة بنا. لذا، سنحتاج إلى توجيه عرض RCTRootView الخاص بنا إلى مكان الملفّ <code>index.bundle</code> (عبر <code>NSURL</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> على أنّه اسم الوحدة (moduleName) الذي أنشأناه أعلاه عند كتابة شيفرة مكوّن React Native الخاص بنا.
+
لتسهيل التنقيح (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>‎‎ يبدأ بتشغيل آلة وهميّة 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>‎‎.
+
لاحظ أن المقطع ‎‎<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) على القرص عبر سطرٍ برمجيّ مثل ‎‎<code>[[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];</code>‎‎. يمكنك استخدام برمجيّة <code>react-native-xcode.sh</code> الموجودة في المسار ‎‎<code>node_modules/react-native/scripts/</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|مركز|لاإطار]]
+
[[ملف:react-native-add-react-native-integration-wire-up.png|مركز|لاإطار|720x720بك]]
من أسهل الطرق للقيام بهذا هي فتح العرض في لوحة العمل (storyboard) والنقر بزر الفأرة الأيمن على الرابط الجديد. حدّد حدثًا مثل حدث Touch Up Inside، واسحبه إلى لوحة العمل ثم حدّد العرض المُنشَأ من القائمة المتوفرة.
+
من أسهل الطرق للقيام بهذا هي فتح الواجهة في لوحة العمل (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|مركز|لاإطار|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 هذا التعليق] حلًّا ممكنًا.
 
====اطلع على الشيفرة====
 
يمكنك فحص الشيفرة التي أضافت شاشة React Native إلى تطبيقنا النموذجي على [https://github.com/JoelMarcey/iOS-2048/commit/9ae70c7cdd53eb59f5f7c7daab382b0300ed3585 GitHub].
 
  
 
====ماذا بعد؟====
 
====ماذا بعد؟====
 
يمكنك الآن متابعة تطوير تطبيقك كالمعتاد. راجع توثيق [[ReactNative/debugging|التنقيح]] و<nowiki/>[[ReactNative/running on device|النشر]] لمعرفة المزيد حول العمل مع React Native.
 
يمكنك الآن متابعة تطوير تطبيقك كالمعتاد. راجع توثيق [[ReactNative/debugging|التنقيح]] و<nowiki/>[[ReactNative/running on device|النشر]] لمعرفة المزيد حول العمل مع React Native.
  
==الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Swift==
 
==الدمج مع تطبيقات Android قائمة مكتوبة بلغة Java==
 
  
 
== مصادر ==
 
== مصادر ==
* [https://facebook.github.io/react-native/docs/integration-with-existing-apps صفحة Integration with Existing Apps في توثيق React Native الرسمي.]
+
* [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

المفاهيم الرئيسية

أهمّ الأمور لدمج مكونات React Native في تطبيق Android الخاص بك هي:

  1. إعداد اعتماديات (dependencies) إطار العمل React Native وهيكل المجلّدات (directory structure).
  2. كتابة مكونات React Native باستخدام JavaScript.
  3. إضافة واجهة ReactRootView إلى تطبيق Android الخاص بك. ستكون هذه الواجهة بمثابة الحاوية لمكون React Native الخاص بك.
  4. بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل.
  5. تحقّق من أن جانب 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 من خادم التطوير مع عرض الشاشة التالية:

EmbeddedAppAndroid.png

إنشاء بناء إصدار (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 الخاص بك هي:

  1. إعداد اعتماديات (dependencies) إطار العمل React Native وهيكل المجلّدات (directory structure).
  2. فهم مكونات React Native التي ستستخدمها في تطبيقك.
  3. إضافة هذه المكوّنات كاعتماديّاتٍ باستخدام CocoaPods.
  4. كتابة مكونات React Native باستخدام JavaScript.
  5. إضافة واجهة RCTRootView إلى تطبيق iOS الخاص بك. ستكون هذه الواجهة بمثابة الحاوية لمكون React Native الخاص بك.
  6. بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل.
  7. تحقّق من أن جانب 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.

react-native-existing-app-integration-ios-before.png

أدوات سطر الأوامر لبيئة 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.

react-native-add-react-native-integration-link.png
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. الربط

اربط الرابط الجديد في القائمة الرئيسية بتابعِ معالجِ الأحداث المضاف حديثًا.

react-native-add-react-native-integration-wire-up.png

من أسهل الطرق للقيام بهذا هي فتح الواجهة في لوحة العمل (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-add-react-native-integration-example-home-scree.png

هذه شاشة مكوّن React Native:

react-native-add-react-native-integration-example-high-score.png

إذا واجهتك مشكلة في تقرير الوحدات (module resolution) عند تشغيل تطبيقك، فيرجى الاطلاع على هذه الصفحة للحصول على معلوماتٍ قد تساعدك على حل مشكلتك. يبدو هذا التعليق حلًّا ممكنًا.

ماذا بعد؟

يمكنك الآن متابعة تطوير تطبيقك كالمعتاد. راجع توثيق التنقيح والنشر لمعرفة المزيد حول العمل مع React Native.

الدمج مع تطبيقات iOS قائمة مكتوبة بلغة Swift

المفاهيم الرئيسية

أهمّ الأمور لدمج مكونات React Native في تطبيق iOS الخاص بك هي:

  1. إعداد اعتماديات (dependencies) إطار العمل React Native وهيكل المجلّدات (directory structure).
  2. فهم مكونات React Native التي ستستخدمها في تطبيقك.
  3. إضافة هذه المكوّنات كاعتماديّاتٍ باستخدام CocoaPods.
  4. كتابة مكونات React Native باستخدام JavaScript.
  5. إضافة الواجهة RCTRootView إلى تطبيق iOS الخاص بك. ستكون هذه الواجهة بمثابة الحاوية لمكون React Native الخاص بك.
  6. بدء تشغيل خادم React Native وتشغيل التطبيق الأصيل.
  7. تحقّق من أن جانب 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.

react-native-existing-app-integration-ios-before.png

أدوات سطر الأوامر لبيئة Xcode

لتثبيت أدوات سطر الأوامر (Command Line Tools). اختر "Preferences...‎" في قائمة Xcode. انتقل إلى لوحة Locations وثبّت الأدوات عن طريق تحديد أحدث إصدارٍ في قائمة Command Line Tools المنسدلة.

GettingStartedXcodeCommandLineTools.png

إعداد اعتماديات 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.

react-native-add-react-native-integration-link.png
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. الربط

اربط الرابط الجديد في القائمة الرئيسية بتابعِ معالجِ الأحداث المضاف حديثًا.

react-native-add-react-native-integration-wire-up.png

من أسهل الطرق للقيام بهذا هي فتح الواجهة في لوحة العمل (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-add-react-native-integration-example-home-scree.png

هذه شاشة مكوّن React Native:

react-native-add-react-native-integration-example-high-score.png

إذا واجهتك مشكلة في تقرير الوحدات (module resolution) عند تشغيل تطبيقك، فيرجى الاطلاع على هذه الصفحة للحصول على معلوماتٍ قد تساعدك على حل مشكلتك. يبدو هذا التعليق حلًّا ممكنًا.

ماذا بعد؟

يمكنك الآن متابعة تطوير تطبيقك كالمعتاد. راجع توثيق التنقيح والنشر لمعرفة المزيد حول العمل مع React Native.


مصادر