基于CocoaPods的組件化原理及私有庫(kù)實(shí)踐

輪子為什么會(huì)存在

智人能在殘酷的進(jìn)化大戰(zhàn)中存活下來(lái),原因之一就是智人懂得將知識(shí)沉淀成外物,輔助彼此之間的合作,從而使得整個(gè)群體產(chǎn)生了規(guī)模效應(yīng),即1+1>2的效果。
從一個(gè)角度上說(shuō),石器時(shí)代是基于石器的組件化的時(shí)代,因?yàn)槔蠌埣业氖ɑ蚱渌^利器)借給了老王,一樣可以拿去狩獵。要想實(shí)現(xiàn)這個(gè)目的,一定要保證:

  1. 石矛足夠鋒利。不然冒然拿著石矛去找野獸就變成了給野獸送夜宵。
  2. 石矛容易使用。如果是石矛非常重或者難以抓起,也很難讓人使用。
image.png

一種觀點(diǎn)認(rèn)為,信息時(shí)代是基于軟件構(gòu)建起來(lái)的,由工程師不斷貢獻(xiàn)智力和體力,從而產(chǎn)生價(jià)值的時(shí)代。產(chǎn)品需求就好像前文說(shuō)到的獵物,完成需求類似于成功捕殺獵物,而產(chǎn)品逾期就好比被獵物吃掉。因此在這個(gè)時(shí)代,也需要一些可以好用且容易使用的功能代碼段,方便程序員拿來(lái)快速實(shí)現(xiàn)需求,就好比遠(yuǎn)古時(shí)代的可以復(fù)用的石矛。制作這種功能代碼段的過(guò)程叫做組件化,這種方法帶來(lái)的產(chǎn)出叫做組件,俗稱輪子。

上古時(shí)代的輪子

從本質(zhì)上說(shuō),組件是通過(guò)庫(kù)的方式來(lái)進(jìn)行封裝從而提供給開發(fā)者使用。而庫(kù),就是一種組織一個(gè)或多個(gè)文件的方式。在 iOS 8 之前,iOS 只支持以靜態(tài)庫(kù)的方式來(lái)使用第三方的代碼。

靜態(tài)庫(kù)

靜態(tài)庫(kù),在iOS中會(huì)被打包成.a文件,配合.h頭文件一起可以完成功能的調(diào)用。但是在在概念上,靜態(tài)庫(kù)是一種All In One的設(shè)計(jì)思路,因?yàn)橐蕾囲o態(tài)庫(kù)的代碼會(huì)把靜態(tài)庫(kù)完全鏈接到App的可執(zhí)行文件中。也就是說(shuō),靜態(tài)庫(kù)是在編譯器被鏈接到App中的,因此如果多個(gè)App都引用了同一個(gè)靜態(tài)庫(kù),則每個(gè)App都會(huì)把這個(gè)靜態(tài)庫(kù)鏈接一份,這其實(shí)浪費(fèi)了內(nèi)存。
當(dāng)然,靜態(tài)庫(kù)的缺點(diǎn)不止于此。在使用靜態(tài)庫(kù)時(shí),必須手動(dòng)一個(gè)個(gè)鏈接它依賴的外部庫(kù),例如早期微信支付SDK的靜態(tài)庫(kù)接入方法中,必須要手動(dòng)鏈接上:

SystemConfiguration.framework, 
libz.dylib, 
libsqlite3.0.dylib, 
libc++.dylib,
Security.framework, 
CoreTelephony.framework,
CFNetwork.framework

有沒有一種需要輪流背誦蒸羊羔、蒸熊掌、蒸鹿尾兒、燒花鴨、燒雛雞、燒子鵝、鹵豬。。。的既視感。
而且,靜態(tài)庫(kù)的特點(diǎn)導(dǎo)致了App每次啟動(dòng)時(shí)都要重新加載靜態(tài)庫(kù)的內(nèi)存,無(wú)法控制加載時(shí)機(jī),而且每次啟動(dòng)都需要重新加載靜態(tài)庫(kù),導(dǎo)致二次加載時(shí)間無(wú)法被優(yōu)化。
大部分時(shí)候,還需要在Other Linker Flags里填入Objc -all_load來(lái)確保靜態(tài)庫(kù)正常工作。
好吧,聽起來(lái)靜態(tài)庫(kù)很難用。
我們都知道,后期iOS支持了動(dòng)態(tài)庫(kù)。那動(dòng)態(tài)庫(kù)是不是就能完美解決問(wèn)題了呢?

動(dòng)態(tài)庫(kù)

動(dòng)態(tài)庫(kù),大部分會(huì)被打包成.tbd文件或者.dylib文件。不同于靜態(tài)庫(kù)在編譯期鏈接到App,動(dòng)態(tài)庫(kù)是在運(yùn)行時(shí)鏈接到App的,因此它有了三個(gè)好處:

  • 按需加載,什么時(shí)候需要運(yùn)行什么時(shí)候加載,提高了啟動(dòng)app的效率
  • 因?yàn)榇嬖诙鄠€(gè)app使用同一個(gè)動(dòng)態(tài)庫(kù)的情況,因此一旦某個(gè)動(dòng)態(tài)庫(kù)被加載到內(nèi)存中,下一個(gè)app使用時(shí)無(wú)需再次耗費(fèi)內(nèi)存加載此動(dòng)態(tài)庫(kù),大家公用一個(gè)動(dòng)態(tài)庫(kù)。
  • 因?yàn)閯?dòng)態(tài)庫(kù)不需要參與編譯過(guò)程,因此不會(huì)產(chǎn)生鏈接時(shí)符號(hào)沖突的問(wèn)題。

不過(guò),蘋果對(duì)動(dòng)態(tài)庫(kù)的完全支持僅停留在系統(tǒng)的動(dòng)態(tài)庫(kù)上,例如UI.framework,對(duì)于第三方的動(dòng)態(tài)庫(kù),還是需要embed到系統(tǒng)中。早期的一些熱更新框架,例如JSPatch鉆了漏子通過(guò)dlopen來(lái)進(jìn)行熱更新,不過(guò)很快被禁掉了。
不過(guò),如果是企業(yè)證書,還是可以在自己的app里靈活的加載第三方動(dòng)態(tài)庫(kù)的。

Framework

在解釋靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)的過(guò)程中,我并沒有提framework的字眼。有些開發(fā)者覺得framework文件就是動(dòng)態(tài)庫(kù),其實(shí)并不準(zhǔn)確。
我們提到的framework,指的是.framework文件,這既不一定是靜態(tài)庫(kù),也不一定是動(dòng)態(tài)庫(kù)。實(shí)際上這是一種打包方式,將Header(頭文件)、Binary(二進(jìn)制代碼文件)和bundle(資源文件)一起打包,方便開發(fā)者進(jìn)行接入和調(diào)用。
因此framework到底是靜態(tài)庫(kù)還是動(dòng)態(tài)庫(kù),取決于Binary文件(Mach-O文件)到底是靜態(tài)庫(kù)還是動(dòng)態(tài)庫(kù)。

痛點(diǎn)

“老一輩”的iOS開發(fā)都會(huì)記得手動(dòng)引入靜態(tài)庫(kù)時(shí),那無(wú)止境的編譯錯(cuò)誤。我簡(jiǎn)單總結(jié)一下,如果手動(dòng)引入靜態(tài)庫(kù),需要:

  • 將靜態(tài)庫(kù)和頭文件引入工程
  • 添加各依賴庫(kù)(不同版本下可能略有不同)
  • 修改Other_linker_flags,例如設(shè)置-ObjC,-fno-objc-arc等參數(shù)
  • 祈禱
  • 編譯,如果出問(wèn)題,從第一步進(jìn)行檢查
  • 如果沒有問(wèn)題,未來(lái)要手動(dòng)管理更新

程序員的創(chuàng)造力很多時(shí)候來(lái)源于“懶”,終于,CocoaPods橫空出世,從此開啟了一行命令行完成模塊集成的時(shí)代!

CocoaPods

簡(jiǎn)介

CocoaPods是iOS平臺(tái)當(dāng)前最流行的包管理工具,可以將它理解為一個(gè)可以自動(dòng)部署到項(xiàng)目的組件池,而對(duì)應(yīng)的podfile文件就相當(dāng)于請(qǐng)求組件的Request。當(dāng)組件下載到工程后,cocoaPods會(huì)自動(dòng)完成組件集成到現(xiàn)有項(xiàng)目的工作,并完成修改.xcodeproj文件和創(chuàng)建.xcworkspace文件。最終將所有組件統(tǒng)一打包成Pods.framework靜態(tài)庫(kù),供項(xiàng)目使用。

在CocoaPods中,會(huì)存在以下幾種文件:

  • podspec
    Pod的描述文件,一般來(lái)說(shuō)表征你的項(xiàng)目地址,項(xiàng)目使用的平臺(tái)和版本等信息
  • podfile
    用戶編寫的對(duì)于期望加載的pod以及對(duì)應(yīng)Target信息
  • podfile.lock
    記錄了之前pod加載時(shí)的一些信息,包括版本、依賴、CocoaPods版本等
  • mainfest.lock
    記錄了本地pod的基本信息,實(shí)際上是podfile.lock的拷貝
    大部分開發(fā)者最熟悉的cocoaPods指令就是pod install,那具體在執(zhí)行pod install時(shí)發(fā)生了什么呢?

pod install 運(yùn)行原理分析

當(dāng)我們運(yùn)行pod install時(shí),會(huì)發(fā)生:

  • 分析Dependency。
    對(duì)比本地pod的version和podfile.lock中的pod version,如果不一致會(huì)提示存在風(fēng)險(xiǎn)
  • 對(duì)比podfile是否發(fā)生了變化。
    如果存在問(wèn)題,會(huì)生成兩個(gè)列表,一個(gè)是需要Add的Pod(s),一個(gè)是需要Remove的Pod(s)。
  • (如果存在remove的)刪除需要Remove的Pods
  • 添加需要的Pod(s)。
    此時(shí),如果是常規(guī)的CocoaPods庫(kù)(如果基于Git),會(huì)先去:
    • Spec下查找對(duì)應(yīng)的Pod文件夾
    • 找到對(duì)應(yīng)的tag
    • 定位其Podspec文件
    • git clone下來(lái)對(duì)應(yīng)的文件(根據(jù)具體協(xié)議的不同,這里還可能存在以下幾種方式的download:Bazaar、Mercurial、HTTP、SCP、SVN)
    • copy到Pod文件夾中
    • 運(yùn)行pre-Install hook
  • 生成Pod Project
    • 將該P(yáng)od中對(duì)應(yīng)文件添加到工程中
    • 添加對(duì)應(yīng)的framework、.a庫(kù)、bundle等
    • 鏈接頭文件(link headers),生成Target
    • 運(yùn)行 post-install hook
  • 生成podfile.lock,之后生成此文件的副本,將其放到Pod文件夾內(nèi),命名為manifest.lock
    (如果出現(xiàn) The sandbox is not sync with the podfile.lock這種錯(cuò)誤,則表示manifest.lock和podfile.lock文件不一致),此時(shí)一般需要重新運(yùn)行pod install命令。
  • 配置原有的project文件(add build phase)
    • 添加了 Embed Pods Frameworks
    • 添加了 Copy Pod Resources

其中,pre-install hook和post-install hook可以理解成回調(diào)函數(shù),是在podfile里對(duì)于install之前或者之后(生成工程但是還沒寫入磁盤)可以執(zhí)行的邏輯,邏輯為:

pre_install do |installer| 
    # 做一些安裝之前的hook
end

post_install do |installer| 
    # 做一些安裝之后的hook
end

CocoaPods第三方庫(kù)下載邏輯

CocoaPods的下載流程
  • 首先,CocoaPods會(huì)根據(jù)Podfile中的描述進(jìn)行依賴分析,最終得出一個(gè)扁平的依賴表。
    這里,CocoaPods使用了一個(gè)叫做 Milinillo 的依賴關(guān)系解決算法。簡(jiǎn)單說(shuō)就是使用了回溯法來(lái)整理出所有第三方庫(kù)的一個(gè)依賴列表出來(lái),據(jù)說(shuō)是CoocaPods的開發(fā)工程師原創(chuàng)的算法,在解決問(wèn)題上應(yīng)該是夠用,但是貌似如果第三方庫(kù)復(fù)雜的時(shí)候會(huì)有性能問(wèn)題。這里美團(tuán)技術(shù)團(tuán)隊(duì)對(duì)此有專門的優(yōu)化,詳情請(qǐng)見 美團(tuán)外賣iOS多端復(fù)用的推動(dòng)、支撐與思考
  • 針對(duì)列表中的每一項(xiàng),回去Spec的Repo中查看其podSpec文件,找到其地址
  • 通過(guò)downloader進(jìn)行對(duì)應(yīng)庫(kù)的下載。如果地址為git+tag,則此步驟為git clone xxxx.git
    注意,此時(shí)必須要保證需要下載的pod版本號(hào)和git倉(cāng)庫(kù)的tag標(biāo)簽號(hào)一致。

所有依賴庫(kù)下載之后,便進(jìn)入了和Xcode工程的融合步驟。

Xcode工程有什么變化

Xcode工程上有什么變化

在cocoaPods和Xcode工程進(jìn)行集成的過(guò)程中,會(huì)有有以下流程

  • creat workspace
    創(chuàng)建xcworkspace文件。其實(shí)xcworkspace文件本質(zhì)上只是xcodeproject的集合,數(shù)據(jù)結(jié)構(gòu)如下:
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "group:Demo/Demo.xcodeproj">
   </FileRef>
   <FileRef
      location = "group:Pods/Pods.xcodeproj">
   </FileRef>
</Workspace>
  • create group
    在工程中創(chuàng)建group文件夾,邏輯上隔離一些文件

  • create pod project & add pod library
    創(chuàng)建pod.xcodeproject工程,并且將在podfile中定義的第三方庫(kù)引入到這個(gè)工程之中。

  • add embed frameworks script phase
    添加了[CP] Embed Pods Frameworks,相應(yīng)的,多了pods_xxx的group,下列xxx.framework.sh,來(lái)完成將內(nèi)部第三方庫(kù)打包成.a靜態(tài)庫(kù)文件(在Podfile中如果選擇了!use_frameworks,則此步驟會(huì)打包成.framework)


    [CP] Embed Pods Frameworks
  • remove embed frameworks script phase
    如果本次podfile刪除了部分第三方庫(kù),則此步驟會(huì)刪除掉不需要的第三方庫(kù),將其的引用關(guān)系從Pod.xcodeproject工程中拿走。

  • add copy resource script phase
    如果第三方庫(kù)存在資源bundle,則此步驟會(huì)將資源文件進(jìn)行復(fù)制到集中的目錄中,方便統(tǒng)一進(jìn)行打包和封裝。相應(yīng)的,會(huì)添加[CP] Copy Pods Resources腳本。


    [CP] Copy Pods Resources
  • add check manifest.lock script phase
    前文提到過(guò),manifest.lock其實(shí)是podfile.lock的副本。此步驟會(huì)進(jìn)行diff,如果存在不一致,則會(huì)提示著名的那句The sandbox is not sync with the podfile.lock錯(cuò)誤。

  • add user script phase
    此步驟是對(duì)原有project工程文件進(jìn)行改造。在運(yùn)行過(guò)pod install后,再次打開原有工程會(huì)發(fā)現(xiàn)無(wú)法編譯通過(guò),因?yàn)橐呀?jīng)做了改動(dòng)。

    • 首先,添加了對(duì)Pod工程的依賴,具體為引用中多了libPods_xxx.a文件。此步驟的.a文件(或者.framework文件)為上述步驟中xxx.framework.sh打包出來(lái)的文件,也就是說(shuō),cocoaPods會(huì)把所有第三方的組件封裝為一個(gè).a文件(或者.framework文件)!

      靜態(tài)文件引入

    • 建立了Pods的group,內(nèi)含pods-xxx-debug.xconfig和pods-xxx.release.xconfig文件。這兩個(gè)文件是對(duì)應(yīng)工程的build phase的配置。相應(yīng)的,主工程的Iinfo->Configurations的debug和release配置會(huì)對(duì)應(yīng)上述兩個(gè)配置文件。


      Configurations
    • 上述兩個(gè)配置都做了什么?包括:
      Header_search_path,指向了Pod/Headers/public/xxx,添加了Pods文件編譯后的頭文件地址
      Other_LDFLAGS,添加了-ObjC等等
      一些Pods變了,例如Pods_BUILD_DIR等

至此,原有xcode工程和新建的Pod工程完成了集成和融合。

好了,cocoaPods的好處和原理已經(jīng)介紹的差不多了。大部分時(shí)間,我們通過(guò)引用github上的組件就夠用了。但是有時(shí)候處于業(yè)務(wù)需要,我們需要來(lái)實(shí)現(xiàn)私有Pod庫(kù)。所以接下來(lái)我們來(lái)介紹下如何在公司內(nèi)網(wǎng)來(lái)實(shí)現(xiàn)一個(gè)私有庫(kù),實(shí)現(xiàn)一個(gè)私有組件。

利用CocoaPods實(shí)現(xiàn)私有組件

準(zhǔn)備工作

  • 安裝好XCode
  • 配置好CocoaPods,并且可以pod update 以及 pod install 成功
  • 已經(jīng)獲得CocoaPods的Repo的地址,以及對(duì)應(yīng)pod的Git地址(這里以git.xxx.com上申請(qǐng)的repo為例)
  • 涉及到的所有操作,請(qǐng)盡量在Terminal中進(jìn)行,包括CocoaPods的相關(guān)操作(不要在CocoaPods官方客戶端操作)
  • 本文涉及到的Demo,可以去https://git.xxx.com/XXX_SPA_XXX/HelloXXXPod去圍觀

私有Spec Repo

所謂Spec Repo,就是Pods的索引。一旦在podfile中設(shè)置source為某個(gè)私有repo的git地址,在進(jìn)行pod update的時(shí)候就會(huì)去這個(gè)repo中進(jìn)行檢索,如果檢索到對(duì)應(yīng)的pod,會(huì)讀取該P(yáng)od的podspec從而進(jìn)行安裝。
一個(gè)Spec Repo的目錄結(jié)構(gòu)如下:

image.png

之后我們?nèi)it.xxx.com上新建一個(gè)相應(yīng)的Repo地址,之后添加repo到本地,該repo地址是為了后面提交podspec使用。

# pod repo add [Private Repo Name] [GitHub HTTPS clone URL]
pod repo add XXXCocoaPodsRepo git@git.xxx.com:XXX_SPA_XXX/iOS_CocoaPods_Repo.git

成功后可以進(jìn)入~/.cocoapods/repos目錄下查看XXXCocoaPodsRepo這個(gè)目錄了。

創(chuàng)建并Clone目標(biāo)Pod地址

這里,我們以HelloXXXPod為例。
去git.xxx.com上去新建項(xiàng)目,之后獲取地址,為:

git@git.xxx.com:XXX_SPA_XXX/HelloXXXPod.git

此時(shí)clone到本地,命令為:

git clone git@git.xxx.com:XXX_SPA_XXX/HelloXXXPod.git

創(chuàng)建Pod項(xiàng)目工程文件(源碼方式)

這里建議通過(guò)CocoPods的官方命令來(lái)進(jìn)行Pod項(xiàng)目的創(chuàng)建,以測(cè)試項(xiàng)目HelloXXXPod為例,命令如下:

pod lib create HelloXXXPod

不出意外地話,會(huì)提問(wèn)你六個(gè)問(wèn)題(cocoaPods v1.5.3版本):

1.What platform do you want to use? [ iOS / macOS ]

2.What language do you want to use? [ Swift / ObjC ]

3.Would you like to include a demo application with your library? [ Yes / No ]

4.Which testing frameworks will you use? [ Specta / Kiwi / None ]

5.Would you like to do view based testing? [ Yes / No ]

6.What is your class prefix?

分別解釋一下

  • What platform do you want to use?? [ iOS / macOS ]
    問(wèn)組件化應(yīng)用在哪個(gè)平臺(tái)上,一般我們選iOS

  • What language do you want to use? [ Swift / ObjC ]
    使用何種語(yǔ)言,可以根據(jù)項(xiàng)目是OC還是Swift自行選擇

  • Would you like to include a demo application with your library? [ Yes / No ]
    問(wèn)是否需要一個(gè)Demo工程,方便調(diào)試Pod。如果是第一次做組件化,建議選Yes,方便pod的調(diào)試

  • Which testing frameworks will you use? [ Specta / Kiwi / None ]
    問(wèn)是否需要UT測(cè)試框架,可選擇Specta和Kiwi,或者選擇不要。

  • Specta是OC的一個(gè)輕量級(jí)TDD/BDD框架,參考github/specta

  • Kiwi是一個(gè)iOS的一個(gè)BDD框架,可以簡(jiǎn)單地部署和使用。github/kiwi
    UT測(cè)試框架如果要選擇的話,建議選擇Kiwi,可以參考我之前寫的調(diào)研kiwi上手體驗(yàn)
    本次的Demo,暫時(shí)選None

  • Would you like to do view based testing? [ Yes / No ]
    如果上一步選擇了Specta ,這步會(huì)生成一部分有利于做自動(dòng)化測(cè)試的邏輯和代碼

  • What is your class prefix?
    這里可以指定你的項(xiàng)目前綴,這樣在new一個(gè)類時(shí)會(huì)自動(dòng)加上前綴

之后我們運(yùn)行pod install,生成的文件目錄樹結(jié)構(gòu)如下:

$ tree HelloXXXPod -L 2

HelloXXXPod
├── Example
│   ├── Build
│   ├── Podfile
│   ├── Podfile.lock
│   ├── Pods
│   ├── Tests
│   ├── helloXXXPod
│   ├── helloXXXPod.xcodeproj
│   └── helloXXXPod.xcworkspace
├── LICENSE
├── README.md
├── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
├── helloXXXPod
│   ├── Assets
│   └── Classes
└── helloXXXPod.podspec

開發(fā)

這時(shí)候可以在剛才生成的Example工程內(nèi)做開發(fā),這時(shí)候記得把新建的代碼放到Classes目錄下。如果有圖片資源,建議放到Assets下。

開發(fā)、調(diào)試完成之后,就可以去編輯podspec文件了。按以下方式來(lái)修改,不明白的字段請(qǐng)參考官方文檔

這里給出本次Demo的podspec供各位參考:


Pod::Spec.new do |s|
  s.name             = 'helloXXXPod'
  s.version          = '0.1.0'
  s.summary          = 'A short description of helloXXXPod.'
  s.description      = <<-DESC
TODO: Add long description of the pod here.
                       DESC

  s.homepage         = 'https://git.xxx.com/XXX_SPA_XXX/HelloXXXPod'
  s.license          = { :type => 'MIT', :file => 'LICENSE' }
  s.author           = { 'nimomeng' => 'nimomeng@tencent.com' }
  s.source           = { :git => 'git@git.xxx.com:XXX_SPA_XXX/HelloXXXPod.git', :tag => s.version.to_s }

  s.ios.deployment_target = '8.0'
  s.source_files = 'helloXXXPod/Classes/**/*'
end

其中,注意修改這幾個(gè)字段:

  • s.name
  • s.homepage
  • s.source (非常重要)
  • s.source_files (如果不放在Classes下,記得在這里指定文件目錄)

本地調(diào)試

如果是通過(guò)pod lib create命令創(chuàng)建的Pod,會(huì)在Example中自動(dòng)配置好該pod的本地調(diào)試腳本,如下:

use_frameworks!

platform :ios, '8.0'

target 'helloXXXPod_Example' do
  pod 'helloXXXPod' :path => '../'

  target 'helloXXXPod_Tests' do
    inherit! :search_paths

    
  end
end

其中,pod 'helloXXXPod' :path => '../'的含義是說(shuō),在上層目錄來(lái)下載helloXXXPod這個(gè)pod。這是本地調(diào)試Pod的一種。
同樣的,可以實(shí)現(xiàn)類似方式調(diào)試的方法,還有通過(guò):podspec命令來(lái)指定,指定pod所在的podspec文件位置即可

其中,path語(yǔ)法精確到目錄即可;podspec語(yǔ)法必須要精確到文件。

設(shè)置好podfile之后,在Example文件下執(zhí)行pod install,則可以發(fā)現(xiàn)新的文件已經(jīng)出現(xiàn)在項(xiàng)目工程的pods文件夾之下了。

image.png

注意,通過(guò)path語(yǔ)法進(jìn)行更新后,Pod中代碼并不在Pod文件夾中,而是在一個(gè)叫 Development Pods中。

開發(fā)完成,需要本地驗(yàn)證podspec,確保其有效:

pod lib lint helloXXXPod.podspec

同步到Git上

之后要做的就是把庫(kù)同步到Git上去了。這時(shí)候需要去git.xxx.com上建立一個(gè)對(duì)應(yīng)的倉(cāng)庫(kù),例如:

http://git.xxx.com/XXX_SPA_XXX/HelloXXXPod.git (替換為自己的實(shí)際git地址)

然后將代碼同步到此Git上。

git add .

git commit -m "Init"

git remote add origin http://git.xxx.com/XXX_SPA_XXX/HelloXXXPod.git(替換為自己的實(shí)際git地址)

git push --set-upstream origin master

podSpec文件需要版本控制信息,所以我們要打一個(gè)Tag.

git tag -m "first demo" 0.1.0

git push --tags

向Spec Repo提交podspec

在執(zhí)行本歩之前,確保最新代碼已經(jīng)提交到了Git上,且已經(jīng)打好了tag.

向Spec Repo提交podspec的命令:

pod repo push XXXCocoaPodsRepo HelloXXXPod.podspec --allow-warnings

在經(jīng)過(guò)三輪的用戶校驗(yàn)之后,提交成功!這時(shí)候我們?nèi)?code>~/.cocoapods/repos/XXXCocoaPodsRepo中查看,我們的的podspec已經(jīng)在里面了!

此時(shí)通過(guò)pod search HelloXXXPod 已經(jīng)可以查到了!

image.png

最后,為了保證本地的repo已經(jīng)被更新,運(yùn)行pod update來(lái)更新repo

如何在外部項(xiàng)目中使用

我們可以在想要使用的項(xiàng)目中的Podfile里加入如下代碼:

pod 'helloXXXPod'

即可。
當(dāng)然,由于我們的是私有CocoaPods庫(kù),因此最好告訴系統(tǒng)這個(gè)庫(kù)的source在哪里,因此在Podfile文件上部也請(qǐng)加上Spec Repo的git地址。同時(shí),為了確保公共的cocoaPod可以被正常下載,請(qǐng)?zhí)砑油獠緾ocoaPod的庫(kù):

# For inner pods
source 'git@git.xxx.com:XXX_SPA_XXX/iOS_CocoaPods_Repo.git'

# For public pods
source 'https://github.com/CocoaPods/Specs.git'

整個(gè)的Podfile文件看起來(lái)是這樣的:

use_frameworks!

platform :ios, '8.0'

# source 'git@git.xxx.com:XXX_SPA_XXX/iOS_CocoaPods_Repo.git'

# For public pods
source 'https://github.com/CocoaPods/Specs.git'

target 'helloXXXPod_Example' do
  pod 'helloXXXPod'

  target 'helloXXXPod_Tests' do
    inherit! :search_paths
    
  end
end

之后運(yùn)行pod install 即可安裝對(duì)應(yīng)的Pods

驗(yàn)證

我們可以復(fù)用Example項(xiàng)目,只不過(guò)這次不再通過(guò):path命令或者:podspec命令來(lái)做本地調(diào)用,而是完全使用安裝外部pod的方式,即:

  pod 'helloXXXPod'

注意:雖然pod已經(jīng)推送到線上,但是本地一定要先更新pod的repo,不然還是無(wú)法找到最新的pod。確保先做pod update操作。

Example項(xiàng)目中,我們調(diào)用在Pod中寫好的方法,查看是否輸入對(duì)應(yīng)的log即可驗(yàn)證:

image.png

至此,Pod創(chuàng)建完成。

常見問(wèn)題

  • 如果pod中用到framework,應(yīng)該在哪里添加?

    如果pod中用到framework,如AVFoundation,直接在podspec文件中添加s.frameworks = ‘AVFoundation’或者s.frameworks = [‘AVFoundation’,'MapKit'],而不應(yīng)該添加在項(xiàng)目的Link Binary With Libraries下面。

  • 怎么取更新私有 pod?

    更新私有pod的過(guò)程和創(chuàng)建pod的步驟一致,但是要記得在更改代碼后要記得一定重新run一下aggregate,更改podspec里的s.version(因?yàn)閠ag不能重復(fù)提交), 重新pod repo push

  • 如果出現(xiàn)這個(gè)錯(cuò)誤怎么辦:

[!] An unexpected version directory `Assets` was encountered for the `/Users/nimo/.cocoapods/repos/xxxx` Pod in the `xxxx` repository.

這個(gè)錯(cuò)誤,請(qǐng)查看:

  • podspec 是否未上傳到服務(wù)器
  • Podfile的source地址是否是Spec Repo的地址,而不是具體某一個(gè)Pod的地址。

參考文章

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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