BUCK - 使用BUCK编译iOS项目(4)
这一节中,将把React Native集成入之前的项目中,集成React Native有两种方式:1. 为每一个RN的子pod编写一个BUCK文件;2. 将React Native编译成Framework,使用集成mars的方式进行集成。比较一下,方法一的工作量巨大,因为整个ReactNative项目被拆分为了多个pod,尽管官方提供部分pod的BUCK文件,但数量还是有点大,所以采用了方法二。
Build Framework
这里采用了Cocoapods的一个插件:cocoapods-packager,只需提供一个podspec文件即可通过该插件生成Framework,这个插件的使用方式网上有很多文章。插件的安装方式:gem install cocoapods-packager
。
这里使用podspec文件源于之前的一个项目,如何将RN作为一个Framework的方式引入。podspec文件如下所示,其中的这些依赖源自一个私有Cocoapods仓库。
Pod::Spec.new do |s|
s.name = 'RNContainer'
s.version = '0.1.0'
s.summary = 'React Native Container'
s.description = 'Description of RNContainer'
s.homepage = 'https://github.com/iossocket/RNContainer'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'iossocket' => 'avx302@gmail.com' }
s.source = { :git => 'https://github.com/iossocket/RNContainer.git', :tag => s.version.to_s }
s.ios.deployment_target = '10.0'
s.source_files = 'RNContainer/Classes/**/*.{h,m,mm}'
s.public_header_files = 'RNContainer/Classes/RNContainer.h'
s.static_framework = true
s.libraries = 'stdc++'
s.frameworks = 'Accelerate', 'AudioToolbox', 'CoreTelephony', 'JavaScriptCore', 'MobileCoreServices', 'SystemConfiguration'
s.dependency 'FBLazyVector'
s.dependency 'FBReactNativeSpec'
s.dependency 'RCTRequired'
s.dependency 'RCTTypeSafety'
s.dependency 'React'
s.dependency 'React-Core'
s.dependency 'React-CoreModules'
s.dependency 'React-Core/DevSupport'
s.dependency 'React-RCTActionSheet'
s.dependency 'React-RCTAnimation'
s.dependency 'React-RCTBlob'
s.dependency 'React-RCTImage'
s.dependency 'React-RCTLinking'
s.dependency 'React-RCTNetwork'
s.dependency 'React-RCTSettings'
s.dependency 'React-RCTText'
s.dependency 'React-RCTVibration'
s.dependency 'React-Core/RCTWebSocket'
s.dependency 'React-cxxreact'
s.dependency 'React-jsi'
s.dependency 'React-jsiexecutor'
s.dependency 'React-jsinspector'
s.dependency 'ReactCommon/jscallinvoker'
s.dependency 'ReactCommon/turbomodule/core'
s.dependency 'Yoga'
s.dependency 'DoubleConversion'
s.dependency 'glog'
s.dependency 'Folly'
end
准备就绪,便可使用插件进行编译打包了,在podspec的同级目录下执行如下命令:
pod package RNContainer.podspec --force --embedded --no-mangle --spec-sources=https://github.com/iossocket/ReactNativeLib.git,https://github.com/CocoaPods/Specs.git
注意,这里的sources指定了两个,一个是私有仓库,一个官方仓库。force:强制覆盖之前的build产物;embedded:指明要生成的Framework为static;no-mangle可以详解文档。
运行成功后,会在该目录下生成一个目录RNContainer-0.1.0
,0.1.0
是podspec中声明的版本号,在这个目录中便可看到生成Framework。
集成进入BUCK
在MyBuckSample-iOS的Libraries目录下创建两个Target,一个是刚刚生成好的Framework,创建一个目录rncontainer,把RNContainer.framewrok拷贝到该目录,并创建一个BUCK文件:
load("//Config:buck_rule_macros.bzl", "apple_lib")
prebuilt_apple_framework(
name = "RNContainer",
framework = "RNContainer.framework",
preferred_linkage = "shared",
visibility = ["PUBLIC"],
)
# Every BUCK file needs at least one library with source.
apple_lib(
name = "PreBuildProjectGeneratorHackRN",
srcs = glob([
"BuckSupportFiles/**/*.swift",
]),
)
其中PreBuildProjectGeneratorHackRN
的目的,可详见之前集成mars的文章。
另一个Target用于放置资源文件js.bundle
react-native init App --version 0.61.2
# 修改一下index.js,简单起见没有引入其他图片资源
react-native bundle --platform='ios' --entry-file='index.js' --dev=false --minify=true --bundle-output="./build/ios/main.jsbundle" --assets-dest="./build/ios"
在Libraries目录下为这个资源target创建目录rnapp,将js.bundle拷贝进入,同时创建一个BUCK文件:
apple_resource(
name = "RNResource",
visibility = ["//App:"],
files = glob(["**/*.jsbundle"]),
)
到此为止两个Target已经创建完毕,检验一下所有的target是否都可以正常被引用:make targets
buck targets //...
Action graph will be rebuilt because files have been added or removed.
//App:Assets
//App:MyBuckSampleApp
//App:MyBuckSampleAppBinary
//App:MyBuckSampleAppPackage
//App:Resource
//App:workspace
//Libraries/ObjcLib:ObjcLib
//Libraries/mars:PreBuildProjectGeneratorHack
//Libraries/mars:mars
//Libraries/rnapp:RNResource
//Libraries/rncontainer:PreBuildProjectGeneratorHackRN
//Libraries/rncontainer:RNContainer
//Libraries/xlog:xlog
//Pods:Alamofire
在主App中使用RNContainer
为了让主App可以使用这新加入的target,需要修改主App的依赖,同时由于React Native的需要,加入了一下系统库的依赖:
...
deps = [
":Resource",
":Assets",
"//Pods:Alamofire",
"//Libraries/ObjcLib:ObjcLib",
"//Libraries/xlog:xlog",
"//Libraries/mars:PreBuildProjectGeneratorHack",
"//Libraries/rnapp:RNResource",
"//Libraries/rncontainer:PreBuildProjectGeneratorHackRN",
"//Libraries/rncontainer:RNContainer"
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/SystemConfiguration.framework",
"$SDKROOT/System/Library/Frameworks/CoreTelephony.framework",
"$SDKROOT/System/Library/Frameworks/Accelerate.framework",
"$SDKROOT/System/Library/Frameworks/AudioToolbox.framework",
"$SDKROOT/System/Library/Frameworks/JavaScriptCore.framework",
"$SDKROOT/System/Library/Frameworks/MobileCoreServices.framework",
]
...
修改ViewController,让它来弹出刚添加的一个RN页面
...
import RNContainer
...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let vc = RNContainer().viewController(byRoute: "App")
present(vc, animated: true, completion: nil)
}
...
大功告成!