原生 iOS 项目集成 React Native

创建一个 React Native 项目并写一个纯的 React Native 应用可以参考官方指南

Android 项目集成 React Native 可以参考 原生 Android 项目集成 React Native

本文主要介绍原生 iOS 项目集成 React Native 并用于部分页面开发的流程。开发环境为 macOS 10.12、Xcode 8.0、React Native 0.35.0。而官方给出的 植入原生 iOS 应用指南 只对应到 0.28 版本。最新版(当前为 0.35)的集成方案稍微有些变动。

0. 安装 CocoaPods

iOS 开发者可以跳过这一步。

0.0 使用 rvm 安装/更新 ruby 环境

安装 cocoapods 对 ruby 版本有要求

$ curl -L https://get.rvm.io | bash -s stable
$ source ~/.rvm/scripts/rvm
// 查看远程 ruby 版本
$ rvm list known
// 查看本地 ruby 版本
$ rvm list
$ rvm install 2.3.0

0.1 使用 gem 安装 cocoapods

如要修改 gem 的镜像地址

$ gem sources -l
// 删除已有的源地址
$ gem sources -r https://rubygems.org/
// 添加需要的源地址
$ gem sources -a https://ruby.taobao.org/
$ gem sources -l

安装 cocoapods

$ gem install cocospods
$ cd ~/.cocoapods/repos/
$ git clone https://github.com/CocoaPods/Specs.git master

顺利的话安装 ruby 和 cocoapods 两步都会成功,如果失败了可以针对具体问题去网上搜索相关教程或解决方案。这里不详述。

1. 创建/修改 iOS 项目

集成 React Native 要求 iOS 系统版本不小于 7.0。

2. 添加 package.json

在 iOS 项目根目录新建文件 package.json,内容如下(参考 react-native init 生成的 package.json 文件)

{
  "name": "react-native-sample",
  "version": "0.0.1",
  "description": "sample of react native embedding ios",
  "main": "index.ios.js",
  "private": true,
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  },
  "author": "danke77",
  "license": "ISC",
  "dependencies": {
    "react": "^15.3.2",
    "react-native": "^0.35.0"
  },
  "devDependencies": {
  }
}

执行 npm install 就可以安装 dependencies 下的 npm 组件了。

这个时候在 iOS 项目根目录就生成了 node_modules/ 文件夹,里面就是一些用到的组件。

.gitignore 中添加

# node.js
node_modules/
npm-debug.log

执行 react-native upgrade 可以更新已有组件。

3. 添加 index.ios.js

在 iOS 项目根目录创建目录 js/,js 相关的代码就放在这个文件夹下。

js/ 下添加 App.js,内容如下

import React, { Component } from 'react'
import { View, Text, StyleSheet } from 'react-native'

export default class extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.text}>
          Hello React Native!
        </Text>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#ffffff'
  },
  text: {
    fontSize: 20,
    color: '#333333'
  }
})

在 iOS 项目根目录新建文件 index.ios.js,内容如下

import { AppRegistry } from 'react-native'
import App from './js/App'

AppRegistry.registerComponent('navigation', () => App)

这里的 navigation 一般会根据模块功能命名,后面还会用到。

当然也可以把 App.js 的内容写在 index.ios.js 里,但这样写更清晰一些,尤其是项目大了文件多的情况。

4. 用 cocoapods 集成 React Native

在 iOS 项目根目录的 Podfile 文件(没有则创建)添加 React Native 相关内容

target 'HelloReactNative' do
platform :ios, '7.0'

source 'https://github.com/CocoaPods/Specs.git'

# 这里的 :path 内容取决于 node_modules/ 实际所在的位置
pod 'React', :path => ‘./node_modules/react-native', :subspecs => [
    'Core',
    'RCTText',
    'RCTImage',
    'RCTNetwork',
    'RCTWebSocket', # needed for debugging
    # Add any other subspecs you want to use in your project
]

要在这里添加项目需要的依赖,如要使用 React Native 的 Text 则必须要添加 RCTText 依赖(pod 'React/RCTText')。

然后在根目录执行 pod install

5. React Native 相关的 ViewController

创建一个用于容纳 React Native 组件的 ViewController,并添加 RCTRootView,它会把 React Native 组件解析成原生的 UIView。

#import "HelloReactViewController.h"
#import <RCTRootView.h>

@interface HelloReactViewController ()

@end

@implementation HelloReactViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
#ifdef DEBUG
    NSURL * jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"];
#else
    NSURL * jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"bundle/index.ios" withExtension:@"bundle"];
#endif
    
    RCTRootView * rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                         moduleName:@"navigation"
                                                  initialProperties:nil
                                                      launchOptions:nil];
                                                                                                     
    self.view = rootView;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

jsCodeLocation 是 React Native 资源加载的路径,可以通过网络加载本地的资源文件(主要用于本地调试),或者将其打包成 js bundle 文件(用于发布正式包)。

moduleName 对应 React Native 组件的入口,必须和前面的 AppRegistry.registerComponent('navigation', () => App) 里的 navigation 对应。

6. 启动服务

debug 模式下需要启动 package server,在 package.json 所在目录(一般为项目根目录)下执行 npm start,它等效于 package.jsonscripts 下的 node node_modules/react-native/local-cli/cli.js start,相当于启动一个本地服务。

Terminal 显示如下表示服务已正常启动

> react-native-module@0.0.1 start /Users/danke77/Projects/react-native/HelloReactNative
> node node_modules/react-native/local-cli/cli.js start

Scanning 581 folders for symlinks in /Users/danke77/Projects/react-native/HelloReactNative/node_modules (17ms)
 ┌────────────────────────────────────────────────────────────────────────────┐
 │  Running packager on port 8081.                                            │
 │                                                                            │
 │  Keep this packager running while developing on any JS projects. Feel      │
 │  free to close this tab and run your own packager instance if you          │
 │  prefer.                                                                   │
 │                                                                            │
 │  https://github.com/facebook/react-native                                  │
 │                                                                            │
 └────────────────────────────────────────────────────────────────────────────┘
Looking for JS files in
   /Users/danke77/Projects/react-native/HelloReactNative

[2016-10-17 17:06:48] <START> Building Dependency Graph
[2016-10-17 17:06:48] <START> Crawling File System
[Hot Module Replacement] Server listening on /hot

React packager ready.

[2016-10-17 17:06:49] <END>   Crawling File System (966ms)
[2016-10-17 17:06:49] <START> Building in-memory fs for JavaScript
[2016-10-17 17:06:49] <END>   Building in-memory fs for JavaScript (260ms)
[2016-10-17 17:06:49] <START> Building in-memory fs for Assets
[2016-10-17 17:06:50] <END>   Building in-memory fs for Assets (138ms)
[2016-10-17 17:06:50] <START> Building Haste Map
[2016-10-17 17:06:50] <START> Building (deprecated) Asset Map
[2016-10-17 17:06:50] <END>   Building (deprecated) Asset Map (104ms)
[2016-10-17 17:06:50] <END>   Building Haste Map (428ms)
[2016-10-17 17:06:50] <END>   Building Dependency Graph (1825ms)

7. 开发调试

模拟器上可以通过工具栏的 Hardware->Shake Gesture 或快捷键调出开发调试菜单;真机上可以通过摇一摇调出。

8. 发布正式包

React Native 的开发版需要有一个 package server 随时发送更新后的 js bundle 文件。如果要打正式包,需要把 js bundle 文件保存到 iOS 项目的目录下。这样,正式包就不需要 server 支持了,可独立运行。

在根目录下创建 bundle/ 文件夹,执行以下命令将 js bundle 保存到资源目录下

$ react-native bundle --platform ios --dev false --entry-file index.ios.js --bundle-output ./bundle/index.ios.bundle --assets-dest ./bundle

bundle/ 下就会生成 index.ios.bundle 文件及 assets/ 文件夹,后者会放 React Native 中用到的资源如图片等。

然后将生成的 bundle/ 文件夹以 Create folder references 的形式导入到工程里,就可以打正式包了。

本文是 慌不要慌 原创,发表于 https://danke77.github.io/,请阅读原文支持原创 https://danke77.github.io/2016/10/19/react-native-embedding-ios/,版权归作者所有,转载请注明出处。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 想了很久,要先介绍各种组件的实际应用好,还是先介绍怎么把React Native集成到原生项目好。因为想起,一旦开...
    朱_源浩阅读 22,668评论 84 129
  • 核心概念 如果你正准备从头开始制作一个新的应用,那么React Native会是个非常好的选择。但如果你只想给现有...
    made_China阅读 1,300评论 1 2
  • 小时候 你是过节的味道 我想啊想 盼啊盼 你的味道总让我馋 长大后 你是家乡的味道 我走啊走 望啊望 你的味道总让...
    春暖花开啦阅读 187评论 0 2
  • 口欲期,一岁以前,快感中心集中在口腔部位,这一阶段需要发展出基本的信任,也就是一个人由衷地相信,外部世界是愿意满足...
    尹二尹阅读 758评论 0 0