如何在已有的iOS APP原生代码中整合React Native


核心概念#

React Native is great when you are starting a new mobile app from scratch. However, it also works well for adding a single view or user flow to existing native applications. With a few steps, you can add new React Native based features, screens, views, etc.

React Native去开发一个全新的手机App是个很好的选择,并且React Native也可以和原生App整合。用几个步骤,你就可以将React Native的基础特性,视图,窗体等控件加入到现有原生App中。

iOS App中整合RN组件的关键点如下:

理解需要整合的React Native组件。

为所有需要整合的React Native组件创建带有subspecsPodfile

Javascript中创建你的React Native组件。

增加一个新的事件句柄,该句柄用于创建一个RCTRootView,该RCTRootView用于指向你的React Native组件,事件句柄的AppRegistry名称需要在index.ios.js中定义。

启动React Native server,并运行原声应用。

选择性的增加更多的React Native组件。

调试

准备发布(比如:运行脚本 react-native-xcode.sh

正式发布



先决条件


总述

首先,按照Getting Started guide把开发环境搞定,先让React NativeiOS上能跑起来。


CocoaPods 

CocoaPods是一个打包管理工具,支持MACiOS的开发。我们使用它用于在本地增加一个React Native framework的代码到我们现有的工程中。

$ sudo gem install cocoapods


从技术上讲,不使用CocoaPods是可能的,但是这需要手动链接新增的静态库。


示例App 

假设需要整合的APP2048这个游戏。下面是一个 不包含React Native的原生应用的主菜单。



包依赖#

React Native 整合需要ReactReact Native节点模块。React Native Framework将提供代码以便于你的应用整合可以执行。


package.json #

我们可以增加依赖package.json文件的包。如果这个包在你的工程的根目录不存在,则创建该文件。

通常在React Navie工程的根目录中,你将放入一些像package.jsonindex.ios.js这样的文件,并且在形如 ios/的子目录中增加仅在iOS上运行的原生代码,这些代码在本地运行。

下面是一个最简单的的package.json的例子。

版本号按照你的需要写入,通常用最新的ReactReact Native版本号。

{

  "name": "NumberTileGame",

  "version": "0.0.1",

  "private": true,

  "scripts": {

    "start": "node node_modules/react-native/local-cli/cli.js start"

  },

  "dependencies": {

    "react": "15.0.2",

    "react-native": "0.26.1"

  }

}


包安装#

通过Node package manager安装ReactReact Native模块。Node模块需要被装到你的工程的根目录的node_modules/子路径中。


#在包含package.json的工程中,安装模块至node_modules/文件夹

$ npm install


React Native Framework #

按照上面的命令安装React Native Framework到你的工程中。我们将安装CocoaPods Podfile,其中携带你希望使用的组件。


Subspecs #

在你整合React Native到你的应用之前,你需要决定那部分React Native Framework你希望去整合。这就是需要引入subspecs的地方。当你创建你的Podfile的时候,你需要指定使用并依赖哪些React Native库,每个库将变成在Podfile中的一个subspec


需要支持的subspecs列表可在node_modules/react-native/React.podspec中查看。他们大都按照功能命名。比如,我们一直要使用的Core subspec。它可以提供AppRegistryStyleSheetView和其他核心React Native库。如果你要增加React NativeText库,即支持<Text>元素,这时你需要RCTText subspec。如果你需要Image库,即支持<Image>元素,这时你需要RCTImage subspec


Podfile #

在你使用Node去安装React React Native frameworksnode_modules路径之后,并且你已经决定你需要整合哪些React Native元素,这时你需要准备去创建Podfile了,以便于你可以安装这些组件到你的应用中。

最简单的方法去创建一个Podfile是通过是在原生iOS代码路径中用CocoaPods init命令创建。

## In the directory where your native iOS code is located (e.g., where your `.xcodeproj` file is located)

$ pod init


Podfile将被创建和保存在iOS的当前工程路径(ios/)中,并且将包含一个样本文件设定,用于微调你的整合目的。最后,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',

    'RCTText',

    'RCTWebSocket', # needed for debugging

    # Add any other subspecs you want to use in your project

  ]


end


Pod 安装#

在你创建完毕Podfile后,你就可以准备安装React Native pod了。

$ 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.


Code 整合#

现在我们有一个基础包了,我们将实现修改原生应用,将React Native整合到应用中。对于我们的2048应用,我们将在React Native中增加一个"High Score”屏幕。


React Native 组件#

我们将写的第一段代码是用React Native写的新的"High Score”屏幕,这将被整合入你的应用。


创建index.ios.js文件#

首先,创建一个空的index.ios.js文件。为了方便,我们将该文件置于工程根目录。index.ios.jsiOSReact Native应用的起点,并且该文件是必须的。该文件可以是一个小文件,用于表示你的React Native组件或者应用需要哪些其它的文件,或者它可以包含所需的所有代码。


# In root of your project

$ touch index.ios.js


增加React Native代码#

在你的ndex.ios.js文件中创建你的组件,这里的例子中,我们在<View>中添加<Text>组件。


'use strict';


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,

  },

});


// Module name

AppRegistry.registerComponent('RNHighScores', () => RNHighScores);


RNHighScores 是你使用React Native增加的一个视图模块名称。


关于RCTRootView#

现在在index.ios.js中创建你的React Native组件,你需要添加组件至一个新建的或者是已存在的ViewController中。最简单的办法是任意创建一个事件路径到你的组件中,然后将这个组件加到ViewController中。下一步,我们将React Native组件和一个在ViewController中的新建的原生视图绑定,该ViewController被命名为RCTRootView


创建事件路径#

You can add a new link on the main game menu to go to the "High Score" React Native page.

你可以在主游戏菜单中增加一个新链接便于跳转到"High Score"这个React Native页面。



事件句柄#

我们将在菜单链接中增加一个事件句柄。在应用的ViewController中增加一个方法。这时,RCTRootView就开始起作用了。


当你建立一个React Native应用,你可以使用React Native打包器去创建index.ios.bundle,用于对接React Native server。因此,我们需要通过NSURLRCTRootView指向index.ios.bundle,并且将它和该模块绑定。 


处于便于调试的目的,我们要在时间句柄被唤醒时打log。然后,在本地的React Native代码中创建一个字符串,这些代码在index.ios.bundle文件中。


最后,创建主窗体RCTRootView。在写React Native组件代码的时候,注意我们是如何提供RNHighScores作为模块名的。


#import "RCTRootView.h"


initialProperties是保存一些初始化数据的,后续可以在代码中用this.props获取这些数据。


- (IBAction)highScoreButtonPressed:(id)sender {

    NSLog(@"High Score Button Pressed");

    NSURL *jsCodeLocation = [NSURL

                             URLWithString:@"http://localhost:8081/index.ios.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];

}


注意RCTRootViewinitWithURL会启动一个新的JSCJavaScriptCore)虚拟机。保存资源和简化不同的RN窗体间的通信,你可以有很多RN窗体,这些窗体与单一JS执行时间相关。为此,可以用RCTBridge initWithBundleURL 创建一个桥,用于代替RCTRootViewinitWithURL


当你的应用要发布的时候,NSURL可以指向一个预打包文件,类似这样:

[[NSBundle mainBundle] URLForResource:@"main"

withExtension:@"jsbundle"];. 

你可以在node_modules/react-native/packager/路径中使用react-native-xcode.sh脚本去生产一个预打包文件。



连接#

连接主菜单中的新链接和新增加的事件句柄方法。


一个较为简便的方法是打开storyboard,右击新链接。选择类似Touch Up Inside事件,拖动到storyboard中,然后选择在列表中创建方法。


测试你的整合应用#

你已经完成了所有步骤去整合RN和你的APP。现在我们开始用RN打包器去建立index.ios.bundle包,便于localhost的服务器去加载。


App的通信安全#

Apple has blocked implicit cleartext HTTP resource loading. So we need to add the following our project's Info.plist (or equivalent) file.

Apple公司已经关闭了隐式明文HTTP资源加载的接口。因此,我们需要在Info.plist文件中增加下面这段内容。


<key>NSAppTransportSecurity</key>

<dict>

    <key>NSExceptionDomains</key>

    <dict>

        <key>localhost</key>

        <dict>

            <key>NSTemporaryExceptionAllowsInsecureHTTPLoads</key>

            <true/>

        </dict>

    </dict>

</dict>


运行 Packager#

# From the root of your project, where the `node_modules` directory is located.

$ npm start


运行 App#

如果你使用Xcode或者其它代码编辑器,你可以像以往一样建立和运行你的原生iOS APP。或者,你可以用下面这种命令行来运行APP


# From the root of your project

$ react-native run-ios


在我们的示例应用中,你可以看到"High Scores”的链接,然后当你点击链接的时候,RN组件将会被渲染。


原生应用的 home 视图:


React Native 的high score视图:


如果你在整合的时候遇到获取模块的问题,可以参考下面的链接:

https://github.com/facebook/react-native/issues/4968


源代码

你可以参考一下整合相关的源代码GitHub.


原文链接:

http://facebook.github.io/react-native/releases/0.33/docs/integration-with-existing-apps.html#subspecs

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值