iOS CocoaPods組件平滑二進(jìn)制化解決方案

iOS CocoaPods組件平滑二進(jìn)制化方案及詳細(xì)教程

感謝"fly2never_寶貝別哭"羔沙《ち瑁可以使用cocoapods-packager這個(gè)插件來方便生成library(靜態(tài)庫(kù)建钥,動(dòng)態(tài)庫(kù)都可以)选调。

強(qiáng)烈建議生成framework骤公。

IS_SOURCE=1 pod package YTXChart.podspec --library --exclude-deps --spec-sources=http://gitlab.baidao.com/ios/ytx-pod-specs.git,https://github.com/CocoaPods/Specs.git

雖然有了這個(gè)方便的工具堕义,但是了解一下打包的過程也是好的坏瞄。

后記,有人問我為什么不改用Carthage鸠匀。

可以看看我寫的這一篇Carthage和iOS組件二進(jìn)制化

CocoaPods和Carthage設(shè)計(jì)目的不一樣。

我們的現(xiàn)在組織架構(gòu)有多個(gè)iOS team机错,多個(gè)App。

  • 已經(jīng)使用了CocoaPods,如果要再集成Carthage比較麻煩。我們自己team還好說群扶,還有其他team怎么辦及刻。
  • 其他team已經(jīng)用的是CocoaPods。對(duì)他們來說只需要升級(jí)版本號(hào)竞阐,要用源碼時(shí)加上一個(gè)IS_SOURCE=1 pod install就可以了缴饭。夠平滑。
  • Carthage我要如何方便的切回源碼調(diào)試骆莹。
  • Carthage并不能解決版本更新要編譯的問題茴扁。除非track那些framework,共享給Team中的其他人汪疮。那樣的話你的git庫(kù)就會(huì)很大峭火。

說到底Carthage并不能解決實(shí)際應(yīng)用的問提。

什么是組件二進(jìn)制化智嚷?

在iOS開發(fā)中卖丸,事實(shí)標(biāo)準(zhǔn)是我們使用CocoaPods生成、管理和使用library盏道。這里的library就是一個(gè)模塊稍浆、組件或庫(kù)。二進(jìn)制化指的是通過編譯把組件的源碼轉(zhuǎn)換成靜態(tài)庫(kù)或動(dòng)態(tài)庫(kù)猜嘱,以提高該組件在App項(xiàng)目中的編譯速度衅枫。

我們的方案是轉(zhuǎn)換成靜態(tài)庫(kù),也就是.a格式的文件加上暴露出來的頭文件朗伶。

為什么我們需要二進(jìn)制化呢弦撩?

在我們App開發(fā)中,我們逐漸的抽象了很多模塊论皆、業(yè)務(wù)益楼、UI等把他轉(zhuǎn)換成私有CocoaPod庫(kù)猾漫。其中有一個(gè)是用C++和Objective-C混寫的,源碼格式為.mm感凤。在app項(xiàng)目編譯時(shí).mm部分代碼編譯非常慢悯周。這作為一個(gè)契機(jī)讓我們?nèi)タ紤]如何加快編譯速度。

這個(gè)混寫的CocoaPod庫(kù)叫做YTXChart陪竿,之后會(huì)以此庫(kù)為例反復(fù)提到禽翼。

另外隨著業(yè)務(wù)的擴(kuò)展,私有CocoaPod庫(kù)和第三方CocoaPod庫(kù)越來越多族跛,App項(xiàng)目中的文件也越來越多闰挡。每次pod install安裝新庫(kù)或pod update更新庫(kù)的時(shí)候,重新編譯的過程需要等待很長(zhǎng)時(shí)間庸蔼。這也向我們提出了加快編譯速度的需求。

另外如果想要做組件化的話贮匕,一定要做二進(jìn)制化姐仅。

所以我們想到了二進(jìn)制化的方案來解決這個(gè)問題,并且很多大公司也是這么做的刻盐。

這帶來一個(gè)新問題掏膏?一步就位還是平滑過度。

對(duì)我們來說敦锌,這是一個(gè)嘗試馒疹,不可能開始就決定把所有的私有CocoaPod庫(kù)二進(jìn)制化,也不可能決定把所有第三方CocoaPod庫(kù)二進(jìn)制化乙墙。當(dāng)務(wù)之急的情況是加快YTXChart庫(kù)編譯速度颖变。所以必須找到一個(gè)方案平滑過度。

我們的App中的podflie是這樣的

target 'jryMobile' do
    pod 'AFNetworking', '~> 2.6.3'
    pod 'Mantle', '~> 1.5.7'
    pod 'DateTools', '~> 1.7.0'
    pod 'ReactiveCocoa', '~> 2.3.1'
    pod 'CocoaAsyncSocket', '~> 7.4.1'
    pod 'FMDB', '~> 2.5'
    pod 'MWPhotoBrowser', '~> 1.4.1'
    pod 'MZFormSheetController', '~> 2.3.6'
    pod 'HMSegmentedControl', '~> 1.5.1'
    pod 'UMengAnalytics', '~> 3.5.8'
    pod 'UMengFeedback', '~> 2.3.4'
    pod 'TSMessagesNW', '~> 0.9.15'
    pod 'TPKeyboardAvoiding', '~> 1.2.9'
    pod 'SDWebImage', '~> 3.7'
    pod 'JHChainableAnimations', '~> 1.3.0' 
    pod 'BarrageRenderer', '~> 1.7.0'
    pod 'MJRefresh', '~> 3.1.7'
    pod 'YTXAnimations', '~> 1.2.4', :subspecs => ["AnimateCSS", "Transformer"]
    pod 'YTXMediaIJKPlayer', '~> 0.2.1'
    
    pod 'YTXTradeBusinessType', '~> 1.1.0'
    pod 'YTXServerId', '~> 0.1.4'
    pod 'YTXUtilCategory','~> 1.2.0'
    pod 'YTXScreenShotManager', '~> 0.1.7'
    pod 'YTXRequest', '~> 1.0.0'
    pod 'YTXCommonSocket', '~> 0.1.9'
    pod 'YTXChartSocket', '~> 0.5.1'
# 希望是二進(jìn)制化的
    pod 'YTXChart', '~> 0.17.0'

    pod 'YTXRestfulModel', '~> 1.2.2', :subspecs => ["RACSupport", "YTXRequestRemoteSync", "FMDBSync", "UserDefaultStorageSync"]
    pod 'YTXWebViewJavaScriptBridge', '~> 0.1.2'
    pod 'YTXCheckForAppUpdates', '~> 1.0.0'
    #    pod 'YTXVideoAVPlayer', '~> 0.5.0'
    pod 'YTXChatUI', '~> 0.3.2'
    pod 'PNChart', '~>0.8.9'
    
    #pod 'EaseMobSDKFull', :git => 'https://github.com/easemob/sdk-ios-cocoapods-integration.git', :tag => '2.2.0'
    # EaseMobSDKFull 更新地址'https://github.com/easemob/sdk-ios-cocoapods-integration.git'
    #pod 'AFgzipRequestSerializer', '~> 0.0.2'
    
    pod 'AdhocSDK', '~> 2.2.1'
    
    pod 'FLEX', '~> 2.0', :configurations => ['Debug']
    
    pod 'React', :path => './ReactComponent/node_modules/react-native', :subspecs => [
    'Core',
    'RCTImage',
    'RCTNetwork',
    'RCTText',
    'RCTWebSocket',
    # 添加其他你想在工程中使用的依賴听想。
    ]
    
    pod 'CodePush', :path => './ReactComponent/node_modules/react-native-code-push'
end

平滑二進(jìn)制方案需求點(diǎn)

  • 其他的CocoaPod庫(kù)都還是源碼腥刹。YTXChart為二進(jìn)制化。
  • 以后能夠逐步迭代把更多的以YTX開頭的CocoaPod庫(kù)進(jìn)行二進(jìn)制化汉买,而不影響主App衔峰。
  • 能夠提供一種方式把二進(jìn)制化CocoaPod庫(kù)切換回源碼CocoaPod庫(kù)以便調(diào)試。盡量做的方便蛙粘。
  • 解決YTXChart引用依賴的問題垫卤。(YTXChart還依賴了第三方AFNetworking和私有YTXServerId。保證生成的靜態(tài)庫(kù)中不會(huì)含有AFNetworking的內(nèi)容和YTXServerId的內(nèi)容并且能夠編譯通過)
  • 利用原來的YTXChart.git出牧,不創(chuàng)建新項(xiàng)目穴肘,不創(chuàng)建新的git庫(kù)。因?yàn)槲覀兊亩M(jìn)制化庫(kù)的生成還是來自于源碼舔痕,當(dāng)源碼更新時(shí)梢褐,我們需要一種非惩冢快捷的方式去生成二進(jìn)制的東西,不希望copy源碼到某處盈咳,或者增加一個(gè)git submodule耿眉。
  • 希望App源碼和YTXChart中的源碼盡量少或者沒有改動(dòng)。
  • 希望App中的Podfile盡量少或者沒有改動(dòng)鱼响。
  • 希望Podfile中的版本號(hào)保持風(fēng)格一致鸣剪,不會(huì)出現(xiàn)'~> 2.2.1.binary'這種情況。
  • 用原來的那一個(gè)CocoaPods Repo Spec丈积。

以下這個(gè)解決方案的教程滿足了以上所有需求點(diǎn)

==注意筐骇,以下的例子基于Cocoapods@1.0.1,而且目前只能是1.0.1==

第一步:源碼生成靜態(tài)庫(kù)

如果你是通過命令pod lipo create創(chuàng)建的CocoaPod庫(kù)并且pod install的話江滨,它的目錄結(jié)構(gòu)應(yīng)該像這樣子(只列出重要的):

YTXChart
  |-Example
    |-YTXChart
    |-Pods
    |-YTXChart.xcodeproj
    |-YTXChart.xcworkspace
    |-Podfile
    \-Podfile.lock
  |-Pod
    |-Assets
    \-Classes
  \-YTXChart.podspec

在xcode中創(chuàng)建新Target YTXChartBinaryFile->New->Target->Framework & Library->Cocoa Touch Static Library

==如果你們的項(xiàng)目最低支持到iOS8可以創(chuàng)建Dynamic Framework==

==注意在Podfile中加入以下這段==

target 'YTXChartBinary' do

end

然后pod install

解釋:Cocoapods@1.0.1會(huì)在Header Search Path自動(dòng)加入內(nèi)容铛纬。如果你用CocoaPods@0.39.0則需要自己加Header Search Path保證依賴庫(kù)YTXServerId和AFNetwork能夠被找到。如圖:

headersearchpath

然后把Pod/Classes中的源碼拖入到Y(jié)TXChartBinary中唬滑,這樣選擇(這樣會(huì)link源碼而不是復(fù)制):

default

然后變成這樣子:
default

Headers需要自己加告唆,里面是你需要暴露的頭文件

在YTXChartBinary Target中的Build Settings下找到iOS Deployment Target選擇和YTXChart.podspec中的s.platform保持一致。這里是7.0YTXChartBinary Target->Build Settings->iOS Deployment Target

在根目錄創(chuàng)建shell腳本buildbinary.sh

你也可以創(chuàng)建一個(gè)Aggregate Target用來執(zhí)行shell腳本

代碼如下:

#獲得當(dāng)前目錄的名字晶密,一般是YTXChartSocket這種
PROJECT_NAME=${PWD##*/}

# 編譯工程
BINARY_NAME="${PROJECT_NAME}Binary"

cd Example

INSTALL_DIR=$PWD/../Pod/Products
rm -fr "${INSTALL_DIR}"
mkdir $INSTALL_DIR
WRK_DIR=build

BUILD_PATH=${WRK_DIR}

DEVICE_INCLUDE_DIR=${BUILD_PATH}/Release-iphoneos/usr/local/include
DEVICE_DIR=${BUILD_PATH}/Release-iphoneos/lib${BINARY_NAME}.a
SIMULATOR_DIR=${BUILD_PATH}/Release-iphonesimulator/lib${BINARY_NAME}.a
RE_OS="Release-iphoneos"
RE_SIMULATOR="Release-iphonesimulator"

xcodebuild -configuration "Release" -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${BINARY_NAME}" -sdk iphoneos clean build CONFIGURATION_BUILD_DIR="${WRK_DIR}/${RE_OS}" LIBRARY_SEARCH_PATHS="./Pods/build/${RE_OS}"
xcodebuild ARCHS=x86_64 ONLY_ACTIVE_ARCH=NO -configuration "Release" -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${BINARY_NAME}" -sdk iphonesimulator clean build CONFIGURATION_BUILD_DIR="${WRK_DIR}/${RE_SIMULATOR}" LIBRARY_SEARCH_PATHS="./Pods/build/${RE_SIMULATOR}"

if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"

cp -rp "${DEVICE_INCLUDE_DIR}" "${INSTALL_DIR}/"

INSTALL_LIB_DIR=${INSTALL_DIR}/lib
mkdir -p "${INSTALL_LIB_DIR}"

lipo -create "${DEVICE_DIR}" "${SIMULATOR_DIR}" -output "${INSTALL_LIB_DIR}/lib${PROJECT_NAME}.a"
rm -r "${WRK_DIR}"

這個(gè)腳本寫的并不是很好擒悬。說說主要做了什么。
Release不同的靜態(tài)庫(kù)稻艰,真機(jī)和模擬器的懂牧。只構(gòu)建x86_64,不構(gòu)建i386加快速度

xcodebuild -configuration "Release" -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${BINARY_NAME}" -sdk iphoneos clean build CONFIGURATION_BUILD_DIR="${WRK_DIR}/${RE_OS}" LIBRARY_SEARCH_PATHS="./Pods/build/${RE_OS}"
xcodebuild ARCHS=x86_64 ONLY_ACTIVE_ARCH=NO -configuration "Release" -workspace "${PROJECT_NAME}.xcworkspace" -scheme "${BINARY_NAME}" -sdk iphonesimulator clean build CONFIGURATION_BUILD_DIR="${WRK_DIR}/${RE_SIMULATOR}" LIBRARY_SEARCH_PATHS="./Pods/build/${RE_SIMULATOR}"

*通過lipo命令合并尊勿。新.a使用project name是因?yàn)橐虯pp項(xiàng)目的OTHER_LDFLAGS兼容-l"YTXChart"

lipo -create "${DEVICE_DIR}" "${SIMULATOR_DIR}" -output "${INSTALL_LIB_DIR}/lib${PROJECT_NAME}.a"

結(jié)果:


default

為什么要?jiǎng)h除i386

實(shí)際上僧凤,二進(jìn)制化方案就是以空間換時(shí)間。我們這個(gè)YTXChart庫(kù)生成的.a去除i386之后大小有166.3M元扔。上傳到git倉(cāng)庫(kù)后拼弃,git會(huì)壓縮。增加了33M左右摇展。而作為二進(jìn)制文件吻氧,git是沒法做增量的。所以每次上傳.a都會(huì)大大增加git庫(kù)大小咏连,增加硬盤使用量盯孙。考慮到我們的服務(wù)器硬盤只有60個(gè)G能用祟滴,以后還會(huì)二進(jìn)制化很多組件振惰。

==所以得出:盡量壓縮二進(jìn)制文件大小垄懂;盡量不上傳.a骑晶,直到發(fā)布某個(gè)版本時(shí)才上傳痛垛。==

當(dāng)然,如果你的服務(wù)器硬盤是1T的話桶蛔,我覺得你也可以隨便搞匙头。

現(xiàn)在文件目錄是這樣子的:

YTXChart
  |-Example
    |-YTXChart
    |-Pods
    |-YTXChart.xcodeproj
    |-YTXChart.xcworkspace
    |-YTXChartBinary //空的
    |-Podfile
    \-Podfile.lock
  |-Pod
    |-Assets
    |-Classes //里面是源碼
    \-Products
      |-include
         |-xxx.h
         |-...
         \-xxx.h
      \-lib
         \- libYTXChart.a
  \-YTXChart.podspec

第二步:測(cè)試生成的靜態(tài)庫(kù)。

==修改YTXChart.podspec如下:==

Pod::Spec.new do |s|
  s.name             = "YTXChart"
  s.version          = "0.17.7"
  s.summary          = "YTXChart for pod"

# This description is used to generate tags and improve search results.
#   * Think: What does it do? Why did you write it? What is the focus?
#   * Try to keep it short, snappy and to the point.
#   * Write the description between the DESC delimiters below.
#   * Finally, don't worry about the indent, CocoaPods strips it!
  s.description      = "銀天下Chart, 依賴AFNetworking"

  s.homepage         = "http://gitlab.baidao.com/ios/YTXChart.git"
  # s.screenshots     = "www.example.com/screenshots_1", "www.example.com/screenshots_2"
  s.license          = 'MIT'
  s.author           = { "caojun-mac" => "78612846@qq.com" }
  s.source           = { :git => "http://gitlab.baidao.com/ios/YTXChart.git", :tag => s.version }
  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

  s.platform     = :ios, '7.0'
  s.requires_arc = true

  
  s.source_files = 'Pod/Products/include/**'
  s.public_header_files = 'Pod/Products/include/*.h'
  s.ios.vendored_libraries = 'Pod/Products/lib/libYTXChart.a'


  s.libraries = 'sqlite3', 'c++'

  s.dependency 'YTXServerId'
  s.dependency 'AFNetworking', '~> 2.0'
end

==注意s.source_files和s.public_header_files和s.ios.vendored_libraries的路徑==

Exampl/Podfile是長(zhǎng)這樣子的:

source 'http://gitlab.baidao.com/ios/ytx-pod-specs.git'
source 'https://github.com/CocoaPods/Specs.git'

target 'YTXChart_Example' do
  pod "YTXChart", :path => "../"
  pod 'ReactiveCocoa', '~> 2.5'
  pod 'YTXChartSocket'
  pod 'AFNetworking', '~> 2.0'
end

target 'YTXChartBinary' do

end

target 'YTXChart_Tests' do
  pod "YTXChart", :path => "../"
  pod 'Kiwi'
end

執(zhí)行pod install后應(yīng)該是這樣子的仔雷,然后跑起來沒問題

[站外圖片上傳中...(image-c4e3e-1512559943469)]

執(zhí)行pod lib lint --sources='http://gitlab.baidao.com/ios/ytx-pod-specs.git,master'--verbose --use-libraries --fail-fast也是好的

至此我們構(gòu)建出一個(gè)靜態(tài)庫(kù)蹂析,只包含YTXChart的內(nèi)容,不包含依賴AFNetwork和YTXServerId的內(nèi)容

證明:把s.dependency 'AFNetworking', '~> 2.0'去除再執(zhí)行pod lib lint 'http://gitlab.baidao.com/ios/ytx-pod-specs.git,master' --verbose --use-libraries --fail-fast會(huì)報(bào)出找不到AFNetwork相關(guān)文件碟婆。

==題外話:因?yàn)镃ocoaPods1.0.1不支持C++項(xiàng)目的lint(這是一個(gè)defect)电抚,所以這個(gè)時(shí)候我會(huì)切回CocoaPods@0.39.0來lint和publish。而前面pod instal增加Search Path是依靠CocoaPods@1.0.1竖共。如果你不是.mm混寫的蝙叛,是不會(huì)有這個(gè)問題的。盡管使用CocoaPods@1.0.1公给。強(qiáng)行當(dāng)作沒看到這個(gè)題外話借帘。==

下一步解決如何在源碼和二進(jìn)制中切換
修改YTXChart.podspec為以下內(nèi)容:

#
# Be sure to run `pod lib lint YTXChart.podspec' to ensure this is a
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#

Pod::Spec.new do |s|
  s.name             = "YTXChart"
  s.version          = "0.17.7"
  s.summary          = "YTXChart for pod"

# This description is used to generate tags and improve search results.
#   * Think: What does it do? Why did you write it? What is the focus?
#   * Try to keep it short, snappy and to the point.
#   * Write the description between the DESC delimiters below.
#   * Finally, don't worry about the indent, CocoaPods strips it!
  s.description      = "銀天下Chart, 依賴AFNetworking"

  s.homepage         = "http://gitlab.baidao.com/ios/YTXChart.git"
  # s.screenshots     = "www.example.com/screenshots_1", "www.example.com/screenshots_2"
  s.license          = 'MIT'
  s.author           = { "caojun-mac" => "78612846@qq.com" }
  s.source           = { :git => "http://gitlab.baidao.com/ios/YTXChart.git", :tag => s.version }
  # s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'

  s.platform     = :ios, '7.0'
  s.requires_arc = true

  
 if ENV['IS_SOURCE']
    puts '-------------------------------------------------------------------'
    puts 'Notice:YTXChart is source now'
    puts '-------------------------------------------------------------------'
      s.source_files  = "Pod/Classes/painter/*.{h,m,mm}", "Pod/Classes/painterview/*.{h,m,mm}", "Pod/Classes/chart/*.{h,m,mm}", "Pod/Classes/core/*.{h,mm}", "Pod/Classes/core/**/*.{h,m,mm,inl}"  
  else
    puts '-------------------------------------------------------------------'
    puts 'Notice:YTXChart is binary now'
    puts '-------------------------------------------------------------------'
    s.source_files = 'Pod/Products/include/**'
    s.public_header_files = 'Pod/Products/include/*.h'
    s.ios.vendored_libraries = 'Pod/Products/lib/libYTXChart.a'
  end

  s.libraries = 'sqlite3', 'c++'

  s.dependency 'YTXServerId'
  s.dependency 'AFNetworking', '~> 2.0'
end

注意這段if ENV['IS_SOURCE']。我們的需求是優(yōu)先使用二進(jìn)制妓布,偶爾才會(huì)切回源碼姻蚓。

==刪除Example/Pods目錄宋梧。==

執(zhí)行IS_SOURCE=1 pod install匣沼。你會(huì)看到Example/Pods/YTXChart/里面都是源碼

輸出Notice:YTXChart Now is source

進(jìn)一步跑起模擬器,因?yàn)槭窃创a編譯用了很長(zhǎng)時(shí)間捂龄,模擬器起來释涛,一切也是好的

再試下pod cache clean --all && IS_SOURCE=1 pod lib lint也是好的

再試下pod cache clean --all && pod lib lint也是好的

現(xiàn)在我們通過if else簡(jiǎn)單地實(shí)現(xiàn)了本地Example App項(xiàng)目切換源碼和二進(jìn)制。

發(fā)布到自己的pod repo spec

發(fā)布就和正常發(fā)布沒有任何區(qū)別倦沧。

檢查從spec repo的source中安裝

Podfile修改為pod 'YTXChart', '~> 0.17.7'

以下兩步很重要

==pod cache clean --all==

==刪除Example/Pods==

然后pod install

檢查Example/Pods/YTXServerId/和Example/Pods/AFNetwork/發(fā)現(xiàn)都是.h .m源碼唇撬。

檢查Example/Pods/YTXChart/里的是二進(jìn)制.a和頭文件。跑起App并沒有問題展融。

嘗試切回源碼

如果你直接IS_SOURCE=1 pod install你會(huì)發(fā)現(xiàn)Example/Pods/YTXChart/里的內(nèi)容都變成了空

這是為什么呢窖认,因?yàn)閜od cache了一個(gè)podspec.json「嫦#可以通過pod cache list查看扑浸。他cache了一個(gè)描述如何從s.source中找到相關(guān)文件。現(xiàn)在的描述還是從Pod/Products/下去找燕偶,自然為空喝噪。

為了避免這個(gè)問題,所以必須執(zhí)行上面兩步指么。這個(gè)是唯一的問題酝惧,目前我還找不到更好的解決方案榴鼎。切換的行為只是偶爾發(fā)生,這是可以接受的晚唇。

執(zhí)行2步巫财。再次IS_SOURCE=1 pod install你就發(fā)現(xiàn)Example/Pods/YTXChart/里的內(nèi)容都變成了.h .mm源碼。跑起App也是好的缺亮。

==為什么lint之前要cache clean翁涤。原理是一樣的。如果YTXChart依賴的YTXServerId也被做成了二進(jìn)制化就需要cache clean萌踱。不過你也可以這樣pod cache clean YTXServerId==

特別注意==IS_SOURCE==應(yīng)當(dāng)作為一個(gè)所有非二進(jìn)制化Pod庫(kù)的統(tǒng)一標(biāo)識(shí)葵礼,并且通知你們的項(xiàng)目組里所有成員。pod install可能會(huì)有某幾個(gè)已經(jīng)二進(jìn)制化的庫(kù)使用二進(jìn)制的內(nèi)容并鸵。IS_SOURCE=1 pod install時(shí)鸳粉,所有的庫(kù)都將會(huì)是源碼的內(nèi)容。

版本管理

請(qǐng)參考這篇我的文章CocoaPod版本規(guī)范

完整分析

當(dāng)你發(fā)布完成之后园担,查看届谈。我們發(fā)現(xiàn)在Spec Repo中對(duì)應(yīng)版本的podspec就是我們的YTXChart/podspec。
CocoaPod從s.sourcegit地址和tag下載對(duì)應(yīng)的代碼弯汰,Pod/Products和Pod/Classes里的內(nèi)容都存在
當(dāng)你使用IS_SOURCE=1時(shí)ENV['IS_SOURCE']會(huì)為true艰山。CocoaPods通過s.source_files從下載代碼的路徑找到源碼構(gòu)建Example/Pods和YTXChart.xcworkspace

publishspec

明白了上面的過程,來再分析下為什么要在切換源碼和二進(jìn)制化時(shí)刪除cache和Pods目錄咏闪。放幾張圖就明白了
podcachelist

cachejson

刪除cache和Pods目錄曙搬。IS_SOURCE=1 pod install觀察json。
cachejsonsource

再進(jìn)一步

當(dāng)你刪除了所有cache之后再pod install會(huì)比較慢鸽嫂,有時(shí)候我們只是想要查看某個(gè)庫(kù)的源碼纵装,怎么辦。

  • podspec中變成這樣子if ENV['IS_SOURCE'] || ENV["YTXChart_SOURCE"]
  • 刪除Pods/Pods.xcodeproj
  • 刪除Pods/YTXChart
  • 刪除YTXChart的cachepod cache clean YTXChart
  • 最后YTXChart_SOURCE=1 pod install

總結(jié):

  • 沒有使用submodule或新的git倉(cāng)庫(kù)來構(gòu)建出一個(gè)不包含依賴內(nèi)容的靜態(tài)庫(kù)据某。一份原來的git倉(cāng)庫(kù)橡娄。
  • 沒有因?yàn)闃?gòu)建二進(jìn)制庫(kù)而需要增加冗余的源代碼。所以當(dāng)你修改Pod/Classes中的源碼癣籽,可以方便簡(jiǎn)單地執(zhí)行buildbinary.sh腳本來構(gòu)建出靜態(tài)庫(kù)挽唉。一份源碼。
  • 共用了一份YTXChart.podspec
  • 沒有大量修改YTXChart.podspec
  • 使用pod lib lintIS_SOURCE=1 pod lib lint檢查通過筷狼。
  • 沒有修改Podfile瓶籽。這個(gè)Example的Podflie只是測(cè)試需要才改的。?主App項(xiàng)目中的Podfile可以一行都不改桑逝。不會(huì)出現(xiàn)'~> 2.2.1.binary'棘劣。
  • App中的源碼不會(huì)因?yàn)槭褂昧硕M(jìn)制CocoaPods組件而做任何修改。
  • 沒有手動(dòng)配置Search Path楞遏,這樣更容易茬暇。
  • 在Example App中可以通過IS_SOURCE靈活地切換源碼和二進(jìn)制靜態(tài)庫(kù)首昔。唯一一個(gè)問題每次切換要?jiǎng)h除Pods目錄和pod cache clean --all
  • 跑起Example App總是好的。
  • 沒有影響到其他庫(kù)糙俗,我可以逐步平滑地把YTXXXX一個(gè)一個(gè)做成二進(jìn)制勒奇。

下一步目標(biāo)

逐步平滑地把YTXXXX一個(gè)一個(gè)做成二進(jìn)制

進(jìn)一步的把第三方如AFNetwork在私有spec repo中做份鏡像也提供二進(jìn)制化

把Podfile中絕大部分組件都做成二進(jìn)制(RN這種本地安裝模式和有sub spec的庫(kù)目前不打算二進(jìn)制化)

關(guān)于資源文件,資源文件在二進(jìn)制化中的配置是一樣的

另外巧骚,使用二進(jìn)制化的CocoaPods庫(kù)不會(huì)增加ipa的大小赊颠。所以我們應(yīng)當(dāng)優(yōu)先用二進(jìn)制化的東西,這可以加快Archive速度劈彪。

關(guān)于有sub spec的CocoaPods組件

兩個(gè)方案:

  • 提供一個(gè)全集
  • 對(duì)每一個(gè)sub spec都做份二進(jìn)制并保持它們之間依賴的相互關(guān)系

走過的彎路?⒈摹!2着6焕ā!L戏汀8倬!4痢:采唷!6АR渭!0隆R固椤7咐纭J舴摺!K嵋邸W≈睢!;猎琛<拧!H牍稹Q俎薄!?钩睢D俚佟:峭怼!沫屡!

現(xiàn)在這個(gè)解決方案看起來簡(jiǎn)單饵隙,但在當(dāng)初的探索過程中并不是那么順利齐遵。以下是不成功的嘗試胀瞪!

創(chuàng)建另一個(gè)YTXChartBinary.podspec

  • 把生成的Products目錄放到Y(jié)TXChartBinary下
  • 把YTXChartBinary.podspec目錄放到Y(jié)TXChartBinary下
  • Podfile中通過增加Binary字段安裝二進(jìn)制化如pod 'YTXChartBinary', '~> 0.17.7'

問題

  • 要維護(hù)2個(gè)podspec。版本號(hào)很可能不統(tǒng)一失驶。
  • 當(dāng)pod spec lint報(bào)錯(cuò):找不到相關(guān)文件勺届。
  • Podfile中通過增加Binary字段來切換驶俊,非常不方便。
  • 要改App源碼免姿。當(dāng)安裝二進(jìn)制的時(shí)候<YTXChart/YTXChart.h>需要改成<YTXChartBinary/YTXChart.h>废睦。來回切換都需要改,極不方便养泡。
  • 要改App源碼嗜湃。這次一勞永逸。直接這樣使用"YTXChart.h"澜掩。但這樣也不好购披。

創(chuàng)建另一個(gè)專門放二進(jìn)制化的Spec Repo,通過不同的Source來區(qū)分

解決了要改App源碼的問題肩榕。只需要在Podfile中加個(gè)source刚陡。

不同的source例子

source 'http://gitlab.baidao.com/ios/ytx-binary-pod-specs.git'
source 'http://gitlab.baidao.com/ios/ytx-pod-specs.git'

問題

  • 發(fā)布兩次,lint兩次株汉。
  • 創(chuàng)建了2個(gè)Spec Repo筐乳。

后記加強(qiáng)篇

解決每次切換要?jiǎng)h除Pods目錄和需要pod cache clean --all

唯一額外要付出的是需要一個(gè)靜態(tài)資源服務(wù)器。

其實(shí)這部分內(nèi)容我在《iOS App組件化開發(fā)實(shí)踐》提過一下乔妈,沒有展開說蝙云。可能有些小伙伴只看了這篇文章從而沒有了解到路召。

如果小伙伴們耐心看到了最后勃刨,現(xiàn)在這些算是驚喜吧。

請(qǐng)看這個(gè)模板NAME.podspec

# ...省去不相關(guān)內(nèi)容
ytx_zipURL='http://ios-pod.baidao.com/binaryfiles/${POD_NAME}.zip'

  if ENV['IS_SOURCE'] || ENV["#{s.name}_SOURCE"]
      # 如果是源碼股淡,則使用git地址
      s.source           = { :git => "http://gitlab.baidao.com/ios/#{s.name}.git", :tag => s.version.to_s }
  else
     # 如果是二進(jìn)制身隐,則是用zip地址
      s.source           = { :http => ytx_zipURL}
  end

  if ENV['IS_SOURCE'] || ENV["#{s.name}_SOURCE"]
      # 因?yàn)樵创a使用的是git,所以執(zhí)行downlolad_zip去把zip下載下來
      # prepare_command這一段是pod install時(shí)會(huì)執(zhí)行的腳本
      s.prepare_command = <<-'END'
        test -f download_zip.sh && sh download_zip.sh ${POD_NAME}
      END

      puts '-------------------------------------------------------------------'
      puts "Notice:#{s.name} is source now"
      puts '-------------------------------------------------------------------'
      s.source_files = '${POD_NAME}/Classes/**/*'
  else
      puts '-------------------------------------------------------------------'
      puts "Notice:#{s.name} is binary now"
      puts '-------------------------------------------------------------------'
      s.source_files = '${POD_NAME}/Classes/*.h'
      # 如果是framework唯灵,public_header_files就不需要了
      s.public_header_files = '${POD_NAME}/Classes/*.h'
      s.ios.vendored_libraries = "${POD_NAME}/lib/lib#{s.name}.a"
  end
  # preserve_paths是受保護(hù)的路徑也就是會(huì)一起安裝到目錄下/進(jìn)入cache(哪怕可能根本不參與編譯)
  s.preserve_paths = "#{s.name}/lib/lib#{s.name}.a","#{s.name}/Classes/**/*", "download_zip.sh"
# ...省去不相關(guān)內(nèi)容

附上ytx-pod-template里面有download_zip.sh和zip.sh給大家參考贾铝。download_zip.sh值得一說的是,它會(huì)判斷相關(guān)目錄下有沒有文件,有文件的話則不會(huì)每次下載垢揩。

思路是保證無論是IS_SOURCE還是非IS_SOURCE都要保證你的pod/cache目錄下既有.h,.m,.a/.framework大脉,但又不會(huì)沖突。

.a/.framework不要進(jìn)git水孩,否則會(huì)造成git越來越大镰矿,但是放在靜態(tài)服務(wù)器就沒事。

當(dāng)你發(fā)版時(shí)CI也會(huì)自動(dòng)zip文件.h, .m .a俘种,傳上靜態(tài)服務(wù)器秤标。

當(dāng)你pod install的時(shí)候,cache/pods里面的內(nèi)容是cocoapods直接用的zip地址宙刘,所有文件都有苍姜。沒問題。

當(dāng)你IS_SOURCE pod install的時(shí)候悬包,不會(huì)有.a(.a沒進(jìn)git)衙猪,所以執(zhí)行prepare_command去下載zip的地址,解壓布近,把.a解壓到相關(guān)目錄垫释。

因?yàn)?code>preserve_paths,你的Pods/XX的目錄下或者cache目錄下都是這個(gè)樣子的:

XX
|- Classes
   |- XX.h
   |- XX.m
   |- AA.h
   \- AA.m
\- lib
   \- libXX.a

最后無論你進(jìn)了if還是else撑瞧,你podspec中描述的文件總能找到棵譬。

完美。
pod cache list

1513045548954.jpg

1513045760188.jpg
1513045953521.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末预伺,一起剝皮案震驚了整個(gè)濱河市订咸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌酬诀,老刑警劉巖脏嚷,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異瞒御,居然都是意外死亡父叙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門葵腹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來高每,“玉大人屿岂,你說我怎么就攤上這事践宴。” “怎么了爷怀?”我有些...
    開封第一講書人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵阻肩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng)烤惊,這世上最難降的妖魔是什么乔煞? 我笑而不...
    開封第一講書人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮柒室,結(jié)果婚禮上渡贾,老公的妹妹穿的比我還像新娘。我一直安慰自己雄右,他們只是感情好空骚,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著擂仍,像睡著了一般囤屹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上逢渔,一...
    開封第一講書人閱讀 51,624評(píng)論 1 305
  • 那天肋坚,我揣著相機(jī)與錄音,去河邊找鬼肃廓。 笑死智厌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的盲赊。 我是一名探鬼主播峦剔,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼角钩!你這毒婦竟也來了吝沫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤递礼,失蹤者是張志新(化名)和其女友劉穎惨险,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脊髓,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡辫愉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了将硝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恭朗。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖依疼,靈堂內(nèi)的尸體忽然破棺而出痰腮,到底是詐尸還是另有隱情,我是刑警寧澤律罢,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布膀值,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沧踏。R本人自食惡果不足惜歌逢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望翘狱。 院中可真熱鬧秘案,春花似錦、人聲如沸潦匈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)历等。三九已至讨惩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寒屯,已是汗流浹背荐捻。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寡夹,地道東北人处面。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像菩掏,于是被迫代替她去往敵國(guó)和親魂角。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 這篇文章主要想介紹以下幾個(gè)部分: subspecs基本概念 實(shí)際應(yīng)用的好處 如何對(duì)含有subspecs的Cocoa...
    曹俊_413f閱讀 7,610評(píng)論 7 24
  • 項(xiàng)目組件化智绸、平臺(tái)化是技術(shù)公司的共同目標(biāo)野揪,越來越多的技術(shù)公司推崇使用pod管理第三方庫(kù)以及私有組件,一方面使項(xiàng)目架構(gòu)...
    swu_luo閱讀 21,773評(píng)論 0 39
  • Ruby 安裝 要安裝cocospods 首先需要安裝ruby瞧栗,可以先安裝xcode斯稳,再安裝macport ,最后...
    山天大畜閱讀 1,859評(píng)論 0 1
  • CocoaPods 是什么迹恐? CocoaPods 是一個(gè)負(fù)責(zé)管理 iOS 項(xiàng)目中第三方開源庫(kù)的工具挣惰。CocoaPo...
    朝洋閱讀 25,682評(píng)論 3 51
  • 漫漫長(zhǎng)街里的繁燈一盞 迷離塵世中的寂靜一隅 入畫景象間的淡雅一筆 孤夜至深后的幾抹閃耀 日出微亮前的幾縷霞光 泛黃...
    賈方舟閱讀 366評(píng)論 0 8