使用CocoaPods開發lib庫

weixin_34185364發表於2017-10-19
1464925-49275812764a0e1d.png
cocoapods.png

一、前言

上篇文章主要介紹瞭如何在App中使用CocoaPods引入第三方庫,本篇文章將介紹怎樣使用CocoaPods進行lib庫的開發(lib庫指靜態庫或動態庫)。

二、CocoaPods lib 建立

如果還未進行開發,需要利用CocoaPods從零開始建立,那麼恭喜你,CocoaPods會為你搭建好整個開發環境。利用pod lib create MyLibrary命令,通過一些嚮導後,會自動幫你生成lib工程及demo工程。

1464925-305d16aebbd41935.png
pod-lib-create.png

生成的工程目錄如下:

1464925-d06f1e7ecf4df799.png
MyLibrary目錄結構.png

MyLibrary.xcworkspace中的結構如下:

1464925-4804a23000d48ede.png
MyLibrary.xcworkspace工程目錄.png

通過一個小小的命令,我們的工程框架就搭好了,可以在Pods工程裡新增程式碼和檔案,來實現我們的功能,然後在MyLibrary工程裡可以新增對lib庫的測試程式碼。

三、利用已有工程搭建CocoaPods lib工程

有時候,我們的lib庫已經開發很久了,也有相應的demo,只不過想升級一下逼格或xxx的原因,想通過CocoaPods來管理,而不是傳統的.xcodeproj.xcworkspace
這種情況下,我們可以直接模仿上面的工程目錄,通過編寫Podfilepodspec,就可以了。

四、podspec

  • podspec簡介

podspecpod specification的縮寫。通過上面的展示,已經可以知道podspec是對lib庫的配置檔案,Podfile會根據該檔案進行檔案的載入,所以,podspec的編寫也是非常重要的。

  • podspec建立

podspec的建立跟Podfile一樣,可以自己手動新建一個檔案,然後字尾改為.podspec。也可以命令列執行pod spec create MyLibrary

  • podspec編寫

Podfile一樣,podspec也有一套自己的語法(官網介紹)。

#一個podspec檔案包含一個Spec和若干個subspec,podfile可以引入整個podspec或subspec
Pod::Spec.new do |s|

  #Pod的名稱,必填,如Podfile中pod 'AFNetworking',AFNetworking就是name
  s.name         = "MyLibrary"      s.version      = "0.0.1"  #版本,必填

  #簡介,必填
  s.summary      = "A short description of library."  

  #詳細的描述,支援多行字串,必填
  s.description  = <<-DESC
                Add long description of the pod here.
                   DESC  

  #主頁,必填
  s.homepage     = "http://EXAMPLE/MyLibrary"  

  #遵守的開源協議,必填。注意GPL可能給公司帶來很大風險,不要輕易使用
  s.license      = "MIT"  

  #pod庫作者,必填
  s.author             = { "wangbingwf" => "wangbingwf@163.com" }  

  #平臺版本資訊,這裡表示支援iOS,7.0及以上系統
  s.platform     = :ios, "7.0"    

  #當支援多平臺時,使用deployment_target替代platform
  #s.osx.deployment_target = '8.0'  

  #程式碼路徑,這裡雖然填寫的是git倉庫的路徑,但Podfile中使用path方式引入podspec時,並不會再從git上下載程式碼,而是使用本地的程式碼,所以就可以在這種方式下開發lib庫。
  #這裡支援git,svn,http等。並且可以設定tag或version等資訊
  s.source       = { :git => "https://git.coding.net/yourgit/MyLibrary.git", :tag => s.version.to_s} 

  # ———— Subspecs ————————————————————————————————————#
  # ———— MyLibraryCFiles ———————————————————————————————#
  #建立一個subspec
   s.subspec 'MyLibraryCFiles' do |subcfiles|

    #subspec包含的程式碼檔案,上面source是路徑,這裡source_files是具體要包含哪些檔案
    #其中**表示包含子目錄,*表示當前目錄下的所有檔案
    #下面表示當前subspec包含MyLibrary/cfiles目錄及其子目錄中的所有.h和.c檔案;以及MyLibrary/log目錄下的所有.h和.c檔案
    subcfiles.source_files = ["MyLibrary/cfiles/**/*.{h,c}",
                              "MyLibrary/log/*.{h,c}"]

    #不包含的檔案
    subcfiles.exclude_files = ["MyLibrary/jni/**/*",
                            "MyLibrary/profile/unit_test/*"]

    #加入到pod庫中,被一起編譯
    #這裡通常使用私有第三方庫時,需要依賴某個lib或framework時使用。
    #新增如下選項後,會將.a新增到工程中,並且新增LIBRARY_SEARCH_PATHS路徑
    #但是需要注意的是,如果使用pod package對該pod庫進行打包,這個.a並不會打進去。
    #比如說使用pod package對MyLibrary打包成MyLibrary.a,inner.a並不會被編譯進MyLibrary.a。
    #此時,如果如果對外提供MyLibrary.a,inner.a也同樣需要提供出去
    subcfiles.vendored_libraries = "MyLibrary/lib/ios/inner.a"

    #pod工程的配置
    #對於HEADER_SEARCH_PATHS,對將設定的字串直接拷貝到xcode中,不會像上面source_files這樣使用相對路徑。
    #所以,我在這裡先獲取當前路徑,再設定進去。最後加**表示recursive,即迴圈查詢子目錄的意思
    $dir = File.dirname(__FILE__)
    $dir = $dir + "/MyLibrary/cfiles/**"  #$dir:/Users/wangbing/TempCode/MyLibrary/cfiles/**
    subcfiles.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => $dir}

    #demo工程的配置,上面是對pod工程的設定,當需要對demo工程設定時,使用user_target_xcconfig,這裡就不做介紹了

    #相對於public_headers,這些檔案不會被公開給Demo
    subcfiles.private_header_files = "MyLibrary/cfiles/**/*.h"
    #保護目錄結構不變,如果不設定,所有標頭檔案都將被放到同一個目錄下
    subcfiles.header_mappings_dir = "MyLibrary/cfiles/**"
    end

# ———— MyLibraryMain ———————————————————————————————#
  #建立一個subspec
  s.subspec 'MyLibraryMain' do |submain|

    #引入程式碼檔案
    submain.source_files = "MyLibrary/main/**/*.{h,m}"

    #設定公開標頭檔案
    submain.public_header_files = "MyLibrary/main/public.h"

    #設定資原始檔
    submain.resource  = "MyLibrary/resources/configFiles.bundle"

    #設定MyLibraryMain模組依賴的系統庫,注意,這裡加的是系統庫
    submain.frameworks = "SystemConfiguration"

    #設定依賴,這裡可以依賴當前spec中的subspec,也可以依賴github上公開的開源庫,如'AFNetworking'。
    submain.dependency "MyLibrary/MyLibraryCFiles"
    end
end
  • podspec坑

  • 坑——HEADER_SEARCH_PATHS
    在我實際專案使用過程中,C檔案的存在給我編寫spec帶來了很大問題。
    故事是這樣的,我們專案中要引用另一個團隊編寫的.a庫,暫且稱為inner.a。這個庫是C寫的,同時,標頭檔案中存在多層巢狀。
    這要求在使用該靜態庫時,要在HEADER_SEARCH_PATHS中新增標頭檔案的路徑。
    於是我一開始在podspec中是這麼寫的
subcfiles.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" =>"MyLibrary/cfiles/**"}

於是,編譯出錯了,找不到標頭檔案,因為這個設定在xcode中是這樣的:

1464925-3d00cd3ffbb9770e.png
HEADER_SEARCH_PATHS

Shit!為什麼不像上面似的,給加相對路徑呢,畢竟是路徑的設定嘛。但是也可以解決的,解決方案有三:

  1. 如果知道Demo工程和lib庫之間的路徑關係,可以通過${PODS_ROOT}/../../MyLibrary/cfiles/**來解決。
  2. 通過設定絕對路徑來解決:
    podspec中使用ruby指令碼,獲取當前路徑,而podspeclib庫的位置一般不會變,所以最終使用了這種方式來設定HEADER_SEARCH_PATHS
$dir = File.dirname(__FILE__)
$dir = $dir + "/MyLibrary/cfiles/**"  #$dir:/Users/wangbing/TempCode/MyLibrary/cfiles/**
subcfiles.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => $dir}
  1. 利用CocoaPods總會新增的兩個預設路徑,設定header_mappings_dir保護目錄結構不發生變化。
#設定cfiles及子目錄結構保持不變
subcfiles.header_mappings_dir = "MyLibrary/cfiles/**"

#將這些檔案設定為private_file或public_file
subcfiles.private_header_files = "MyLibrary/cfiles/**/*.h"

#因為我的C標頭檔案有巢狀,需要查詢子目錄,所以需要將non-recursive改為recursive
subcfiles.pod_target_xcconfig = { "HEADER_SEARCH_PATHS" => "${PODS_ROOT}/Headers/Private/**"}

通過方法3同樣使用了${PODS_ROOT},與方法1有什麼區別呢?區別就是,如果按照方法3,可以忽略Demo工程與lib庫的路徑關係。因為在執行pod install後,系統會在Pods工程目錄下生成PrivatePublic目錄,就不用考慮原來cfiles的路徑了,如下圖。

1464925-c9e511060f041359.png
cfiles目錄

五、CocoaPods打包靜態庫

如果你開發的SDK並不想開源,那麼在通過CocoaPods管理並開發之後,還可以通過CocoaPods進行打包。生成framework.a。打包命令是pod package

1464925-8458dbe9e5b7e5f9.png
pod package

像上面的例子中,我的打包命令為:pod package MyLibrary.podspec --force --no-mangle --embedded --subspecs= MyLibraryMain。然後將生成的framework和inner.a共同交給使用者使用即可。

這裡需要注意

  1. 打包命令會根據podspec檔案下載程式碼到一個臨時檔案中進行打包,並不是使用的本地檔案,所以記得要提交併打tag。
  2. 上面的HEADER_SEARCH_PATHS問題,使用方法1和方法2同樣會導致pod package失敗。因為你不知道cfiles的路徑,無法新增到HEADER_SEARCH_PATHS。但卻可以使用方法3!
  3. podspec檔案中使用vendor標識的frameworklibraries,並不會真正編譯到最終的framework中。這真的很讓我受傷,我查了好久。。。好久。。。好久。。。也正因為這個問題,我決定放棄pod package方式。準備嘗試自己寫個指令碼進行打包,還在準備中。。。

六、總結

研究CocoaPods的初衷是為了元件化開發,目前算是有了一點初步的認識。
我們把不同的功能模組放在不同的資料夾中,通過podspec進行配置,通過podfile進行引用,通過pod package進行打包。一切是很方便的。
不過在我的實際應用中,目前在打包和public_headers方面還有一些問題,I'm working on it!

最後,大家有疑惑的可以留言,寫的有問題的歡迎糾正!

相關文章