React Native iOS本地私有库实践

起因

Facebook官方对react-native(下面简称RN)在iOS端的集成方式为本地Pod库,这种方式有两个缺点:

  1. 项目更新RN依赖版本较为繁琐,需要对项目中的node_modules文件夹进行文件操作,这种方式不利于模块化和版本管理

  2. 对pod形式的库项目支持不友好,pod库的依赖只能支持name+version的形式,无法支持本地源码形式的依赖

综上,需要在公司私有Pod仓库中添加对RN的支持。

一些尝试

常规方式

常规方式Pod库的常规操作方式为先lint再push,这种方式在RN上会遇到很多错误,解决了一批错误后最终停止在一个x86_64的错误上:Undefined symbols for architecture x86_64,这是一个比较常见的编译错误,但是尝试了很多方式后仍然无法解决。这里记录下关键问题,供后续参考:

  • lint和push的时候需要增加几个关键选项:
1
2
3
--use-libraries		#不加会报一些c++库找不到的错误
--allow-warnings #不加只有warning也无法lint通过
--verbose #增加一些关键信息
  • 如果对本地仓库有依赖,sources要指定本地私有仓库,不然默认是查找Cocoapods官方仓库

曲线方式

另外一个思路是考虑先把RN整体打成framework,再集成到Pod库中。这种方式最终停止在一个错误:invalid bitcode signature,跟上面的问题一样,没有搞定。

手动操作Spec仓库方式

在没有思路的时候,在google尝试搜索关键词react-native podspec,发现一篇文章:私有Pods集成react-native库,里面分享通过pod ipc spec命令将.podspec文件转成.podspec.json格式文件,然后将代码和.podspec.json分别提交到代码仓库和Pod仓库的方式,绕过lint和push的常规步骤。

这里需要注意的是,代码提交到私有源码库和podspec提交到私有Pod库是互相独立的操作。可以只提交Podspec到私有Pod库,但是代码提交到到私有源码库有两个好处

1
2
1. 内部网络更新速度快
2. 可以修改代码后自定义版本

尝试了一下,虽然遇到一些编译错误,但不是整体编译不过,而是一些RN内部类找不到,说明至少RN放到Pod仓库了。沿着这个思路往下探索,最终解决。

其中RN依赖了一些第三方库,包括folly、yoga、glog等,对于RN依赖的第三方库,处理原则是如果在官方Pod仓库没有对应的版本,都需要以私有Pod的方式提供。具体需要处理哪些库,需要根据当前RN版本来判断(例如Folly的2016.10.31.00在官方库没有,但是2016.09.26.00却能支持)。

操作步骤

  1. https://github.com/facebook/react-native/tags下载对应版本的RN源码包,解压缩后将源码提交到本地仓库`git@gitlab.jinhui365.cn:mobile/react-native.git` master分支。这里需要注意的问题是要保证该版本所有文件都会提交,包括.开头的文和文件夹。
  2. 在gitlab上为新版本打一个tag,tag名称约定为v{版本号},例如版本号为0.57.4,则tag名称为v0.57.4。
  3. 进入源码根目录,执行pod ipc spec React.podspec >> React.podspec.json。这里需要注意的是不要污染本地源码仓库,可以在另外一份源码拷贝上操作。
  4. 打开React.podspec.json文件,将source字段修改为本地的RN源码仓库地址和对应地址

    1
    2
    3
    4
    "source": {
    "git": "git@gitlab.jinhui365.cn:mobile/react-native.git",
    "tag": "v0.57.4"
    }
  5. 将修改后的React.podspec.json放到私有Pod仓库对应的目录下提交。例如React/0.57.4/React.podspec.json

  6. 在源码根目录下,进入ReactCommon/yoga,执行pod ipc spec yoga.podspec >> yoga.podspec.json
  7. 打开yoga.podspec.json,将source字段修改为本地的RN源码仓库地址和对应地址,并将source_files字段修改为"ReactCommon/yoga/**/*.{cpp,h}",public_header_files字段修改为:"ReactCommon/yoga/yoga/{Yoga,YGEnums,YGMacros}.h"

  8. 将修改后的yoga.podspec.json放到私有Pod仓库对应的目录下提交。例如yoga/0.57.4.React/yoga.podspec.json

  9. 本地址行pod repo update {仓库名}

  10. 至此,可以在项目中通过name+version的形式引用RN的pod依赖。

0.53.3的问题

RN在0.53.3上有一些编译问题,李杨在React-Native混编学习#iOS环境搭建 提到过相关解决方案,已按照该方案经将源码修改后tag为0.53.4。可以通过引用0.53.4来规避这些问题。

私有库依赖私有库

金汇移动端目前负责的业务较多,不同的业务之间会复用工具库(如路由库、日志库等)。同一个业务功能,有可能需要在不同的App之间复用(如民工汇、支付SDK)。因此,依赖的路径为APP->业务库->非业务库。

iOS的依赖管理与Android不同。Android统一使用gradle文件来管理依赖,而iOS主项目使用Podfile管理,库项目使用podspec管理,并且库项目无法依赖本地文件。

因此新建了私有库PodLibTest来测试了下,编译通过。这里记录下常见命令:

1
2
3
4
5
pod lib create PodLibTest	#创建pod库项目
pod update --no-repo-update #更新当前pod项目依赖
pod repo update {pod仓库名称} #更新本地pod仓库
pod lib lint --use-libraries --allow-warnings --verbose --sources=git@gitlab.jinhui365.cn:iOS/JHJRSpecs.git,https://github.com/CocoaPods/Specs.git #这些参数上面提到过,比较关键
pod repo push JHJR PodLibTest.podspec --use-libraries --allow-warnings --verbose #同样关注下参数

总结

  1. Cocoapods在依赖管理方面不够完善,提升了iOS工程化的难度等级。因此需要iOS工程师对相关领域知识研究地更深入,才能达到与Android同水平的工程化程度。

  2. 面对非常见疑难问题,通过简单的关键词搜索可能不太好找到解决方案。需要从两个方面着手,一方面多尝试关键词组合,找到和你遇到同样问题的人写的文章或帖子;一方面深入研究官方文档货项目源码,从原理层着手分析问题。