BUCK - 使用BUCK编译iOS项目(4)

2020-01-12  本文已影响0人  西西的一天

这一节中,将把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.00.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)
    }
...

大功告成!

上一篇下一篇

猜你喜欢

热点阅读