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能夠被找到。如圖:
然后把Pod/Classes中的源碼拖入到Y(jié)TXChartBinary中唬滑,這樣選擇(這樣會(huì)link源碼而不是復(fù)制):
然后變成這樣子:
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é)果:
為什么要?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.source
git地址和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
明白了上面的過程,來再分析下為什么要在切換源碼和二進(jìn)制化時(shí)刪除cache和Pods目錄咏闪。放幾張圖就明白了
刪除cache和Pods目錄曙搬。
IS_SOURCE=1 pod install
觀察json。再進(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的cache
pod 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 lint
和IS_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