CocoaPods使用總結

使用CocoaPods也有很長一段時間了,最近幾個月的時間里也主導了公司私有Pods的創建和使用。在此期間踩過了不少坑,在踩坑的過程中也收獲了不少經驗,更加熟練地掌握了CocoaPods的一些指令的使用。本篇作為這段時間收獲的備忘。

一、CocoaPods簡介

CocoaPods是專門為iOS工程提供第三方依賴庫的管理工具,通過CocoaPods,我們可以更方便地管理每個第三方庫的版本,而且不需要我們做太多的配置,就可以直觀、集中和自動化地管理我們項目的第三方庫。

CocoaPods將所有依賴的庫都放在一個名為Pods的項目下,然后讓主項目依賴Pods項目。然后,我們編碼工作都從主項目轉移到Pods項目。Pods項目最終會編譯為一個libPod-項目名.a靜態庫,主項目依賴于這個靜態庫。

對于資源文件,CocoaPods 提供了一個名為 Pods-resources.sh 的 bash 腳本,該腳本在每次項目編譯的時候都會執行,將第三方庫的各種資源文件復制到目標目錄中。

CocoaPods 通過一個名為 Pods.xcconfig 的文件來在編譯時設置所有的依賴和參數。

CocoaPods是用 Ruby 寫的,并由若干個 Ruby 包 (gems) 構成的。在解析整合過程中,最重要的幾個 gems 分別是: CocoaPods/CocoaPods, CocoaPods/Core, 和 CocoaPods/Xcodeproj

CocoaPod的核心組件

  • CocoaPods/CocoaPod
    這是一個面向用戶的組件,每當執行一個 pod 命令時,這個組件都將被激活。該組件包括了所有使用 CocoaPods 涉及到的功能,并且還能通過調用所有其它的 gems 來執行任務。

  • CocoaPods/Core
    Core 組件提供支持與 CocoaPods 相關文件的處理,文件主要是 Podfile 和 podspecs。

  • Podfile
    Podfile 是一個文件,用于定義項目所需要使用的第三方庫。該文件支持高度定制,你可以根據個人喜好對其做出定制。更多相關信息,請查閱 Podfile 指南。

  • Podspec
    .podspec 也是一個文件,該文件描述了一個庫是怎樣被添加到工程中的。它支持的功能有:列出源文件、framework、編譯選項和某個庫所需要的依賴等。

  • CocoaPods/Xcodeproj
    這個 gem 組件負責所有工程文件的整合。它能夠創建并修改 .xcodeproj 和 .xcworkspace 文件。它也可以作為單獨的一個 gem 包使用。如果你想要寫一個腳本來方便地修改工程文件,那么可以使用這個 gem。

CocoaPods的安裝和配置,以及Podfile中第三方庫引用的語法規則(特別是版本號的語法格式)這里就不贅述了,下面挑重點講一講。

二、多target時Podfile該如何寫?

我的建議是使用Ruby語法,定義不同的分組,然后不同的target可以自由選擇依賴哪些分組,這種方式看起來更簡潔,對于多target的項目來說也更友好:

platform :ios, '8.0'

def commonPods #通用pods集
    pod 'AFNetworking', '~> 2.0'
    pod 'Masonry'
end

def appOnlyPods #app專用pods集
    pod 'MBProgressHUD'
end

def extensionPods #擴展專用pods集
    pod 'GTSDKExtension'
end

target :TestCocoaPods do
    commonPods
    appOnlyPods

    target :TestCocoaPodsTests do
    inherit! :search_paths
    # Pods for testing
    end

    target :TestCocoaPodsUITests do
        inherit! :search_paths
        # Pods for testing
    end
end

target :SecondTarget do
    commonPods
end

三、如何忽略Pods警告?

有些第三方Pod集成進來會有一大堆警告信息,如果你看著比較難受想把它忽略的話,在Podfile中對應的target或分組下加上關鍵字inhibit_all_warnings即可。

四、如何直接引用第三方庫中的頭文件?

在用CocoaPods集成第三方庫之后,默認情況下,我們需要使用類似#import <XXX/YYY.h>的方式引入第三方庫的頭文件。我們可以在Build Settings -> User Header Search Paths中添加${SRCROOT}并設置成recursive,這樣我們就可以直接使用#impot "YYY.h"這種方式了。

五、pod install or pod update?

如官方文檔所說,pod installpod update確實是大家最容易搞混的兩條指令,很多人還沒搞清楚這兩條指令的區別,反正不管三七二十一上來就是一個pod update,大家一定要搞清楚這兩條指令的區別。

按照官方文檔所說,pod install在第一次檢索集成第三方以及每一次在Podfile中新增、更改或刪除pod的時候使用。每一次執行pod install命令,它都會下載安裝新的pod,并且會把每一個安裝的pod的版本信息寫入Podfile.lock文件。Podfile.lock文件跟蹤每一個安裝的pod的版本并且上鎖。每一次執行pod install命令,只解決還沒有在Podfile.lock中列出的依賴:對于已在Podfile.lock中列出的pod,會下載指定的版本,不會檢查是否有新版本。對于沒有在Podfile.lock中列出的pod,它會搜索并安裝Podfile中指定的版本。

直接執行pod update命令會檢查安裝Podfile中列出的所有pod的新版本(往往比較慢)。

執行pod update PODNAME命令會檢查PODNAME的新版本(不考慮Podfile.lock中記錄的版本信息),它會把PODNAME更新為最新版本,只要跟Podfile中指定的版本匹配。也就是說,pod update PODNAME將PODNAME更新到Podfile中指定的版本,可以是更新到老版本也可以是更新到新版本,取決于Podfile。(比如:如果此時Podfile中指定了pod 'AFNetworking', '~> 2.0',此時執行pod update AFNetworking并不會把AFNetworking更新到最新版本(因為此時的版本滿足大于等于2.0版),必須先修改Podfile中的版本信息才會更新到指定版本)。

兩者的區別:

  • pod install命令來安裝新的pod,每次在Podfile中新增和刪除pod都使用pod install命令。

  • 在Podfile中添加新的pod后應該用pod install命令,而不是pod update命令。通過pod install命令安裝新的pod而不用擔心在同一進程中修改已有的pod。

  • pod update命令僅用在更新指定pod到指定版本或者更新所有pod。

我的建議是:該用pod install的時候不要用pod update PODNAME。另外,盡量不要用pod update,因為它是全部檢查一遍,不僅慢有時候還會出現坑。比如有一個依賴的第三方庫本來是2.0版本的用的好好的,因為它是國外的資源,下載起來非常慢,我們在沒有bug的情況下是不希望輕易去更新它的,那么如果你上來就是一個pod update指令,OK, 如果你Podfile中指定了每次使用最新版本(不指定版本號),那么CocoaPods就會去下載最新的這個第三方庫,那在下載完成之前你還要不要做其他事情了?這還是情況好的,如果這個最新的版本一直下載失敗,所以一直集成失敗怎么辦?

六、如何創建私有Pod?

要創建私有Pod,首先我們需要兩個私有倉庫,一個放私有Pod源碼,一個放私有Pod的說明書(類似公有Pod的CocoaPods/Specs)。

1、添加私有Spec倉庫到本地

pod repo add privateSpecs your_privateSpecs.git

如果執行成功,之后便可以通過pod repo list命令查看本地Spec倉庫列表,正常情況下會有一個公有的CocoaPods官方的master repo 和你的 privateSpecs repo,并可以看到它們在本地的存放路徑(其實在~/.cocoapods/repos目錄下)。

2、創建私有Pod

在私有Pod代碼所在文件夾下執行pod spec create your_podName在該目錄下創建一個your_podName.podspec說明書文件。之后的工作就是編輯這個說明書文件了,這里簡單注明一下規則:

Pod::Spec.new do |s|

  s.name         = "ATCategory"
  s.version      = "0.0.1"
  s.summary      = "共用擴展類集合"
  s.description  = <<-DESC
  大家如果需要用到擴展,都使用這里已有的擴展啦。
                   DESC
  s.homepage     = "your_privatePodGit_address/ATCategory"
  s.author       = { "ApesTalk" => "https://github.com/apestalk" }
  s.platform     = :ios
  s.platform     = :ios, "8.0"
  s.source       = { :git => "your_privatePodGit_address", :tag => "#{s.version}"
  # 如果你有多個私有Pod放在一個倉庫里,你可以修改tag像下面這樣,對應打tag的時候的規則就對應需要變成PodName-v0.0.1這樣子了
  # s.source       = { :git => "your_privatePodGit_address", :tag => s.name + "-v"+"#{s.version}"
}
  s.source_files  = 'ATCategory/**/*'
  s.public_header_files = 'ATCategory/Category/*.h'
  s.requires_arc = true
  s.frameworks = 'UIKit','Foundation'

# 依賴的系統library,這里是指系統的類似libz.tbd、libxml2.tbd這類的系統庫
# s.library = 'z' // 單個
# s.libraries = 'z','xml2' // 多個

# 第三方.a
# s.vendored_libraries =
# 第三方frameworks文件
# s.vendored_frameworks =
# 依賴關系,該項目所依賴的其他庫,如果有多個需要填寫多個s.dependency
# s.dependency 'AFNetworking', '~> 2.3'
# 資源文件地址
# s.resource_bundles = {
#   'ATCategory' => ['ATCategory/Images/*.png']
# }
end

3、提交源代碼并打tag

注意這里tag必須跟podspec文件中的tag保持一致,因為CocoaPods是通過podspec文件中的tag去找源文件的,如果tag對應不起來就會驗證失敗。打好tag提交到遠端。

4、驗證podspec文件合法性和可選參數

有兩種驗證方式,一種是本地驗證pod lib lint your_podName.podspec和聯網驗證pod spec lint your_podName.podspec。建議大家都用聯網驗證。

這里可選參數有:

  • --allow-warnings:允許警告
  • --sources=‘master,privateSpecs:指定源,比如你的私有pod同時依賴了公有庫和私有庫,你必須指定源才行,因為默認只會去在公有源中查找對應的依賴
  • --use-libraries:如果使用了靜態庫,記得加上它

5、提交說明書文件到私有說明書庫

pod repo push privateSpecs your_podName.podspec,同樣的加上上面驗證時使用到的可選參數。

6、如何使用素材?

官方建議pod中的素材用bundle的形式避免和主項目中的文件名發生沖突,那么集成后我們如何使用bundle中的素材呢?

NSBundle *bundle = [NSBundle mainBundle];
//NSBundle *bundle = [NSBundle bundleForClass:[ClassFromPodspec class]];//對于靜態庫,拿到的是mainBundle,如果是動態庫,拿到的是類所在的bundle。使用動態庫需要在Podfile中開啟use_frameworks!
NSURL *wttpodBundleURL = [bundle URLForResource:@"WTTPod" withExtension:@"bundle"];
NSBundle *wttpodBundle = [NSBundle bundleWithURL: wttpodBundleURL];
UIImage *img = [UIImage imageNamed:@"Chat_checkin_empty_stu" inBundle:wttpodBundle compatibleWithTraitCollection:nil];

7、關于subspec

如果我們的pod中文件比較多,而我們又希望能像AFNetworking那樣集成后分幾個物理文件夾(默認會把所有文件都放在一個物理文件夾下,文件太多會顯得很亂),那么就要用到subspec來把我們的pod分成幾個獨立的子模塊。


s.subspec '子模塊名稱' do |別名,不能和子模塊名稱相同,比如ss|

ss.source_files = ''

end

具體怎么用,大家可以參考AFNetworking.podspec文件中的寫法。

七、如何使用私有庫

如果我們同時使用了公有庫和私有庫,我們只需要在Podfile的頭部同時把公有庫和私有庫的source加上即可。

八、pod search搜不到私有庫?或者搜得到pod install失敗?

在提交私有庫說明書之后,先執行一下pod repo update privateSpecs,然后再集成。

九、集成某一個pod速度過慢,比如MobileVLCKit總是下載失敗

把對應版本的MobileVLCKit下載下來放在訪問速度更快的地方(比如內網服務器或者本機用Python開啟一個FTP服務)在本機master repo源中搜索找到對應版本的MobileVLCKit.podspec.json文件,把其中的source改成我們存放MobileVLCKit.tar.xz文件地址,之后再執行相關指令集成。

如何利用Python開啟一個本地FTP服務:

cd到要共享的目錄下,執行python -m SimpleHTTPServer 8000,之后同一個局域網內就可以通過本機ip:8000訪問到該共享文件夾了。


"http":"http://192.168.210.111:8000/MobileVLCKit-3.1.2-bf58e19-37855b857a.tar.xz"

獲取本機IP地址的方法:按住Option的同時點下Mac菜單欄的無線網Icon,在下拉列表中即可看到IP地址。也可以在終端中輸入ifconfig en0命令查看。

十、驗證podspec時,報錯 symbol(s) not found for architecture i386

檢查一下是否私有Pod中使用到的什么文件不支持i386架構,比如什么.a文件。

解決辦法:在podspec文件中指定支持的架構

valid_archs = ['armv7s','arm64',]
s.xcconfig = {
  'VALID_ARCHS' =>  valid_archs.join(' '),
}
s.pod_target_xcconfig = {
    'ARCHS[sdk=iphonesimulator*]' => '$(ARCHS_STANDARD_64_BIT)'
}

同樣的,如果在驗證過程中遇到xcodebuild: Returned an unsuccessful exit code.卻不報具體的錯,去看看NOTE類型的信息,如果看到missing required architecture i386 in file此類消息,說明某個.a或者frawork不支持i386架構,需要在podspec文件中寫明該pod支持哪些架構。

十一、pod init失敗?

用Xcode9.4.1新建一個項目,然后執行pod init(Cocoapods1.4.0版本)時提示失敗,錯誤提示如下:


――― MARKDOWN TEMPLATE ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

### Command


/usr/local/Cellar/cocoapods/1.4.0/libexec/bin/pod init


### Report

* What did you do?

* What did you expect to happen?

* What happened instead?


### Stack


   CocoaPods : 1.4.0
        Ruby : ruby 2.3.3p222 (2016-11-21 revision 56859) [universal.x86_64-darwin17]
    RubyGems : 2.5.2
        Host : Mac OS X 10.13.3 (17D47)
       Xcode : 9.4.1 (9F2000)
         Git : git version 2.15.2 (Apple Git-101.1)
Ruby lib dir : /System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/lib



### Plugins


cocoapods-deintegrate : 1.0.2
cocoapods-plugins     : 1.0.0
cocoapods-search      : 1.0.0
cocoapods-stats       : 1.0.0
cocoapods-trunk       : 1.3.0
cocoapods-try         : 1.1.0


### Error


RuntimeError - [Xcodeproj] Unknown object version.
/usr/local/Cellar/cocoapods/1.4.0/libexec/gems/xcodeproj-1.5.4/lib/xcodeproj/project.rb:217:in `initialize_from_file'
/usr/local/Cellar/cocoapods/1.4.0/libexec/gems/xcodeproj-1.5.4/lib/xcodeproj/project.rb:102:in `open'
/usr/local/Cellar/cocoapods/1.4.0/libexec/gems/cocoapods-1.4.0/lib/cocoapods/command/init.rb:41:in `validate!'
/Library/Ruby/Gems/2.3.0/gems/claide-1.0.2/lib/claide/command.rb:333:in `run'
/usr/local/Cellar/cocoapods/1.4.0/libexec/gems/cocoapods-1.4.0/lib/cocoapods/command.rb:52:in `run'
/usr/local/Cellar/cocoapods/1.4.0/libexec/gems/cocoapods-1.4.0/bin/pod:55:in `<top (required)>'
/usr/local/Cellar/cocoapods/1.4.0/libexec/bin/pod:22:in `load'
/usr/local/Cellar/cocoapods/1.4.0/libexec/bin/pod:22:in `<main>'


――― TEMPLATE END ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

[!] Oh no, an error occurred.

Search for existing GitHub issues similar to yours:
https://github.com/CocoaPods/CocoaPods/search?q=%5BXcodeproj%5D+Unknown+object+version.&type=Issues

If none exists, create a ticket, with the template displayed above, on:
https://github.com/CocoaPods/CocoaPods/issues/new

Be sure to first read the contributing guide for details on how to properly submit a ticket:
https://github.com/CocoaPods/CocoaPods/blob/master/CONTRIBUTING.md

Don't forget to anonymize any private data!

Looking for related issues on cocoapods/cocoapods...
 - Pod Update: RuntimeError - [Xcodeproj] Unknown object version. Xcode Beta 5
   https://github.com/CocoaPods/CocoaPods/issues/8003 [closed] [17 comments]
   a day ago

 - RuntimeError - [Xcodeproj] Unknown object version.
   https://github.com/CocoaPods/CocoaPods/issues/7697 [closed] [28 comments]
   3 weeks ago

 - Pod init. Unknown object version
   https://github.com/CocoaPods/CocoaPods/issues/7907 [closed] [2 comments]
   03 Jul 2018

and 42 more at:
https://github.com/cocoapods/cocoapods/search?q=[Xcodeproj]%20Unknown%20object%20version.&type=Issues&utf8=?

這種失敗原因是Cocoapods和xcodeproj版本兼容問題。

嘗試了網上的解決辦法Run gem install xcodeproj:1.4.1,依然失敗。

解決辦法:打開項目,在Project Document下將Project Format從Xcode 9.3-compatible修改為Xcode 8.0-compatible即可。

Xocde_project_format

十二、在pod中引入項目文件報錯(file not found)

在開發中有時候需要在pod中import項目中的文件進行調試或測試(當然這種情況比較少見),但是當你輸入import的時候會發現系統根本沒法聯想到你想用的項目中的文件,即使你手動寫入,也會報file not found的錯誤。

此時,可以在我們的Pods項目中的Build settings下找到 User Header Search Paths,添加一行,并設置$(SRCROOT)/..為recursive。注意有/..,意思是在上級目錄下遞歸查找文件。

十三、清除本地緩存(version重用)

如果在某個tag下(假設WTTestPodCache-v0.0.1)驗證私有pod失敗,修改代碼后,我們還希望使用這個版本號而不是去修改podspec文件中的version字段。我們就需要清除Pod的本地緩存了,否則會因為該tag對應的緩存問題,還是一樣驗證不過。這時候我們需要先把代碼修改正確后提交,然后把本地和遠程的原tag刪除,最后在最新代碼節點上打一個相同的tag提交再次提交驗證。---參考:CocoaPods清理本地緩存

我們在驗證的時候加上--verbose參數,會看到CocoaPods有如下拷貝操作:

copyfromto.png

copying XXX from 后面跟著的就是本地緩存的地址,進入該路徑下會看到WTTestPodCache,我們刪除本地和遠程tag后,本地該緩存依然存在,當執行pod cache clean WTTestPodCache后,本地該緩存就不存在了,在驗證私有Pod時CocoaPods就會嘗試再次從遠程拉取。

  • 查看本地緩存列表 pod cache list

  • 清除本地緩存 pod cache clean XXX

十四、集成MobileVLCKit時報錯Lzma library error

如果在驗證pod,安裝某個庫時報Lzma庫的錯誤:

ERROR | [iOS] unknown: Encountered an unknown error ([!] /usr/bin/tar xf /var/folders/tf/jlbz0pq91m16571fgc7ywbk80000gn/T/d20191016-38557-1l50cyi/file.txz -C /var/folders/tf/jlbz0pq91m16571fgc7ywbk80000gn/T/d20191016-38557-1l50cyi

MobileVLCKit-binary/MobileVLCKit.framework/MobileVLCKit: Lzma library error:  No progress is possible
tar: Error exit delayed from previous errors.

這屬于pod依賴的這個庫的壓縮包解壓失敗了,Lzma library是用來解壓用的,如果壓縮包損壞了,會導致解壓失敗,從而報這個錯誤,此時需要檢查壓縮包是否正常。

十五、本地多個Ruby環境導致pod執行失敗

如果執行任意pod指令都失敗,失敗信息像下面這樣:

Ignoring ffi-1.13.1 because its extensions are not built. try: gem pristine ffi —version 1.13.1
Ignoring gem-wrappers-1.4.0 because its extensions are not builts. Try: gem pristine gem-wrappers —version 1.4.0
Traceback (most recent call last):
    23: from /USR/LOCAL/BIN/pod:23:in `<main>`
    22: from /USR/LOCAL/BIN/pod:23:in `load`
    21: from /Users/username/.rvm/rubies/ruby-2.6.0/lib/ruby/gems/2.6.0/gems/cocoapods-1.9.3/bin/pod:36:in `<top (required)>`
    20: from /System/Library/Frameworks/Ruby.frameworks/Versions/2.6/usr/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require`

如果是這樣子的話,根據錯誤提示說明是本地有多個Ruby環境導致的,應該是中途什么時候看了一些教程安裝了rvm然后又裝了個Ruby,刪除rvm目錄下的Ruby即可。

十六、慎用--skip-import-validation與--skip-tests

如果pod怎么都驗證不過,始終只有這么一條提示([iOS] xcodebuild: Returned an unsuccessful exit code),沒有任何其他ERROR信息,可以加上這兩個選項了讓pod驗證通過。
--skip-tests: 在驗證期間跳過構建和運行測試
--skip-import-validation: 跳過驗證pod是否可以導入

十七、pod install報錯

/.rvm/rubies/ruby-3.0.0/lib/ruby/gems/3.0.0/gems/activesupport-7.0.8/lib/active_support/logger_thread_safe_level.rb:12:in `<module:LoggerThreadSafeLevel>': uninitialized constant ActiveSupport::LoggerThreadSafeLevel::Logger (NameError)

rvm use dev 切換到ruby3.0.0
sudo gem install activesupport 安裝新版本的activesupport

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容