最近想為公司搭建cocoapods私有庫框架,老早之前做過搬设,踩過不少坑穴店,想不到又一次掉坑里。果真是好記性不如爛筆頭拿穴,這次得記下來泣洞。
1. 前言
cocoapods早已不是新鮮技術(shù),作為iOS平臺上的依賴管理工具默色,方便我們?nèi)粘i_發(fā)過程中管理和更新第三方庫球凰。基本使用方式不再贅述,這里說一下需求呕诉。項目規(guī)模大了之后缘厢,往往需要將各個業(yè)務(wù)功能拆分封裝,一方面解耦甩挫,另一方面可作為第三方庫供其他項目組使用贴硫。
所以產(chǎn)生一個需求:公司各開發(fā)團隊,將自己的業(yè)務(wù)伊者,封裝為私有庫英遭,上傳到公司私有g(shù)it上,利用cocoapods統(tǒng)一管理亦渗。
分解需求挖诸,我們需要:
- 創(chuàng)建私有spec repo(相當于cocoapods私有庫資源中心https://github.com/CocoaPods/Specs.git),所有的私有庫上傳記錄在私有spec中
- 工程封裝為私有庫央碟,上傳到私有spec repo中
- 項目工程集成私有庫
2. 搭建
1. 創(chuàng)建私有spec repo
什么是spec repo
在Podfile中税灌,我們通過
pod 'repoName'
來配置要加載的第三方庫均函。repoName對應(yīng)的repo地址并沒有寫出來亿虽,因為我們會到https://github.com/CocoaPods/Specs.git中去查找。它相當于一個容器苞也,將所有私有庫的podspec文件放到這個spec repo中洛勉,建立索引,提供私有庫配置信息如迟。
通過pod repo list命令收毫,查看mac 上的repo列表:
HotacooldeMacBook-Pro:0-Venus hotacool$ pod repo list
master
- Type: git (master)
- URL: https://github.com/CocoaPods/Specs.git
- Path: /Users/hotacool/.cocoapods/repos/master
本地spec repo的大致結(jié)構(gòu):
.
├── Specs
└── [SPEC_NAME]
└── [VERSION]
└── [SPEC_NAME].podspec
進入/Users/hotacool/.cocoapods/repos/master,可以看到目錄結(jié)構(gòu):
創(chuàng)建remote&本地clone
理解了上面這個知識點殷勘,我們知道了需要創(chuàng)建一個自己的spec repo此再。
- 在代碼托管平臺(github,Bitbucket玲销,碼云等)输拇,創(chuàng)建spec repo的托管空間
例如在碼云上創(chuàng)建一個spec repo:https://gitee.com/hotacool/HACSpec.git - 利用cocoapods提供command,在terminal上運行:
// Clones `URL` in the local spec-repos directory at `~/.cocoapods/repos/`. The
remote can later be referred to by `NAME`.
pod repo add HACSpec https://gitee.com/hotacool/HACSpec.git
會看到從remote地址git clone spec repo到本地贤斜,再次查看本地repo列表策吠,可以看到我們創(chuàng)建的spec repo:
HotacooldeMacBook-Pro:0-Venus hotacool$ pod repo list
HACSpec
- Type: git (master)
- URL: https://gitee.com/hotacool/HACSpec.git
- Path: /Users/hotacool/.cocoapods/repos/HACSpec
master
- Type: git (master)
- URL: https://github.com/CocoaPods/Specs.git
- Path: /Users/hotacool/.cocoapods/repos/master
這一步是在本地添加私有庫source,其他開發(fā)人員Podfile需要集成私有spec上的私有庫瘩绒,都需要首先通過pod repo add命令猴抹,本地clone spec repo。
另外一些命令也可能會用到:
// 查看本地spec repo列表
pod repo list
// 更新本地specName的spec repo锁荔。私有庫podspec文件更改并push到spec repo后蟀给,往往需要update
pod repo update specName
// 本地刪除spec repo
pod repo remove specName
2. 私有庫制作
創(chuàng)建一個基本(不包含代碼)的私有庫
創(chuàng)建私有庫之前,首先需要解耦獨立出單獨的組件項目。如果已有獨立的組件項目跋理,可以通過pod spec create直接創(chuàng)建podspec文件拍霜,然后配置,生成私有庫薪介。這里首先講一下祠饺,如何完整的創(chuàng)建一個新的私有庫,并push到我么自己的spec repo中汁政。
官網(wǎng)上有詳細的命令解釋文檔道偷。下面以創(chuàng)建一個名為HStockCharts的私有庫為例:
- 創(chuàng)建新的lib庫
pod lib create HStockCharts
cocoapods會clone pod-template模板來生成HStockCharts,創(chuàng)建過程中會問幾個問題:
What language do you want to use?? [ Swift / ObjC ]
> ObjC
Would you like to include a demo application with your library? [ Yes / No ]
> Yes
Which testing frameworks will you use? [ Specta / Kiwi / None ]
> None
Would you like to do view based testing? [ Yes / No ]
> No
What is your class prefix?
> H
Running pod install on your new library.
一般我會按上面代碼來生成一個帶example的例子工程记劈,這樣生成的example相對干凈勺鸦。也可以選擇自動集成testing framework,或者view based testing目木,這樣cocoapods會自動幫你集成一些額外的三方庫换途。
這樣生成的目錄結(jié)構(gòu):
HotacooldeMacBook-Pro:HStockCharts hotacool$ tree -L 2
.
├── Example
│ ├── HStockCharts
│ ├── HStockCharts.xcodeproj
│ ├── HStockCharts.xcworkspace
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Pods
│ └── Tests
├── HStockCharts
│ ├── Assets
│ └── Classes
├── HStockCharts.podspec
├── LICENSE
├── README.md
└── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
10 directories, 5 files
HStockCharts.podspec是私有庫的配置文件;HStockCharts文件夾下是私有庫代碼文件刽射,Assets是放資源军拟,Classes下是編譯的源文件;Example是自動生成的測試工程誓禁。
- 編輯.podspec文件
.podspec文件是私有庫的配置文件懈息。新創(chuàng)建的lib中,自動生成了HStockCharts.podspec摹恰,填充了一些默認配置辫继,我們需要手動去編輯。cocoapods是基于Ruby語音寫的俗慈,所以可以設(shè)置為Ruby語法高亮姑宽。
Pod::Spec.new do |s|
s.name = 'HStockCharts'
s.version = '0.1.0'
s.summary = 'A short description of HStockCharts.'
# 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 = <<-DESC
TODO: Add long description of the pod here.
DESC
s.homepage = 'https://github.com/shisosen@163.com/HStockCharts'
# s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'shisosen@163.com' => 'shisosen@163.com' }
s.source = { :git => 'https://github.com/shisosen@163.com/HStockCharts.git', :tag => s.version.to_s }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.ios.deployment_target = '8.0'
s.source_files = 'HStockCharts/Classes/**/*'
# s.resource_bundles = {
# 'HStockCharts' => ['HStockCharts/Assets/*.png']
# }
# s.public_header_files = 'Pod/Classes/**/*.h'
# s.frameworks = 'UIKit', 'MapKit'
# s.dependency 'AFNetworking', '~> 2.3'
end
各標簽(s.name等)的具體意義可以參照官方文檔。這里先列舉幾個重要的地方:
s.name //名稱闺阱,pod search 查找
s.version //版本號炮车,私有庫git tag。如果s.source里設(shè)置了s.version.to_s馏颂,需要注意與私有庫git的tag相匹配示血。
s.source //重要,私有庫的remote 地址和tag救拉。也可以:branch => 'master'設(shè)置分支难审。
s.source_files //#代碼源文件地址,**/*表示Classes目錄及其子目錄下所有文件亿絮;如果有多個目錄下則用逗號分開如['','','']形式
s.resource_bundles //資源文件地址
s.public_header_files //公開的頭文件地址
s.frameworks //所需的framework告喊,多個用逗號隔開
s.dependency //依賴關(guān)系麸拄,該項目所依賴的其他庫,如果有多個需要填寫多個s.dependency黔姜。cocoapods會自動將依賴的其他庫集成到工程中拢切。
現(xiàn)在只簡單的對source等配置(s.source,s.description)做一下修改:
Pod::Spec.new do |s|
s.name = 'HStockCharts'
s.version = '0.1.0'
s.summary = 'HStockCharts.'
s.description = <<-DESC
HStockCharts is a library for stock charts.
DESC
s.homepage = 'https://gitee.com/hotacool'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.author = { 'hotacool' => 'shisosen@163.com' }
s.source = { :git => 'https://gitee.com/hotacool/HStockCharts.git', :tag => s.version.to_s }
s.ios.deployment_target = '8.0'
s.source_files = 'HStockCharts/Classes/**/*'
# s.resource_bundles = {
# 'HStockCharts' => ['HStockCharts/Assets/*.png']
# }
# s.public_header_files = 'Pod/Classes/**/*.h'
# s.frameworks = 'UIKit', 'MapKit'
# s.dependency 'AFNetworking', '~> 2.3'
end
- push 私有庫到remote秆吵,validation
現(xiàn)在在本地已完成私有庫的創(chuàng)建淮椰,需要將私有庫push到遠程倉庫,這樣其他人才能到遠程倉庫clone纳寂。這里將本地HStockCharts push到https://gitee.com/hotacool/HStockCharts.git主穗,即podspec設(shè)置的source地址。
因為我們podspec s.source設(shè)置了:tag => s.version.to_s毙芜,version是0.1.0忽媒,所以需要打tag,注意tag應(yīng)與version保持一致:
git tag -m "release v.0.1.0 "0.1.0"
git push --tags //推送tag到遠端倉庫
然后開始通過pod lib lint 對私有庫進行validation:
HotacooldeMacBook-Pro:HStockCharts hotacool$ pod lib lint
-> HStockCharts (0.1.0)
HStockCharts passed validation.
驗證通過腋粥。默認情況下晦雨,如果驗證出現(xiàn)warning,是不能通過驗證的隘冲∧智疲可以通過添加--allow-warnings來允許warnings。
- push私有庫到spec repo
我們已經(jīng)在本地搭建好了一個私有庫对嚼,在Example中夹抗,Podfile通過相對地址,引入HStockCharts纵竖。
pod 'HStockCharts', :path => '../'
現(xiàn)在需要將私有庫推送到我們的spec repo中,從而可以從遠程clone杏愤。
之前已經(jīng)在本地通過pod repo add靡砌,新加入了我們自己的spec repo: HACSpec。通過命令:
pod repo push HACSpec HStockCharts.podspec //前面是本地Repo名字 后面是podspec名字
將HStockCharts推送到spec repo珊楼。執(zhí)行pod repo update HACSpec通殃,再cd 到本地repo文件夾/Users/hotacool/.cocoapods/repos/HACSpec,我們可以看到:
.
├── HStockCharts
│ └── 0.1.0
├── README.md
HStockCharts已從remote加入本地spec厕宗,版本號0.1.0画舌。
- 集成私有庫
現(xiàn)在我們可以在其他項目中,集成我們自己的私有庫已慢。Podfile加入:
pod 'HStockCharts'
運行pod install曲聂,報錯[!] Unable to find a specification for HStockCharts
因為HStockCharts在我們自己的spec repo中,默認的source地址是cocoapods repo佑惠。Podfile頭部加入:
source 'https://gitee.com/hotacool/HACSpec.git' #private spec repo
再次pod install朋腋,成功齐疙。
同樣的,因為我們的spec是自建的旭咽,沒有fork cocoapods的贞奋,所以不包含其他公共的私有庫,如果你用到pod 'AFNetworking'穷绵,Podfile也需要添加:
source 'https://github.com/CocoaPods/Specs.git'
至此轿塔,我們已經(jīng)完整的建立了私有庫,并加入私有的spec repo仲墨。但這個庫什么都干不了催训,如果加入我們自己的代碼,需要做更多的配置和處理宗收。
私有庫設(shè)置
上面的介紹漫拭,沒有涉及功能代碼,實際的私有庫配置混稽,往往會遇到各種問題采驻。下面對遇到的具體問題,做一些對應(yīng)匈勋。
- 私有庫依賴其他framework礼旅、library設(shè)置
解耦出的私有庫工程,往往有對某些系統(tǒng)或者第三方framework洽洁、.a靜態(tài)庫的依賴痘系。所以需要對私有庫的podspec進行設(shè)置,使其他工程Podfile集成私有庫時饿自,能夠自動引入相關(guān)依賴汰翠。
我們會對下面幾個標簽進行設(shè)置:
s.framework = 'SystemConfiguration','CoreData' //對系統(tǒng)framework的依賴
s.libraries = 'c++','stdc++.6.0.9' //對系統(tǒng).a的依賴,注意去掉lib前綴
s.ios.vendored_libraries = 'libXXX.a' //對第三方.a的依賴昭雌,注意路徑复唤,libXXX.a在.podspec目錄下
s.ios.vendored_frameworks = 'XXX.framework' //對第三方framework的依賴
在項目的Pods工程的私有庫target的build setting中,查看Other linker Flags烛卧,可以看到依賴的framework和.a:
- 包含.a的私有庫
上面說了對第三方.a的依賴佛纫,可以通過s.ios.vendored_libraries來配置。公開的header files总放,按照官方說明呈宇,可以再s.public_header_files中設(shè)置。但貌似沒達不到效果局雄。目前我是通過s.source_files甥啄,將.h頭文件統(tǒng)一放在s.source_files目錄下輸出。
另一個問題哎榴,直接對包含.a的私有庫使用pod lib lint來validation是可以通過的型豁,但pod repo push驗證會出錯會報錯僵蛛。報錯內(nèi)容類似:
- ERROR | [iOS] unknown: Encountered an unknown error (The 'Pods-App' target has transitive dependencies that include static binaries: (/private/var/folders/vx/466sl6v913z1xvbssfgpnjb00000gn/T/CocoaPods-Lint-20171220-2701-11qtxh8-MarketModule/Pods/XXXLib/libXXX.a)) during validation.
雖然這并不影響Pod的使用,但是驗證是無法通過的迎变〕湮荆可以通過 --use-libraries 來讓驗證通過:
pod repo push HACSpec MarketModule.podspec --allow-warnings --use-libraries
還有一個類似問題,如果在工程Podfile中使用use_frameworks!衣形,會報錯:
[!] The 'Pods-Venus' target has transitive dependencies that include static binaries: (/Users/hotacool/work/0-Venus/0-Venus/Pods/SZYDataSource/libMApi.a)
這里一個可行的辦法驼侠,只能注釋掉'use_frameworks!'。參考鏈接谆吴。
- 通配符地址匹配報錯
可能會出現(xiàn)類似報錯:
- ERROR | [iOS] file patterns: The `source_files` pattern did not match any file.
一般是文件路徑無法匹配造成倒源。例如:
s.source_files = ['HStockCharts/Classes/**/*.{c,h,hh,m,mm,cpp}' //可以匹配HStockCharts/Classes/A/A.m,但無法匹配HStockCharts/Classes/A.m句狼。
s.source_files = ['HStockCharts/Classes/**/*.{c,h,hh,m,mm,cpp}', 'HStockCharts/Classes/*.{c,h,hh,m,mm,cpp}'] //可以實現(xiàn)上述匹配
- 私有庫對私有庫的依賴
私有庫B需要依賴私有庫A笋熬,可以在B.podspec中設(shè)置:
s.dependency 'A'
如果執(zhí)行pod lib lint 或者pod repo push 會報錯找不到A。這是肯定的腻菇,默認的spec repo是cocoapods的公共庫胳螟,所以需要對source單獨設(shè)置:
pod lib lint --sources='https://gitee.com/hotacool/HACSpec.git'
pod repo push HACSpec B.podspec --sources='https://gitee.com/hotacool/HACSpec.git'
- 添加資源文件
有以下幾種添加資源方法:
// 1.默認方式,cocoapods會將匹配路徑'HStockCharts/Assets/*.png’下資源打包為名稱HStockCharts的bundle筹吐,代碼中通過bundle名來查找資源糖耸。
s.resource_bundles = {
'HStockCharts' => ['HStockCharts/Assets/*.png']
}
// 調(diào)用方式:
NSBundle *bundle = [NSBundle bundleForClass:[MYSomeClass class]];
NSURL *bundleURL = [bundle URLForResource:@"HStockCharts" withExtension:@"bundle"];
NSBundle *resourceBundle = [NSBundle bundleWithURL: bundleURL];
UIImage *img = [UIImage imageNamed:icon inBundle:bundle compatibleWithTraitCollection:nil];
// 或者
UIImage *image = [UIImage imageNamed:@"HStockCharts.bundle/imagename"];
// 2.資源會在打包的時候直接拷貝的main bundle中,可能會和其它資源產(chǎn)生命名沖突
spec.resources = ["Images/*.png", "Sounds/*"]
// 3.把資源都放在bundle中丘薛,然后打包時候這個bundle會直接拷貝進app的mainBundle中嘉竟。使用的時候在mainBundle中查找這個bundle然后再搜索具體資源
spec.resource = "Resources/MYLibrary.bundle"
//調(diào)用方式:
NSURL *bundleURL = [[NSBundle mainBundle] URLForResource:@"JZShare" withExtension:@"bundle"];
NSBundle *bundle = [NSBundle bundleWithURL:bundleURL];
UIImage *img = [UIImage imageNamed:icon inBundle:bundle compatibleWithTraitCollection:nil];
- pod lib lint 和 pod spec lint
兩者都是對私有庫進行validation。不過區(qū)別是:
pod lib lint是只從本地驗證你的pod能否通過驗證
pod spec lint是從本地和遠程驗證你的pod能否通過驗證
所以如果你使用pod spec lint洋侨,需要保證remote及時更新舍扰,否則會發(fā)現(xiàn)本地已經(jīng)做了修改,但一直無法validation成功...
更新維護podspec
在開發(fā)初始階段凰兑,可能會頻繁對podspec進行更改妥粟。在pod repo push后,記得及時pod repo update specName吏够,否則可能拉取不到最新的私有庫配置。mrc文件設(shè)置
某些文件需要設(shè)置mrc滩报,podspec可以如下設(shè)置:
non_arc_files = 'MyLib/Classes/xxx.m'
s.exclude_files = non_arc_files
s.subspec 'no-arc' do |sp|
sp.source_files = non_arc_files
sp.requires_arc = false
end
but锅知,不知是否是cocoapods的版本原因(1.3.1),采用這種方式我仍舊無法通過驗證脓钾。通過--no-clean售睹,進入驗證的APP工程,發(fā)現(xiàn)私有庫下的編譯文件只有non_arc_files文件可训。
懷疑是否默認使用no-arc 的subspec昌妹,之后參照鏈接捶枢,設(shè)置default_subspec和subspec。但仍舊無法驗證通過飞崖。
最后烂叔,只能通過將requires_arc設(shè)為mrc,單獨添加arc配置固歪,來實現(xiàn)對mrc文件的編譯蒜鸡。如下:
#source_files需要包含mrc和arc全部文件路徑
s.source_files = ["xxx/MRC/*.{c,h,hh,m,mm,cpp}", 'xxx/Classes/**/*.{c,h,hh,m,mm,cpp}', 'xxx/Classes/*.{c,h,hh,m,mm,cpp}']
s.requires_arc = false
s.requires_arc = ['xxx/Classes/**/*.{c,h,hh,m,mm,cpp}', 'xxx/Classes/*.{c,h,hh,m,mm,cpp}']
- pod 本地repo緩存清理
私有庫開發(fā)階段,會經(jīng)常修改文件牢裳,這時候極易出現(xiàn)remote上已更新逢防,但本地pod install/update 的私有庫還是老版本的情況。這往往是因為pod本地緩存導致蒲讯,需要更新本地緩存:
//1. 更新本地spec
pod repo update xxxSpec
//2. 去本地pods緩存文件夾(~/Library/Caches/CocoaPods/Pods/Release)忘朝,刪除需要更新的庫
open ~/Library/Caches/CocoaPods/Pods/
3. 項目集成私有庫
私有庫已搭建完成,私有庫的集成方式與cocoapods公共庫集成方式無異判帮,只需按照上述局嘁,在Podfile的source加入私有spec repo地址。無需贅述脊另。
這里提一下本地相對路徑的集成方式导狡。我們可以通過pod xxx,直接從remote clone私有庫到項目中偎痛。也可以通過pod 'HStockCharts', :path => '../'的方式旱捧,將本地庫鏈到工程中。對于需要開發(fā)的各個私有庫踩麦,可以將其clone到本地枚赡,然后本地相對路徑的方式鏈接到工程中,對私有庫進行修改谓谦,然后push到對應(yīng)私有庫贫橙。或許可以以此來解耦復雜工程反粥,結(jié)合腳本卢肃,搭建環(huán)境,完成業(yè)務(wù)的獨立開發(fā)才顿,這也是目前我做的事情莫湘。
4. 總結(jié)
這里總結(jié)了一下基于cocoapods的私有庫搭建,歸納了一些基本的方法郑气,記錄了一些遇到的問題幅垮。因為需求不大,涉及深度有限尾组。如在后續(xù)改造優(yōu)化過程中有更多的問題出現(xiàn)忙芒,也在此同步更新示弓。