الدمج مع تطبيقات قائمة في React Native

من موسوعة حسوب

إطار العمل React Native رائعٌ لبرمجة تطبيق جوال جديد من البداية. لكن، يُمكنك كذلك استخدامه لإضافة عرضٍ (view) واحدٍ أو ميّزة إلى التطبيقات الأصيلة الموجودة بالفعل. يمكنك باتّباع بضعة خطواتٍ إضافةُ ميّزات جديدة باستخدام React Native إلى تطبيقك، مثل شاشات (screens) جديدة، وعروض، إلخ.

تختلف الخطوات حسب النظام الأساسي الذي تستهدفه:

الدمج مع تطبيقات 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": "node node_modules/react-native/local-cli/cli.js 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 كذلك (استبدل المقطع version_printed_above برقم النسخة المذكور في المُخرج، أي 16.2.0 في هذه الحالة):

yarn add react@version_printed_above

أنشأَ Yarn مجلدَ ‎‎/node_modules‎‎ جديد. يخزّن هذا المجلد جميع اعتماديات JavaScript المطلوبة لإنشاء مشروعك.

أضف ‎‎‎node_modules/‎‎ إلى ملفّ ‎‎.gitignore‎‎ الخاص بك.

3. تثبيت CocoaPods

CocoaPods هي أداةٌ لإدارة الحزم لتطوير تطبيقات iOS و macOS. سنستخدمها لإضافة شيفرة إطار React Native الفعليّة محليًا إلى مشروعك الحالي.

نوصي بتثبيت CocoaPods باستخدام Homebrew.

brew install cocoapods

ملاحظة: من الممكن تقنيًا الاستغناء عن أداة CocoaPods، ولكن هذا سيتطلب إضافة مكتباتٍ وروابط يدويًّا، ما سيُعقّد هذه العملية.

إضافة React Native إلى تطبيقك

لنفترض أن التطبيق الذي تُريد دمج مكونات جديدة معه هو لعبة 2048. إليك ما تبدو عليه القائمة الرئيسية للتطبيق الأصيل دون React Native.

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

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

إعداد اعتماديات CocoaPods

قبل دمج React Native مع تطبيقك، ستحتاج إلى تحديد أجزاء إطار React Native التي تريد دمجها مع التطبيق. سنستخدم CocoaPods لتحديد أي من هذهِ المواصفات الفرعيّة (subspecs) سيعتمد عليها تطبيقك.

قائمة المواصفات الفرعية المدعومة متوفّرة في الملفّ ‎‎/node_modules/react-native/React.podspec‎‎. وتُسمَّى عمومًا حسب وظيفتها. على سبيل المثال، ستحتاج دائمًا إلى المواصفة الفرعيّة Core. التي تحتوي على AppRegistry و StyleSheet و View وغيرها من المكتبات الرئيسية في إطار React Native. إذا كنت ترغب في إضافة مكتبة Text الخاصة بإطار React Native (لاستخدام عناصر <Text> على سبيل المثال)، فستحتاج إلى المواصفة الفرعيّة RCTText. إذا كنت ترغب باستخدام مكتبة Image (لعناصر <Image> مثلًا)، فستحتاج إلى المواصفة الفرعيّة RCTImage.

يمكنك تحديد المواصفات الفرعية التي سيعتمد عليها تطبيقك في ملف Podfile. أسهل طريقة لإنشاء ملفّ Podfile هي تنفيذ الأمر ‎‎init‎‎ الخاص بأداة CocoaPods في المجلد الفرعي‎‎/ios‎‎ الخاص بمشروعك:

pod init

سوف يحتوي ملف Podfile على إعداد نموذجيّ (boilerplate setup) يُمكنك تعديله حسب طريقة الدمج التي تريدها. يجب أن يبدو ملفّ Podfile مشابهًا لهذا في النهاية:

# 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 '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
    'RCTAnimation', # Needed for FlatList and animations running on native UI thread
    # 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.26.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';

class RNHighScores extends React.Component {
  render() {
    var contents = this.props['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,
  },
});


// اسم الوحدة
AppRegistry.registerComponent('RNHighScores', () => RNHighScores);

RNHighScores هو اسم وحدتك الذي سيُستخدَم عند إضافة عرض إلى React Native من داخل تطبيق iOS الخاص بك.

الرابط العجيب: ‎‎RCTRootView‎‎

الآن، وبعد إنشاء مكون React Native الخاص بك عبر ملفّ index.js، تحتاج إلى إضافة هذا المكون إلى مُتحكّم ViewController جديد أو موجود مسبقا. أيسر سبيل إلى ذلك هو إنشاء مسار حدث (event path) اختياريًّا يُشير إلى المكون الخاص بك، ثم إضافة هذا المكون إلى ViewController موجود.

سنربط مكوّن React Native الخاص بنا مع عرض أصيل جديد في متحكّم ViewController، والذي سيستضيف المكوّن. العرضُ الأصيل يُسمّى RCTRootView.

1. إنشاء مسار حدث (Event Path)

يمكنك إضافة رابط جديد في قائمة اللعبة الرئيسية للانتقال إلى صفحة "High Score" المبنيّة بإطار React Native.

2. معالج الأحداث

سنضيف الآن معالج أحداثٍ من رابط القائمة. سيُضاف تابع إلى متحكّم ViewController الرئيسي للتطبيق الخاص بك. هنا سنعتمد على عرض RCTRootView.

عند إنشاء تطبيق React Native، يُستخدَم مُحزِّم React Native لإنشاء ملفّ ‎‎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/‎‎ لإنشاء هذا الملف المُحزّم مسبقًا.

الربط

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

من أسهل الطرق للقيام بهذا هي فتح العرض في لوحة العمل (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 أصيل وشغّله كالمعتاد. أو يمكنك تشغيل التطبيق من سطر الأوامر عبر تنفيذ الأمر التالي من المجلّد الجذر لمشروعك :

$ react-native run-ios

سترى الآن في تطبيقنا، رابطًا يشير إلى شاشة "High Scores"، وعند النقر فوقه، سترى مكوّن React Native الخاص بك.

هذه هي الشاشة الرئيسيّة للتطبيق الأصيل:

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

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

اطلع على الشيفرة

يمكنك فحص الشيفرة التي أضافت شاشة React Native إلى تطبيقنا النموذجي على GitHub.

ماذا بعد؟

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

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

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

مصادر